From d6b53b14173504ffcd3aacff665a2d78b28ec5bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 09:21:44 -0500 Subject: [PATCH 01/42] build(deps): Bump github.com/hashicorp/terraform-plugin-go (#1078) Bumps [github.com/hashicorp/terraform-plugin-go](https://github.com/hashicorp/terraform-plugin-go) from 0.25.0 to 0.26.0. - [Release notes](https://github.com/hashicorp/terraform-plugin-go/releases) - [Changelog](https://github.com/hashicorp/terraform-plugin-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/terraform-plugin-go/compare/v0.25.0...v0.26.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/terraform-plugin-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 48 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 193d48e39..207c0347e 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.25.0 + github.com/hashicorp/terraform-plugin-go v0.26.0 github.com/hashicorp/terraform-plugin-log v0.9.0 ) @@ -16,7 +16,7 @@ require ( github.com/hashicorp/go-hclog v1.5.0 // 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-registry-address v0.2.4 // 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 @@ -25,10 +25,10 @@ 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.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/grpc v1.67.1 // indirect - google.golang.org/protobuf v1.35.1 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/grpc v1.69.4 // indirect + google.golang.org/protobuf v1.36.3 // indirect ) diff --git a/go.sum b/go.sum index 99e7874be..5a586522d 100644 --- a/go.sum +++ b/go.sum @@ -5,22 +5,28 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.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.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-go v0.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M= +github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY= 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= -github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= +github.com/hashicorp/terraform-registry-address v0.2.4 h1:JXu/zHB2Ymg/TGVCRu10XqNa4Sh2bWcqCNyKWjnCPJA= +github.com/hashicorp/terraform-registry-address v0.2.4/go.mod h1:tUNYTVyCtU4OIGXXMDp7WNcJ+0W1B4nmstVDgHMjfAU= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= @@ -48,24 +54,34 @@ 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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 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.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= -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.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -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= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/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 374065521f4ba577f8dbf002b65b697dd8a40356 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 09:25:18 -0500 Subject: [PATCH 02/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1076) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 0721580cf..6afcd13e7 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -88,7 +88,7 @@ jobs: - run: go mod download - run: go test -coverprofile=coverage.out ./... - run: go tool cover -html=coverage.out -o coverage.html - - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: go-${{ matrix.go-version }}-coverage path: coverage.html From 4e0d57bbcf9434ec0221682cb26bd10855bf2aac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 09:35:52 -0500 Subject: [PATCH 03/42] build(deps): Bump golang.org/x/net from 0.23.0 to 0.33.0 in /tools (#1080) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.23.0 to 0.33.0. - [Commits](https://github.com/golang/net/compare/v0.23.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 3c9751431..ceb8a3f68 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -55,7 +55,7 @@ require ( go.mongodb.org/mongo-driver v1.10.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index aae7cc64e..0cd7527e4 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -426,8 +426,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From d259efd18959b940783db189eb5662768555604d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 09:49:39 -0500 Subject: [PATCH 04/42] build(deps): Bump github.com/hashicorp/copywrite in /tools (#1081) Bumps [github.com/hashicorp/copywrite](https://github.com/hashicorp/copywrite) from 0.19.0 to 0.20.0. - [Release notes](https://github.com/hashicorp/copywrite/releases) - [Changelog](https://github.com/hashicorp/copywrite/blob/main/.goreleaser.yaml) - [Commits](https://github.com/hashicorp/copywrite/compare/v0.19.0...v0.20.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/copywrite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 17 ++++++----------- tools/go.sum | 44 ++++++++++++-------------------------------- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index ceb8a3f68..c7560ffca 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -2,17 +2,16 @@ module tools go 1.22.7 -require github.com/hashicorp/copywrite v0.19.0 +require github.com/hashicorp/copywrite v0.20.0 require ( - github.com/AlecAivazis/survey/v2 v2.3.6 // indirect + github.com/AlecAivazis/survey/v2 v2.3.7 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 // indirect - github.com/cli/go-gh v1.2.1 // indirect + github.com/cli/go-gh/v2 v2.11.2 // indirect github.com/cli/safeexec v1.0.0 // indirect - github.com/cli/shurcooL-graphql v0.0.2 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -26,32 +25,28 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/henvic/httpretty v0.0.6 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jedib0t/go-pretty v4.3.0+incompatible // indirect github.com/jedib0t/go-pretty/v6 v6.4.6 // indirect github.com/joho/godotenv v1.3.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/knadh/koanf v1.5.0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mergestat/timediff v0.0.3 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/muesli/termenv v0.12.0 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/samber/lo v1.37.0 // indirect github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/thanhpk/randstr v1.0.4 // indirect - github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect go.mongodb.org/mongo-driver v1.10.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect diff --git a/tools/go.sum b/tools/go.sum index 0cd7527e4..e9c0dc344 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw= -github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= @@ -44,12 +44,10 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cli/go-gh v1.2.1 h1:xFrjejSsgPiwXFP6VYynKWwxLQcNJy3Twbu82ZDlR/o= -github.com/cli/go-gh v1.2.1/go.mod h1:Jxk8X+TCO4Ui/GarwY9tByWm/8zp4jJktzVZNlTW5VM= +github.com/cli/go-gh/v2 v2.11.2 h1:oad1+sESTPNTiTvh3I3t8UmxuovNDxhwLzeMHk45Q9w= +github.com/cli/go-gh/v2 v2.11.2/go.mod h1:vVFhi3TfjseIW26ED9itAR8gQK0aVThTm8sYrsZ5QTI= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/cli/shurcooL-graphql v0.0.2 h1:rwP5/qQQ2fM0TzkUTwtt6E2LbIYf6R+39cUXTa04NYk= -github.com/cli/shurcooL-graphql v0.0.2/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= @@ -146,12 +144,10 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/copywrite v0.19.0 h1:f9LVxTDBfFYeQmdBpOsZ+HWknXonI8ZwubbO/RwyuCo= -github.com/hashicorp/copywrite v0.19.0/go.mod h1:6wvQH+ICDoD2bpjO1RJ6fi+h3aY5NeLEM12oTkEtFoc= +github.com/hashicorp/copywrite v0.20.0 h1:i+iNq4lWsGopKIhC0HfZjUvNAnXnU/Pc5e+4L5WF+1Y= +github.com/hashicorp/copywrite v0.20.0/go.mod h1:mu6DAyUI6m6vq8weoJn9a0HDuUUrV+0GQdRp4mD50yU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -186,8 +182,6 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= -github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= @@ -226,8 +220,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -243,10 +235,11 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mergestat/timediff v0.0.3 h1:ucCNh4/ZrTPjFZ081PccNbhx9spymCJkFxSzgVuPU+Y= github.com/mergestat/timediff v0.0.3/go.mod h1:yvMUaRu2oetc+9IbPLYBJviz6sA7xz8OXMDfhBl7YSI= @@ -278,10 +271,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= -github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -319,8 +308,9 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= @@ -357,8 +347,6 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo= github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= -github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= -github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -421,7 +409,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= @@ -479,16 +466,12 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -499,7 +482,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -578,8 +560,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= -gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 0a8b103e8b0b9125b6295e22743af41d92b72e77 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 10:20:16 -0500 Subject: [PATCH 05/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1082) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 6afcd13e7..d977b6434 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - run: go mod download - - uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 + - uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0 terraform-provider-corner-tfprotov5: defaults: run: From 55c54aeba6f8fb6a34e5ca58db8416d7d20696f8 Mon Sep 17 00:00:00 2001 From: Judith Malnick Date: Fri, 31 Jan 2025 09:06:31 -0800 Subject: [PATCH 06/42] Remove web team from CODEOWNERS for content directories (#1083) * add education web presence ability to approve PRs to relavent website files * Remove web team from CODEOWNERS for content directories --- .github/CODEOWNERS | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5d5ed18c4..0a1e51965 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,14 @@ * @hashicorp/terraform-devex -# web presence and education +# engineering and web presence get notified of, and can approve changes to web tooling, but not content. +/website/ @hashicorp/web-presence @hashicorp/terraform-devex +/website/data/ +/website/public/ +/website/content/ -/website/ @hashicorp/team-docs-packer-and-terraform @hashicorp/web-presence @hashicorp/terraform-devex +# education and engineering get notified of, and can approve changes to web content. + +/website/data/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex +/website/public/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex +/website/content/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex From 8fe2632042ac52af1b80a7170703ef611dcfe734 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:18:00 -0500 Subject: [PATCH 07/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1084) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-github-actions.yml | 2 +- .github/workflows/ci-go.yml | 8 ++++---- .github/workflows/ci-goreleaser.yml | 2 +- .github/workflows/release.yml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-github-actions.yml b/.github/workflows/ci-github-actions.yml index 53578c4a2..8451dde35 100644 --- a/.github/workflows/ci-github-actions.yml +++ b/.github/workflows/ci-github-actions.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - run: go install github.com/rhysd/actionlint/cmd/actionlint@latest diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index d977b6434..82cd84f02 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - run: go mod download @@ -34,7 +34,7 @@ jobs: with: path: terraform-provider-corner repository: hashicorp/terraform-provider-corner - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 @@ -60,7 +60,7 @@ jobs: with: path: terraform-provider-corner repository: hashicorp/terraform-provider-corner - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 @@ -82,7 +82,7 @@ jobs: go-version: [ '1.23', '1.22' ] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version: ${{ matrix.go-version }} - run: go mod download diff --git a/.github/workflows/ci-goreleaser.yml b/.github/workflows/ci-goreleaser.yml index e4181d55f..989b03503 100644 --- a/.github/workflows/ci-goreleaser.yml +++ b/.github/workflows/ci-goreleaser.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da0415909..6e72b8fc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: ref: ${{ inputs.versionNumber }} fetch-depth: 0 - - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' From 3c0bf49a3a3492cd530f39a03efa4c954d03a8a7 Mon Sep 17 00:00:00 2001 From: Rose M Koron <32436232+rkoron007@users.noreply.github.com> Date: Fri, 7 Feb 2025 05:31:42 -0800 Subject: [PATCH 08/42] adjust permissions for docs files (#1085) --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a1e51965..36958c1cc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,3 +12,6 @@ /website/data/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex /website/public/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex /website/content/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex +/website/docs/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex +/website/img/ @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex +/website/README.md @hashicorp/team-docs-packer-and-terraform @hashicorp/terraform-devex From e1e686639f4c97187876aaf8b77f156094d8ad19 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 10 Feb 2025 10:43:00 -0500 Subject: [PATCH 09/42] Add support for write only attributes (#1044) * initial ephemeral resource interfaces * add ephemeral resource configure data * attribute implementations * uncomment custom type tests * added block implementations * add nested attribute implementations * add schema test * remove todo * doc updates, renames, removals * initial protov5 + fwserver implementation (protov6 stubbed) * add fromproto5 tests * add toproto5 tests * add proto5server tests * implement protov6 * schema + metadata tests * add close proto5/6 tests * add fwserver tests for schema/metadata * prevent random false positives * validate fwserver tests * open/renew/close fwserver tests * update error message * update plugin go * Update `terraform-plugin-go` dependency * remove `config` from renew * Implement write only attributes in the `resource/schema` package * Implement write only attributes in the `datasource/schema` package * Implement write only attributes in the `provider/schema` and `provider/metaschema` packages * Implement write only attributes in the `internal/testing/testschema` package * Update `terraform-plugin-go` dependency * Implement write only attributes in the `ephemeral/schema` package * Populate writeOnly fields in `internal/toproto5` and `internal/toproto6` * Implement `ValidateResourceConfigClientCapabilities` in the `ValidateResourceConfig` RPC * Add attribute validation for write only attributes * Initial `RequiredWriteOnlyNilsAttributePaths()` implementation * Complete `RequiredWriteOnlyNilsAttributePaths()` implementation * Implement `validator.ValidateSchemaClientCapabilities` * Implement automatic write-only value nullification during `ApplyResourceState` RPC * Explicitly set `ValidateSchemaClientCapabilities` during `ValidateDataSourceConfig`, `ValidateEphemeralResourceConfig`, and `ValidateProviderResourceConfig` RPCs * Nullify write-only attributes during Plan and Apply regardless of client capability * remove apply client capability * add validation for older terraform client versions * add client capabilities to nested attribute validation * Update wording of `IsWriteOnly` comment for `ephemeral/schema`, `provider/schema`, and `provider/metaschema` * Update various comments for wording * Update test cases * Update wording for `write-only` attribute validation errors * Refactor write_only_nested_attribute_validation.go and write_only_nested_attribute_validation_test.go * Move `Required` + `WriteOnly` validations from `PlanResourceChange` RPC to `ValidateResourceConfig` RPC * Add write-only value nullification to `ReadResource`, `ImportResourceState`, `UpgradeResourceState`, and `MoveResourceState` RPCs * Add missing `IsWriteOnly()` unit tests for `ephemeral/schema` package * Add godoc comment to `NullifyWriteOnlyAttributes()` * Add testing for nested types for `NullifyWriteOnlyAttributes()` * Add website documentation * Add recommendation to use private state to store hashes * Add changelog entries --------- Co-authored-by: Austin Valle --- .../unreleased/FEATURES-20250206-114700.yaml | 6 + .../unreleased/NOTES-20250206-114436.yaml | 5 + datasource/schema/bool_attribute.go | 8 +- datasource/schema/bool_attribute_test.go | 31 +- datasource/schema/dynamic_attribute.go | 8 +- datasource/schema/dynamic_attribute_test.go | 31 +- datasource/schema/float32_attribute.go | 5 + datasource/schema/float32_attribute_test.go | 28 + datasource/schema/float64_attribute.go | 8 +- datasource/schema/float64_attribute_test.go | 31 +- datasource/schema/int32_attribute.go | 5 + datasource/schema/int32_attribute_test.go | 28 + datasource/schema/int64_attribute.go | 8 +- datasource/schema/int64_attribute_test.go | 31 +- datasource/schema/list_attribute.go | 8 +- datasource/schema/list_attribute_test.go | 31 +- datasource/schema/list_nested_attribute.go | 8 +- .../schema/list_nested_attribute_test.go | 31 +- datasource/schema/map_attribute.go | 8 +- datasource/schema/map_attribute_test.go | 31 +- datasource/schema/map_nested_attribute.go | 5 + .../schema/map_nested_attribute_test.go | 31 +- datasource/schema/number_attribute.go | 8 +- datasource/schema/number_attribute_test.go | 31 +- datasource/schema/object_attribute.go | 8 +- datasource/schema/object_attribute_test.go | 31 +- datasource/schema/set_attribute.go | 8 +- datasource/schema/set_attribute_test.go | 31 +- datasource/schema/set_nested_attribute.go | 5 + .../schema/set_nested_attribute_test.go | 31 +- datasource/schema/single_nested_attribute.go | 5 + .../schema/single_nested_attribute_test.go | 31 +- datasource/schema/string_attribute.go | 8 +- datasource/schema/string_attribute_test.go | 31 +- ephemeral/schema/bool_attribute.go | 9 +- ephemeral/schema/bool_attribute_test.go | 29 + ephemeral/schema/dynamic_attribute.go | 9 +- ephemeral/schema/dynamic_attribute_test.go | 28 + ephemeral/schema/float32_attribute.go | 6 + ephemeral/schema/float32_attribute_test.go | 28 + ephemeral/schema/float64_attribute.go | 9 +- ephemeral/schema/float64_attribute_test.go | 31 +- ephemeral/schema/int32_attribute.go | 6 + ephemeral/schema/int32_attribute_test.go | 28 + ephemeral/schema/int64_attribute.go | 9 +- ephemeral/schema/int64_attribute_test.go | 31 +- ephemeral/schema/list_attribute.go | 10 +- ephemeral/schema/list_attribute_test.go | 28 + ephemeral/schema/list_nested_attribute.go | 9 +- .../schema/list_nested_attribute_test.go | 28 + ephemeral/schema/map_attribute.go | 9 +- ephemeral/schema/map_attribute_test.go | 28 + ephemeral/schema/map_nested_attribute.go | 6 + ephemeral/schema/map_nested_attribute_test.go | 28 + ephemeral/schema/number_attribute.go | 9 +- ephemeral/schema/number_attribute_test.go | 31 +- ephemeral/schema/object_attribute.go | 10 +- ephemeral/schema/object_attribute_test.go | 31 +- ephemeral/schema/set_attribute.go | 9 +- ephemeral/schema/set_attribute_test.go | 28 + ephemeral/schema/set_nested_attribute.go | 6 + ephemeral/schema/set_nested_attribute_test.go | 31 +- ephemeral/schema/single_nested_attribute.go | 6 + .../schema/single_nested_attribute_test.go | 28 + ephemeral/schema/string_attribute.go | 9 +- ephemeral/schema/string_attribute_test.go | 28 + internal/fromproto5/client_capabilities.go | 13 + .../fromproto5/validateresourcetypeconfig.go | 4 +- .../validateresourcetypeconfig_test.go | 38 +- internal/fromproto6/client_capabilities.go | 13 + internal/fromproto6/validateresourceconfig.go | 4 +- .../fromproto6/validateresourceconfig_test.go | 38 +- internal/fwschema/attribute.go | 14 +- .../write_only_nested_attribute_validation.go | 73 + .../data_nullify_collection_blocks.go | 3 +- internal/fwserver/attribute_validation.go | 158 +- .../fwserver/attribute_validation_test.go | 613 ++++++ internal/fwserver/block_validation.go | 41 +- internal/fwserver/block_validation_test.go | 335 +++- internal/fwserver/schema_validation.go | 8 + .../server_applyresourcechange_test.go | 125 ++ internal/fwserver/server_createresource.go | 18 +- .../fwserver/server_createresource_test.go | 57 + .../fwserver/server_importresourcestate.go | 12 + .../server_importresourcestate_test.go | 67 + internal/fwserver/server_moveresourcestate.go | 12 + .../fwserver/server_moveresourcestate_test.go | 57 +- .../fwserver/server_planresourcechange.go | 74 + .../server_planresourcechange_test.go | 300 +++ internal/fwserver/server_readresource.go | 20 +- internal/fwserver/server_readresource_test.go | 61 + internal/fwserver/server_updateresource.go | 18 +- .../fwserver/server_updateresource_test.go | 57 + .../fwserver/server_upgraderesourcestate.go | 34 +- .../server_upgraderesourcestate_test.go | 148 ++ .../server_validatedatasourceconfig.go | 12 +- .../server_validateephemeralresourceconfig.go | 12 +- .../fwserver/server_validateproviderconfig.go | 12 +- .../fwserver/server_validateresourceconfig.go | 16 +- .../server_validateresourceconfig_test.go | 94 +- internal/fwserver/write_only_nullification.go | 77 + .../fwserver/write_only_nullification_test.go | 1710 +++++++++++++++++ .../server_validateresourceconfig_test.go | 5 +- internal/testing/testschema/attribute.go | 9 +- .../testschema/attributewithbooldefault.go | 6 + .../attributewithboolplanmodifiers.go | 9 +- .../testschema/attributewithboolvalidators.go | 9 +- .../testschema/attributewithdynamicdefault.go | 6 + .../attributewithdynamicplanmodifiers.go | 6 + .../attributewithdynamicvalidators.go | 9 +- .../testschema/attributewithfloat32default.go | 6 + .../attributewithfloat32planmodifiers.go | 6 + .../attributewithfloat32validators.go | 6 + .../testschema/attributewithfloat64default.go | 6 + .../attributewithfloat64planmodifiers.go | 6 + .../attributewithfloat64validators.go | 6 + .../testschema/attributewithint32default.go | 6 + .../attributewithint32planmodifiers.go | 6 + .../attributewithint32validators.go | 6 + .../testschema/attributewithint64default.go | 6 + .../attributewithint64planmodifiers.go | 6 + .../attributewithint64validators.go | 6 + .../testschema/attributewithlistdefault.go | 6 + .../attributewithlistplanmodifiers.go | 6 + .../testschema/attributewithlistvalidators.go | 6 + .../testschema/attributewithmapdefault.go | 6 + .../attributewithmapplanmodifiers.go | 6 + .../testschema/attributewithmapvalidators.go | 6 + .../testschema/attributewithnumberdefault.go | 6 + .../attributewithnumberplanmodifiers.go | 6 + .../attributewithnumbervalidators.go | 6 + .../testschema/attributewithobjectdefault.go | 6 + .../attributewithobjectplanmodifiers.go | 6 + .../attributewithobjectvalidators.go | 6 + .../testschema/attributewithsetdefault.go | 6 + .../attributewithsetplanmodifiers.go | 9 +- .../testschema/attributewithsetvalidators.go | 6 + .../testschema/attributewithstringdefault.go | 6 + .../attributewithstringplanmodifiers.go | 6 + .../attributewithstringvalidators.go | 6 + .../testing/testschema/nested_attribute.go | 6 + .../nested_attribute_with_list_default.go | 6 + ...sted_attribute_with_list_plan_modifiers.go | 6 + .../nested_attribute_with_map_default.go | 6 + ...ested_attribute_with_map_plan_modifiers.go | 6 + .../nested_attribute_with_object_default.go | 6 + ...ed_attribute_with_object_plan_modifiers.go | 6 + .../nested_attribute_with_set_default.go | 6 + ...ested_attribute_with_set_plan_modifiers.go | 6 + internal/toproto5/getproviderschema_test.go | 126 ++ internal/toproto5/schema_attribute.go | 4 +- internal/toproto6/getproviderschema_test.go | 94 + internal/toproto6/schema_attribute.go | 4 +- provider/metaschema/bool_attribute.go | 9 +- provider/metaschema/bool_attribute_test.go | 31 +- provider/metaschema/float64_attribute.go | 9 +- provider/metaschema/float64_attribute_test.go | 31 +- provider/metaschema/int64_attribute.go | 9 +- provider/metaschema/int64_attribute_test.go | 31 +- provider/metaschema/list_attribute.go | 9 +- provider/metaschema/list_attribute_test.go | 31 +- provider/metaschema/list_nested_attribute.go | 9 +- .../metaschema/list_nested_attribute_test.go | 31 +- provider/metaschema/map_attribute.go | 9 +- provider/metaschema/map_attribute_test.go | 31 +- provider/metaschema/map_nested_attribute.go | 9 +- .../metaschema/map_nested_attribute_test.go | 31 +- provider/metaschema/number_attribute.go | 9 +- provider/metaschema/number_attribute_test.go | 31 +- provider/metaschema/object_attribute.go | 9 +- provider/metaschema/object_attribute_test.go | 31 +- provider/metaschema/set_attribute.go | 9 +- provider/metaschema/set_attribute_test.go | 31 +- provider/metaschema/set_nested_attribute.go | 9 +- .../metaschema/set_nested_attribute_test.go | 31 +- .../metaschema/single_nested_attribute.go | 9 +- .../single_nested_attribute_test.go | 31 +- provider/metaschema/string_attribute.go | 9 +- provider/metaschema/string_attribute_test.go | 31 +- provider/schema/bool_attribute.go | 9 +- provider/schema/bool_attribute_test.go | 31 +- provider/schema/dynamic_attribute.go | 9 +- provider/schema/dynamic_attribute_test.go | 31 +- provider/schema/float32_attribute.go | 9 +- provider/schema/float32_attribute_test.go | 31 +- provider/schema/float64_attribute.go | 9 +- provider/schema/float64_attribute_test.go | 31 +- provider/schema/int32_attribute.go | 6 + provider/schema/int32_attribute_test.go | 28 + provider/schema/int64_attribute.go | 9 +- provider/schema/int64_attribute_test.go | 31 +- provider/schema/list_attribute.go | 9 +- provider/schema/list_attribute_test.go | 31 +- provider/schema/list_nested_attribute.go | 9 +- provider/schema/list_nested_attribute_test.go | 31 +- provider/schema/map_attribute.go | 9 +- provider/schema/map_attribute_test.go | 31 +- provider/schema/map_nested_attribute.go | 6 + provider/schema/map_nested_attribute_test.go | 31 +- provider/schema/number_attribute.go | 9 +- provider/schema/number_attribute_test.go | 31 +- provider/schema/object_attribute.go | 9 +- provider/schema/object_attribute_test.go | 31 +- provider/schema/set_attribute.go | 9 +- provider/schema/set_attribute_test.go | 31 +- provider/schema/set_nested_attribute.go | 6 + provider/schema/set_nested_attribute_test.go | 31 +- provider/schema/single_nested_attribute.go | 6 + .../schema/single_nested_attribute_test.go | 31 +- provider/schema/string_attribute.go | 9 +- provider/schema/string_attribute_test.go | 31 +- resource/schema/bool_attribute.go | 15 + resource/schema/bool_attribute_test.go | 34 + resource/schema/dynamic_attribute.go | 15 + resource/schema/dynamic_attribute_test.go | 34 + resource/schema/float32_attribute.go | 15 + resource/schema/float32_attribute_test.go | 34 + resource/schema/float64_attribute.go | 15 + resource/schema/float64_attribute_test.go | 34 + resource/schema/int32_attribute.go | 15 + resource/schema/int32_attribute_test.go | 34 + resource/schema/int64_attribute.go | 15 + resource/schema/int64_attribute_test.go | 34 + resource/schema/list_attribute.go | 15 + resource/schema/list_attribute_test.go | 34 + resource/schema/list_nested_attribute.go | 26 + resource/schema/list_nested_attribute_test.go | 122 ++ resource/schema/map_attribute.go | 15 + resource/schema/map_attribute_test.go | 34 + resource/schema/map_nested_attribute.go | 26 + resource/schema/map_nested_attribute_test.go | 122 ++ resource/schema/number_attribute.go | 15 + resource/schema/number_attribute_test.go | 34 + resource/schema/object_attribute.go | 15 + resource/schema/object_attribute_test.go | 34 + resource/schema/set_attribute.go | 15 + resource/schema/set_attribute_test.go | 34 + resource/schema/set_nested_attribute.go | 26 + resource/schema/set_nested_attribute_test.go | 122 ++ resource/schema/single_nested_attribute.go | 23 + .../schema/single_nested_attribute_test.go | 114 ++ resource/schema/string_attribute.go | 15 + resource/schema/string_attribute_test.go | 34 + ...e_only_nested_attribute_validation_test.go | 1281 ++++++++++++ resource/validate_config.go | 16 + schema/validator/bool.go | 5 + schema/validator/client_capabilities.go | 17 + schema/validator/dynamic.go | 5 + schema/validator/float32.go | 5 + schema/validator/float64.go | 5 + schema/validator/int32.go | 5 + schema/validator/int64.go | 5 + schema/validator/list.go | 5 + schema/validator/map.go | 5 + schema/validator/number.go | 5 + schema/validator/object.go | 5 + schema/validator/set.go | 5 + schema/validator/string.go | 5 + website/data/plugin-framework-nav-data.json | 4 + .../handling-data/attributes/bool.mdx | 12 + .../handling-data/attributes/dynamic.mdx | 12 + .../handling-data/attributes/float32.mdx | 12 + .../handling-data/attributes/float64.mdx | 12 + .../handling-data/attributes/int32.mdx | 12 + .../handling-data/attributes/int64.mdx | 12 + .../handling-data/attributes/list-nested.mdx | 14 + .../handling-data/attributes/list.mdx | 12 + .../handling-data/attributes/map-nested.mdx | 14 + .../handling-data/attributes/map.mdx | 12 + .../handling-data/attributes/number.mdx | 12 + .../handling-data/attributes/object.mdx | 12 + .../handling-data/attributes/set-nested.mdx | 14 + .../handling-data/attributes/set.mdx | 12 + .../attributes/single-nested.mdx | 14 + .../handling-data/attributes/string.mdx | 12 + .../docs/plugin/framework/resources/index.mdx | 1 + .../resources/write-only-arguments.mdx | 112 ++ 277 files changed, 9882 insertions(+), 204 deletions(-) create mode 100644 .changes/unreleased/FEATURES-20250206-114700.yaml create mode 100644 .changes/unreleased/NOTES-20250206-114436.yaml create mode 100644 internal/fwschema/write_only_nested_attribute_validation.go create mode 100644 internal/fwserver/write_only_nullification.go create mode 100644 internal/fwserver/write_only_nullification_test.go create mode 100644 resource/schema/write_only_nested_attribute_validation_test.go create mode 100644 schema/validator/client_capabilities.go create mode 100644 website/docs/plugin/framework/resources/write-only-arguments.mdx diff --git a/.changes/unreleased/FEATURES-20250206-114700.yaml b/.changes/unreleased/FEATURES-20250206-114700.yaml new file mode 100644 index 000000000..ce306a4c9 --- /dev/null +++ b/.changes/unreleased/FEATURES-20250206-114700.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'resource/schema: Added `WriteOnly` schema field for managed resource schemas to indicate a write-only attribute. +Write-only attribute values are not saved to the Terraform plan or state artifacts.' +time: 2025-02-06T11:47:00.176842-05:00 +custom: + Issue: "1044" diff --git a/.changes/unreleased/NOTES-20250206-114436.yaml b/.changes/unreleased/NOTES-20250206-114436.yaml new file mode 100644 index 000000000..0ce8a7603 --- /dev/null +++ b/.changes/unreleased/NOTES-20250206-114436.yaml @@ -0,0 +1,5 @@ +kind: NOTES +body: Write-only attribute support is in technical preview and offered without compatibility promises until Terraform 1.11 is generally available. +time: 2025-02-06T11:44:36.156747-05:00 +custom: + Issue: "1044" diff --git a/datasource/schema/bool_attribute.go b/datasource/schema/bool_attribute.go index b9f6e3820..1d984a8db 100644 --- a/datasource/schema/bool_attribute.go +++ b/datasource/schema/bool_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -181,6 +182,11 @@ func (a BoolAttribute) IsRequired() bool { return a.Required } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a BoolAttribute) IsWriteOnly() bool { + return false +} + // IsSensitive returns the Sensitive field value. func (a BoolAttribute) IsSensitive() bool { return a.Sensitive diff --git a/datasource/schema/bool_attribute_test.go b/datasource/schema/bool_attribute_test.go index 40e36a08d..b493d892d 100644 --- a/datasource/schema/bool_attribute_test.go +++ b/datasource/schema/bool_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -423,3 +424,31 @@ func TestBoolAttributeIsSensitive(t *testing.T) { }) } } + +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/dynamic_attribute.go b/datasource/schema/dynamic_attribute.go index 6b1b6c83e..4659cadf6 100644 --- a/datasource/schema/dynamic_attribute.go +++ b/datasource/schema/dynamic_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -182,6 +183,11 @@ func (a DynamicAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a DynamicAttribute) IsWriteOnly() bool { + return false +} + // DynamicValidators returns the Validators field value. func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { return a.Validators diff --git a/datasource/schema/dynamic_attribute_test.go b/datasource/schema/dynamic_attribute_test.go index 8981dbecd..95fe01fb4 100644 --- a/datasource/schema/dynamic_attribute_test.go +++ b/datasource/schema/dynamic_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -390,6 +391,34 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } } +func TestDynamicAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestDynamicAttributeDynamicValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/float32_attribute.go b/datasource/schema/float32_attribute.go index 8f3dbdc21..d2510f5c3 100644 --- a/datasource/schema/float32_attribute.go +++ b/datasource/schema/float32_attribute.go @@ -189,3 +189,8 @@ func (a Float32Attribute) IsRequired() bool { func (a Float32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a Float32Attribute) IsWriteOnly() bool { + return false +} diff --git a/datasource/schema/float32_attribute_test.go b/datasource/schema/float32_attribute_test.go index 0da3cc94e..1040f6245 100644 --- a/datasource/schema/float32_attribute_test.go +++ b/datasource/schema/float32_attribute_test.go @@ -424,3 +424,31 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/float64_attribute.go b/datasource/schema/float64_attribute.go index 1313353ec..1d893dd33 100644 --- a/datasource/schema/float64_attribute.go +++ b/datasource/schema/float64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -188,3 +189,8 @@ func (a Float64Attribute) IsRequired() bool { func (a Float64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a Float64Attribute) IsWriteOnly() bool { + return false +} diff --git a/datasource/schema/float64_attribute_test.go b/datasource/schema/float64_attribute_test.go index f2e05ebf7..b83e16abd 100644 --- a/datasource/schema/float64_attribute_test.go +++ b/datasource/schema/float64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -423,3 +424,31 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/int32_attribute.go b/datasource/schema/int32_attribute.go index 89f852e8c..d06a9b8ce 100644 --- a/datasource/schema/int32_attribute.go +++ b/datasource/schema/int32_attribute.go @@ -189,3 +189,8 @@ func (a Int32Attribute) IsRequired() bool { func (a Int32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a Int32Attribute) IsWriteOnly() bool { + return false +} diff --git a/datasource/schema/int32_attribute_test.go b/datasource/schema/int32_attribute_test.go index 3df85e9c7..23ce97fd5 100644 --- a/datasource/schema/int32_attribute_test.go +++ b/datasource/schema/int32_attribute_test.go @@ -424,3 +424,31 @@ func TestInt32AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/int64_attribute.go b/datasource/schema/int64_attribute.go index ab9d5ca1b..85bd4a445 100644 --- a/datasource/schema/int64_attribute.go +++ b/datasource/schema/int64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -188,3 +189,8 @@ func (a Int64Attribute) IsRequired() bool { func (a Int64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a Int64Attribute) IsWriteOnly() bool { + return false +} diff --git a/datasource/schema/int64_attribute_test.go b/datasource/schema/int64_attribute_test.go index c1ac3c7a4..b15eeb85f 100644 --- a/datasource/schema/int64_attribute_test.go +++ b/datasource/schema/int64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -423,3 +424,31 @@ func TestInt64AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/list_attribute.go b/datasource/schema/list_attribute.go index 9d502067f..2e5140602 100644 --- a/datasource/schema/list_attribute.go +++ b/datasource/schema/list_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -202,6 +203,11 @@ func (a ListAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a ListAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListAttribute) ListValidators() []validator.List { return a.Validators diff --git a/datasource/schema/list_attribute_test.go b/datasource/schema/list_attribute_test.go index 27294a130..1e7335ce3 100644 --- a/datasource/schema/list_attribute_test.go +++ b/datasource/schema/list_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -398,6 +399,34 @@ func TestListAttributeIsSensitive(t *testing.T) { } } +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/list_nested_attribute.go b/datasource/schema/list_nested_attribute.go index b9b70d6fb..922320627 100644 --- a/datasource/schema/list_nested_attribute.go +++ b/datasource/schema/list_nested_attribute.go @@ -7,6 +7,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -14,7 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -230,6 +231,11 @@ func (a ListNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a ListNestedAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/datasource/schema/list_nested_attribute_test.go b/datasource/schema/list_nested_attribute_test.go index 5d1e7db88..fd1cf6941 100644 --- a/datasource/schema/list_nested_attribute_test.go +++ b/datasource/schema/list_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -569,6 +570,34 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } } +func TestListNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListNestedAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/map_attribute.go b/datasource/schema/map_attribute.go index 516576a4e..3d6c57680 100644 --- a/datasource/schema/map_attribute.go +++ b/datasource/schema/map_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -205,6 +206,11 @@ func (a MapAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a MapAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/datasource/schema/map_attribute_test.go b/datasource/schema/map_attribute_test.go index d79425462..ab3d31167 100644 --- a/datasource/schema/map_attribute_test.go +++ b/datasource/schema/map_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -398,6 +399,34 @@ func TestMapAttributeIsSensitive(t *testing.T) { } } +func TestMapAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapAttributeMapValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/map_nested_attribute.go b/datasource/schema/map_nested_attribute.go index 9729efdc3..9bf1bb957 100644 --- a/datasource/schema/map_nested_attribute.go +++ b/datasource/schema/map_nested_attribute.go @@ -231,6 +231,11 @@ func (a MapNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a MapNestedAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/datasource/schema/map_nested_attribute_test.go b/datasource/schema/map_nested_attribute_test.go index ea6dc480d..671f55593 100644 --- a/datasource/schema/map_nested_attribute_test.go +++ b/datasource/schema/map_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -569,6 +570,34 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } } +func TestMapNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapNestedAttributeMapNestedValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/number_attribute.go b/datasource/schema/number_attribute.go index ffe4e0839..c21f74a15 100644 --- a/datasource/schema/number_attribute.go +++ b/datasource/schema/number_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -185,6 +186,11 @@ func (a NumberAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a NumberAttribute) IsWriteOnly() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/datasource/schema/number_attribute_test.go b/datasource/schema/number_attribute_test.go index f8cec323d..449dda7ce 100644 --- a/datasource/schema/number_attribute_test.go +++ b/datasource/schema/number_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -390,6 +391,34 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } } +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestNumberAttributeNumberValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/object_attribute.go b/datasource/schema/object_attribute.go index eafa40c6e..a004329ac 100644 --- a/datasource/schema/object_attribute.go +++ b/datasource/schema/object_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -204,6 +205,11 @@ func (a ObjectAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a ObjectAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/datasource/schema/object_attribute_test.go b/datasource/schema/object_attribute_test.go index c517465ed..ab3c428ce 100644 --- a/datasource/schema/object_attribute_test.go +++ b/datasource/schema/object_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -404,6 +405,34 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } } +func TestObjectAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestObjectAttributeObjectValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/set_attribute.go b/datasource/schema/set_attribute.go index 261b02424..859b0558d 100644 --- a/datasource/schema/set_attribute.go +++ b/datasource/schema/set_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -200,6 +201,11 @@ func (a SetAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a SetAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/datasource/schema/set_attribute_test.go b/datasource/schema/set_attribute_test.go index 793095705..9a0760b9b 100644 --- a/datasource/schema/set_attribute_test.go +++ b/datasource/schema/set_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -398,6 +399,34 @@ func TestSetAttributeIsSensitive(t *testing.T) { } } +func TestSetAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/set_nested_attribute.go b/datasource/schema/set_nested_attribute.go index 1b17b8743..26b14e449 100644 --- a/datasource/schema/set_nested_attribute.go +++ b/datasource/schema/set_nested_attribute.go @@ -226,6 +226,11 @@ func (a SetNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a SetNestedAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/datasource/schema/set_nested_attribute_test.go b/datasource/schema/set_nested_attribute_test.go index 7f7815f70..4b064ee7c 100644 --- a/datasource/schema/set_nested_attribute_test.go +++ b/datasource/schema/set_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -569,6 +570,34 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } } +func TestSetNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetNestedAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/single_nested_attribute.go b/datasource/schema/single_nested_attribute.go index 811e76de4..b6f4e7f32 100644 --- a/datasource/schema/single_nested_attribute.go +++ b/datasource/schema/single_nested_attribute.go @@ -240,6 +240,11 @@ func (a SingleNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a SingleNestedAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/datasource/schema/single_nested_attribute_test.go b/datasource/schema/single_nested_attribute_test.go index 25a5e565e..2a62a8e42 100644 --- a/datasource/schema/single_nested_attribute_test.go +++ b/datasource/schema/single_nested_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -530,6 +531,34 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } } +func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSingleNestedAttributeObjectValidators(t *testing.T) { t.Parallel() diff --git a/datasource/schema/string_attribute.go b/datasource/schema/string_attribute.go index 0c2dd9aba..95534fece 100644 --- a/datasource/schema/string_attribute.go +++ b/datasource/schema/string_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -181,6 +182,11 @@ func (a StringAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not supported in data source schemas. +func (a StringAttribute) IsWriteOnly() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/datasource/schema/string_attribute_test.go b/datasource/schema/string_attribute_test.go index d436b9bd1..f86110feb 100644 --- a/datasource/schema/string_attribute_test.go +++ b/datasource/schema/string_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -390,6 +391,34 @@ func TestStringAttributeIsSensitive(t *testing.T) { } } +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestStringAttributeStringValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/bool_attribute.go b/ephemeral/schema/bool_attribute.go index 47e248aae..56790dee2 100644 --- a/ephemeral/schema/bool_attribute.go +++ b/ephemeral/schema/bool_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -183,3 +184,9 @@ func (a BoolAttribute) IsRequired() bool { func (a BoolAttribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a BoolAttribute) IsWriteOnly() bool { + return false +} diff --git a/ephemeral/schema/bool_attribute_test.go b/ephemeral/schema/bool_attribute_test.go index 077ee1f1f..7eebac422 100644 --- a/ephemeral/schema/bool_attribute_test.go +++ b/ephemeral/schema/bool_attribute_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -423,3 +424,31 @@ func TestBoolAttributeIsSensitive(t *testing.T) { }) } } + +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/dynamic_attribute.go b/ephemeral/schema/dynamic_attribute.go index 5088fef63..9cd22e70a 100644 --- a/ephemeral/schema/dynamic_attribute.go +++ b/ephemeral/schema/dynamic_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -180,6 +181,12 @@ func (a DynamicAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a DynamicAttribute) IsWriteOnly() bool { + return false +} + // DynamicValidators returns the Validators field value. func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { return a.Validators diff --git a/ephemeral/schema/dynamic_attribute_test.go b/ephemeral/schema/dynamic_attribute_test.go index a718167a6..99fbf6f4e 100644 --- a/ephemeral/schema/dynamic_attribute_test.go +++ b/ephemeral/schema/dynamic_attribute_test.go @@ -390,6 +390,34 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } } +func TestDynamicAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestDynamicAttributeDynamicValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/float32_attribute.go b/ephemeral/schema/float32_attribute.go index 93ce2ac7d..46af5bae2 100644 --- a/ephemeral/schema/float32_attribute.go +++ b/ephemeral/schema/float32_attribute.go @@ -187,3 +187,9 @@ func (a Float32Attribute) IsRequired() bool { func (a Float32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a Float32Attribute) IsWriteOnly() bool { + return false +} diff --git a/ephemeral/schema/float32_attribute_test.go b/ephemeral/schema/float32_attribute_test.go index e9e45d785..f542972aa 100644 --- a/ephemeral/schema/float32_attribute_test.go +++ b/ephemeral/schema/float32_attribute_test.go @@ -424,3 +424,31 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/float64_attribute.go b/ephemeral/schema/float64_attribute.go index ff1912d1e..f4699e210 100644 --- a/ephemeral/schema/float64_attribute.go +++ b/ephemeral/schema/float64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -186,3 +187,9 @@ func (a Float64Attribute) IsRequired() bool { func (a Float64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a Float64Attribute) IsWriteOnly() bool { + return false +} diff --git a/ephemeral/schema/float64_attribute_test.go b/ephemeral/schema/float64_attribute_test.go index 4c3f2703a..db9e8db2b 100644 --- a/ephemeral/schema/float64_attribute_test.go +++ b/ephemeral/schema/float64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -423,3 +424,31 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat54AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/int32_attribute.go b/ephemeral/schema/int32_attribute.go index f98cbe3b0..76f6e0194 100644 --- a/ephemeral/schema/int32_attribute.go +++ b/ephemeral/schema/int32_attribute.go @@ -187,3 +187,9 @@ func (a Int32Attribute) IsRequired() bool { func (a Int32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a Int32Attribute) IsWriteOnly() bool { + return false +} diff --git a/ephemeral/schema/int32_attribute_test.go b/ephemeral/schema/int32_attribute_test.go index bd6d4d36c..e118cb0db 100644 --- a/ephemeral/schema/int32_attribute_test.go +++ b/ephemeral/schema/int32_attribute_test.go @@ -424,3 +424,31 @@ func TestInt32AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt2AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/int64_attribute.go b/ephemeral/schema/int64_attribute.go index 52b1b7ffd..27cb2dd23 100644 --- a/ephemeral/schema/int64_attribute.go +++ b/ephemeral/schema/int64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -186,3 +187,9 @@ func (a Int64Attribute) IsRequired() bool { func (a Int64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a Int64Attribute) IsWriteOnly() bool { + return false +} diff --git a/ephemeral/schema/int64_attribute_test.go b/ephemeral/schema/int64_attribute_test.go index e61165c5f..5f07ff2ff 100644 --- a/ephemeral/schema/int64_attribute_test.go +++ b/ephemeral/schema/int64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -423,3 +424,31 @@ func TestInt64AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/list_attribute.go b/ephemeral/schema/list_attribute.go index 3846316b6..258757f99 100644 --- a/ephemeral/schema/list_attribute.go +++ b/ephemeral/schema/list_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -74,7 +75,6 @@ type ListAttribute struct { // Sensitive indicates whether the value of this attribute should be // considered sensitive data. Setting it to true will obscure the value - // in CLI output. Sensitive bool // Description is used in various tooling, like the language server, to @@ -200,6 +200,12 @@ func (a ListAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a ListAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListAttribute) ListValidators() []validator.List { return a.Validators diff --git a/ephemeral/schema/list_attribute_test.go b/ephemeral/schema/list_attribute_test.go index 1b22bbbdc..4ab1985d8 100644 --- a/ephemeral/schema/list_attribute_test.go +++ b/ephemeral/schema/list_attribute_test.go @@ -521,3 +521,31 @@ func TestListAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/list_nested_attribute.go b/ephemeral/schema/list_nested_attribute.go index 4a91f2742..a4478fb48 100644 --- a/ephemeral/schema/list_nested_attribute.go +++ b/ephemeral/schema/list_nested_attribute.go @@ -7,6 +7,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -14,7 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -228,6 +229,12 @@ func (a ListNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a ListNestedAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/ephemeral/schema/list_nested_attribute_test.go b/ephemeral/schema/list_nested_attribute_test.go index 3d1ae651d..7e1153e79 100644 --- a/ephemeral/schema/list_nested_attribute_test.go +++ b/ephemeral/schema/list_nested_attribute_test.go @@ -569,6 +569,34 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } } +func TestListNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListNestedAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/map_attribute.go b/ephemeral/schema/map_attribute.go index 366619ed5..0741747a4 100644 --- a/ephemeral/schema/map_attribute.go +++ b/ephemeral/schema/map_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -203,6 +204,12 @@ func (a MapAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a MapAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/ephemeral/schema/map_attribute_test.go b/ephemeral/schema/map_attribute_test.go index 94219da83..7da8aa46f 100644 --- a/ephemeral/schema/map_attribute_test.go +++ b/ephemeral/schema/map_attribute_test.go @@ -398,6 +398,34 @@ func TestMapAttributeIsSensitive(t *testing.T) { } } +func TestMapAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapAttributeMapValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/map_nested_attribute.go b/ephemeral/schema/map_nested_attribute.go index 11d701cc9..de057e106 100644 --- a/ephemeral/schema/map_nested_attribute.go +++ b/ephemeral/schema/map_nested_attribute.go @@ -229,6 +229,12 @@ func (a MapNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a MapNestedAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/ephemeral/schema/map_nested_attribute_test.go b/ephemeral/schema/map_nested_attribute_test.go index 0e7986f89..ef21cf89b 100644 --- a/ephemeral/schema/map_nested_attribute_test.go +++ b/ephemeral/schema/map_nested_attribute_test.go @@ -569,6 +569,34 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } } +func TestMapNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapNestedAttributeMapNestedValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/number_attribute.go b/ephemeral/schema/number_attribute.go index 547396409..17d557398 100644 --- a/ephemeral/schema/number_attribute.go +++ b/ephemeral/schema/number_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -183,6 +184,12 @@ func (a NumberAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a NumberAttribute) IsWriteOnly() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/ephemeral/schema/number_attribute_test.go b/ephemeral/schema/number_attribute_test.go index 7e326b90e..d3636d006 100644 --- a/ephemeral/schema/number_attribute_test.go +++ b/ephemeral/schema/number_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -390,6 +391,34 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } } +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestNumberAttributeNumberValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/object_attribute.go b/ephemeral/schema/object_attribute.go index 1ff506f9d..fa808e4ba 100644 --- a/ephemeral/schema/object_attribute.go +++ b/ephemeral/schema/object_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -76,7 +77,6 @@ type ObjectAttribute struct { // Sensitive indicates whether the value of this attribute should be // considered sensitive data. Setting it to true will obscure the value - // in CLI output. Sensitive bool // Description is used in various tooling, like the language server, to @@ -202,6 +202,12 @@ func (a ObjectAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a ObjectAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/ephemeral/schema/object_attribute_test.go b/ephemeral/schema/object_attribute_test.go index 429aba1e7..76f4aab38 100644 --- a/ephemeral/schema/object_attribute_test.go +++ b/ephemeral/schema/object_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -438,6 +439,34 @@ func TestObjectAttributeObjectValidators(t *testing.T) { } } +func TestObjectAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestObjectAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/set_attribute.go b/ephemeral/schema/set_attribute.go index 7f6a408ed..7ecd08ffd 100644 --- a/ephemeral/schema/set_attribute.go +++ b/ephemeral/schema/set_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -198,6 +199,12 @@ func (a SetAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a SetAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/ephemeral/schema/set_attribute_test.go b/ephemeral/schema/set_attribute_test.go index 0b6903829..a6211d666 100644 --- a/ephemeral/schema/set_attribute_test.go +++ b/ephemeral/schema/set_attribute_test.go @@ -398,6 +398,34 @@ func TestSetAttributeIsSensitive(t *testing.T) { } } +func TestSetAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/set_nested_attribute.go b/ephemeral/schema/set_nested_attribute.go index 7ed6d2fdf..658dc0df7 100644 --- a/ephemeral/schema/set_nested_attribute.go +++ b/ephemeral/schema/set_nested_attribute.go @@ -224,6 +224,12 @@ func (a SetNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a SetNestedAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/ephemeral/schema/set_nested_attribute_test.go b/ephemeral/schema/set_nested_attribute_test.go index 1bd3daa65..bcfa627a4 100644 --- a/ephemeral/schema/set_nested_attribute_test.go +++ b/ephemeral/schema/set_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -569,6 +570,34 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } } +func TestSetNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetNestedAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/single_nested_attribute.go b/ephemeral/schema/single_nested_attribute.go index a57db9c65..167b8c136 100644 --- a/ephemeral/schema/single_nested_attribute.go +++ b/ephemeral/schema/single_nested_attribute.go @@ -238,6 +238,12 @@ func (a SingleNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a SingleNestedAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/ephemeral/schema/single_nested_attribute_test.go b/ephemeral/schema/single_nested_attribute_test.go index 5b7209edb..d3690828f 100644 --- a/ephemeral/schema/single_nested_attribute_test.go +++ b/ephemeral/schema/single_nested_attribute_test.go @@ -530,6 +530,34 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } } +func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSingleNestedAttributeObjectValidators(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/string_attribute.go b/ephemeral/schema/string_attribute.go index 1b5c7db22..3cfefe97c 100644 --- a/ephemeral/schema/string_attribute.go +++ b/ephemeral/schema/string_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -179,6 +180,12 @@ func (a StringAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas, +// as these schemas describe data that is explicitly not saved to any artifact. +func (a StringAttribute) IsWriteOnly() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/ephemeral/schema/string_attribute_test.go b/ephemeral/schema/string_attribute_test.go index 1c95add7d..e0577b3cb 100644 --- a/ephemeral/schema/string_attribute_test.go +++ b/ephemeral/schema/string_attribute_test.go @@ -390,6 +390,34 @@ func TestStringAttributeIsSensitive(t *testing.T) { } } +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestStringAttributeStringValidators(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/client_capabilities.go b/internal/fromproto5/client_capabilities.go index 0eaf40c50..737354888 100644 --- a/internal/fromproto5/client_capabilities.go +++ b/internal/fromproto5/client_capabilities.go @@ -89,3 +89,16 @@ func OpenEphemeralResourceClientCapabilities(in *tfprotov5.OpenEphemeralResource DeferralAllowed: in.DeferralAllowed, } } + +func ValidateResourceTypeConfigClientCapabilities(in *tfprotov5.ValidateResourceTypeConfigClientCapabilities) resource.ValidateConfigClientCapabilities { + if in == nil { + // Client did not indicate any supported capabilities + return resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: false, + } + } + + return resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: in.WriteOnlyAttributesAllowed, + } +} diff --git a/internal/fromproto5/validateresourcetypeconfig.go b/internal/fromproto5/validateresourcetypeconfig.go index ae6e41298..1919f9e9c 100644 --- a/internal/fromproto5/validateresourcetypeconfig.go +++ b/internal/fromproto5/validateresourcetypeconfig.go @@ -6,11 +6,12 @@ package fromproto5 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" ) // ValidateResourceTypeConfigRequest returns the *fwserver.ValidateResourceConfigRequest @@ -26,6 +27,7 @@ func ValidateResourceTypeConfigRequest(ctx context.Context, proto5 *tfprotov5.Va fw.Config = config fw.Resource = resource + fw.ClientCapabilities = ValidateResourceTypeConfigClientCapabilities(proto5.ClientCapabilities) return fw, diags } diff --git a/internal/fromproto5/validateresourcetypeconfig_test.go b/internal/fromproto5/validateresourcetypeconfig_test.go index 8aec17098..5c07807ed 100644 --- a/internal/fromproto5/validateresourcetypeconfig_test.go +++ b/internal/fromproto5/validateresourcetypeconfig_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -15,8 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov5" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestValidateResourceTypeConfigRequest(t *testing.T) { @@ -88,6 +89,39 @@ func TestValidateResourceTypeConfigRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov5.ValidateResourceTypeConfigRequest{ + Config: &testProto5DynamicValue, + ClientCapabilities: &tfprotov5.ValidateResourceTypeConfigClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + Config: &tfsdk.Config{ + Raw: testProto5Value, + Schema: testFwSchema, + }, + }, + }, + "client-capabilities-not-set": { + input: &tfprotov5.ValidateResourceTypeConfigRequest{ + Config: &testProto5DynamicValue, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: false, + }, + Config: &tfsdk.Config{ + Raw: testProto5Value, + Schema: testFwSchema, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fromproto6/client_capabilities.go b/internal/fromproto6/client_capabilities.go index cd9c92b9c..d22d81623 100644 --- a/internal/fromproto6/client_capabilities.go +++ b/internal/fromproto6/client_capabilities.go @@ -89,3 +89,16 @@ func OpenEphemeralResourceClientCapabilities(in *tfprotov6.OpenEphemeralResource DeferralAllowed: in.DeferralAllowed, } } + +func ValidateResourceConfigClientCapabilities(in *tfprotov6.ValidateResourceConfigClientCapabilities) resource.ValidateConfigClientCapabilities { + if in == nil { + // Client did not indicate any supported capabilities + return resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: false, + } + } + + return resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: in.WriteOnlyAttributesAllowed, + } +} diff --git a/internal/fromproto6/validateresourceconfig.go b/internal/fromproto6/validateresourceconfig.go index 1eb65aa87..f3ea643c9 100644 --- a/internal/fromproto6/validateresourceconfig.go +++ b/internal/fromproto6/validateresourceconfig.go @@ -6,11 +6,12 @@ package fromproto6 import ( "context" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) // ValidateResourceConfigRequest returns the *fwserver.ValidateResourceConfigRequest @@ -26,6 +27,7 @@ func ValidateResourceConfigRequest(ctx context.Context, proto6 *tfprotov6.Valida fw.Config = config fw.Resource = resource + fw.ClientCapabilities = ValidateResourceConfigClientCapabilities(proto6.ClientCapabilities) return fw, diags } diff --git a/internal/fromproto6/validateresourceconfig_test.go b/internal/fromproto6/validateresourceconfig_test.go index 4e21d432c..f649a0d77 100644 --- a/internal/fromproto6/validateresourceconfig_test.go +++ b/internal/fromproto6/validateresourceconfig_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -15,8 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestValidateResourceConfigRequest(t *testing.T) { @@ -88,6 +89,39 @@ func TestValidateResourceConfigRequest(t *testing.T) { }, }, }, + "client-capabilities": { + input: &tfprotov6.ValidateResourceConfigRequest{ + Config: &testProto6DynamicValue, + ClientCapabilities: &tfprotov6.ValidateResourceConfigClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + Config: &tfsdk.Config{ + Raw: testProto6Value, + Schema: testFwSchema, + }, + }, + }, + "client-capabilities-not-set": { + input: &tfprotov6.ValidateResourceConfigRequest{ + Config: &testProto6DynamicValue, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: false, + }, + Config: &tfsdk.Config{ + Raw: testProto6Value, + Schema: testFwSchema, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwschema/attribute.go b/internal/fwschema/attribute.go index 4face5a95..6c440b319 100644 --- a/internal/fwschema/attribute.go +++ b/internal/fwschema/attribute.go @@ -4,8 +4,9 @@ package fwschema import ( - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" ) // Attribute is the core interface required for implementing Terraform @@ -63,6 +64,13 @@ type Attribute interface { // sensitive. This is named differently than Sensitive to prevent a // conflict with the tfsdk.Attribute field name. IsSensitive() bool + + // IsWriteOnly should return true if the attribute configuration value is + // write-only. This is named differently than WriteOnly to prevent a + // conflict with the tfsdk.Attribute field name. + // + // Write-only attributes are a managed-resource schema concept only. + IsWriteOnly() bool } // AttributesEqual is a helper function to perform equality testing on two @@ -101,5 +109,9 @@ func AttributesEqual(a, b Attribute) bool { return false } + if a.IsWriteOnly() != b.IsWriteOnly() { + return false + } + return true } diff --git a/internal/fwschema/write_only_nested_attribute_validation.go b/internal/fwschema/write_only_nested_attribute_validation.go new file mode 100644 index 000000000..4cba87413 --- /dev/null +++ b/internal/fwschema/write_only_nested_attribute_validation.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwschema + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" +) + +// ContainsAllWriteOnlyChildAttributes will return true if all child attributes for the +// given nested attribute have WriteOnly set to true. +func ContainsAllWriteOnlyChildAttributes(nestedAttr NestedAttribute) bool { + nestedObjAttrs := nestedAttr.GetNestedObject().GetAttributes() + + for _, childAttr := range nestedObjAttrs { + if !childAttr.IsWriteOnly() { + return false + } + + nestedAttribute, ok := childAttr.(NestedAttribute) + if ok { + if !ContainsAllWriteOnlyChildAttributes(nestedAttribute) { + return false + } + } + } + + return true +} + +// ContainsAnyWriteOnlyChildAttributes will return true if any child attribute for the +// given nested attribute has WriteOnly set to true. +func ContainsAnyWriteOnlyChildAttributes(nestedAttr NestedAttribute) bool { + nestedObjAttrs := nestedAttr.GetNestedObject().GetAttributes() + + for _, childAttr := range nestedObjAttrs { + if childAttr.IsWriteOnly() { + return true + } + + nestedAttribute, ok := childAttr.(NestedAttribute) + if ok { + if ContainsAnyWriteOnlyChildAttributes(nestedAttribute) { + return true + } + } + } + + return false +} + +func InvalidWriteOnlyNestedAttributeDiag(attributePath path.Path) diag.Diagnostic { + return diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("%q is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n", attributePath)+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ) +} + +func InvalidComputedNestedAttributeWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic { + return diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("%q is a Computed nested attribute that contains a WriteOnly child attribute.\n\n", attributePath)+ + "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + ) +} diff --git a/internal/fwschemadata/data_nullify_collection_blocks.go b/internal/fwschemadata/data_nullify_collection_blocks.go index f907d6d16..a0a19ecd4 100644 --- a/internal/fwschemadata/data_nullify_collection_blocks.go +++ b/internal/fwschemadata/data_nullify_collection_blocks.go @@ -7,11 +7,12 @@ import ( "context" "errors" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fromtftypes" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/logging" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // NullifyCollectionBlocks converts list and set block empty values to null diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 98b05f0fb..3c2492b14 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -34,6 +34,11 @@ type ValidateAttributeRequest struct { // Config contains the entire configuration of the data source, provider, or resource. Config tfsdk.Config + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities validator.ValidateSchemaClientCapabilities } // ValidateAttributeResponse represents a response to a @@ -65,6 +70,24 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt return } + if a.IsWriteOnly() && a.IsRequired() && a.IsOptional() { + resp.Diagnostics.AddAttributeError( + req.AttributePath, + "Invalid Attribute Definition", + "WriteOnly Attributes must be set with only one of Required or Optional. This is always a problem with the provider and should be reported to the provider developer.", + ) + return + } + + if a.IsWriteOnly() && a.IsComputed() { + resp.Diagnostics.AddAttributeError( + req.AttributePath, + "Invalid Attribute Definition", + "WriteOnly Attributes cannot be set with Computed. This is always a problem with the provider and should be reported to the provider developer.", + ) + return + } + configData := &fwschemadata.Data{ Description: fwschemadata.DataDescriptionConfiguration, Schema: req.Config.Schema, @@ -106,6 +129,19 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt ) } + // If the client doesn't support write-only attributes (first supported in Terraform v1.11.0), then we raise an early validation error + // to avoid a confusing data consistency error when the provider attempts to return "null" for a write-only attribute in the planned/final state. + // + // Write-only attributes can only be successfully used with a supporting client, so the only option for a practitoner to utilize a write-only attribute + // is to upgrade their Terraform CLI version to v1.11.0 or later. + if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !attributeConfig.IsNull() { + resp.Diagnostics.AddAttributeError( + req.AttributePath, + "WriteOnly Attribute Not Allowed", + fmt.Sprintf("The resource contains a non-null value for WriteOnly attribute %s. Write-only attributes are only supported in Terraform 1.11 and later.", req.AttributePath.String()), + ) + } + req.AttributeConfig = attributeConfig switch attributeWithValidators := a.(type) { @@ -200,10 +236,11 @@ func AttributeValidateBool(ctx context.Context, attribute fwxschema.AttributeWit } validateReq := validator.BoolRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.BoolValidators() { @@ -265,10 +302,11 @@ func AttributeValidateFloat32(ctx context.Context, attribute fwxschema.Attribute } validateReq := validator.Float32Request{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.Float32Validators() { @@ -330,10 +368,11 @@ func AttributeValidateFloat64(ctx context.Context, attribute fwxschema.Attribute } validateReq := validator.Float64Request{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.Float64Validators() { @@ -395,10 +434,11 @@ func AttributeValidateInt32(ctx context.Context, attribute fwxschema.AttributeWi } validateReq := validator.Int32Request{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.Int32Validators() { @@ -460,10 +500,11 @@ func AttributeValidateInt64(ctx context.Context, attribute fwxschema.AttributeWi } validateReq := validator.Int64Request{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.Int64Validators() { @@ -525,10 +566,11 @@ func AttributeValidateList(ctx context.Context, attribute fwxschema.AttributeWit } validateReq := validator.ListRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.ListValidators() { @@ -590,10 +632,11 @@ func AttributeValidateMap(ctx context.Context, attribute fwxschema.AttributeWith } validateReq := validator.MapRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.MapValidators() { @@ -655,10 +698,11 @@ func AttributeValidateNumber(ctx context.Context, attribute fwxschema.AttributeW } validateReq := validator.NumberRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.NumberValidators() { @@ -720,10 +764,11 @@ func AttributeValidateObject(ctx context.Context, attribute fwxschema.AttributeW } validateReq := validator.ObjectRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.ObjectValidators() { @@ -785,10 +830,11 @@ func AttributeValidateSet(ctx context.Context, attribute fwxschema.AttributeWith } validateReq := validator.SetRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.SetValidators() { @@ -850,10 +896,11 @@ func AttributeValidateString(ctx context.Context, attribute fwxschema.AttributeW } validateReq := validator.StringRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.StringValidators() { @@ -915,10 +962,11 @@ func AttributeValidateDynamic(ctx context.Context, attribute fwxschema.Attribute } validateReq := validator.DynamicRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, attributeValidator := range attribute.DynamicValidators() { @@ -992,6 +1040,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute AttributePath: req.AttributePath.AtListIndex(idx), AttributePathExpression: req.AttributePathExpression.AtListIndex(idx), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } nestedAttributeObjectResp := &ValidateAttributeResponse{} @@ -1026,6 +1075,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute AttributePath: req.AttributePath.AtSetValue(value), AttributePathExpression: req.AttributePathExpression.AtSetValue(value), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } nestedAttributeObjectResp := &ValidateAttributeResponse{} @@ -1060,6 +1110,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute AttributePath: req.AttributePath.AtMapKey(key), AttributePathExpression: req.AttributePathExpression.AtMapKey(key), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } nestedAttributeObjectResp := &ValidateAttributeResponse{} @@ -1097,6 +1148,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute AttributePath: req.AttributePath, AttributePathExpression: req.AttributePathExpression, Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } nestedAttributeObjectResp := &ValidateAttributeResponse{} @@ -1144,10 +1196,11 @@ func NestedAttributeObjectValidate(ctx context.Context, o fwschema.NestedAttribu } validateReq := validator.ObjectRequest{ - Config: req.Config, - ConfigValue: object, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: object, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, objectValidator := range objectWithValidators.ObjectValidators() { @@ -1182,6 +1235,7 @@ func NestedAttributeObjectValidate(ctx context.Context, o fwschema.NestedAttribu AttributePath: req.AttributePath.AtName(nestedName), AttributePathExpression: req.AttributePathExpression.AtName(nestedName), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } nestedAttrResp := &ValidateAttributeResponse{} diff --git a/internal/fwserver/attribute_validation_test.go b/internal/fwserver/attribute_validation_test.go index 457264e0f..6f0fbc5ab 100644 --- a/internal/fwserver/attribute_validation_test.go +++ b/internal/fwserver/attribute_validation_test.go @@ -1700,6 +1700,275 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "write-only-attr-with-required": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Required: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, + "write-only-attr-with-required-null-value": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Required: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Missing Configuration for Required Attribute", + "Must set a configuration value for the test attribute as the provider has marked it as required.\n\n"+ + "Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.", + ), + }, + }, + }, + "write-only-attr-with-optional": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Optional: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, + "write-only-attr-with-computed": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Computed: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Definition", + "WriteOnly Attributes cannot be set with Computed. This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + }, + }, + "write-only-attr-missing-required-and-optional": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Definition", + "Attribute missing Required, Optional, or Computed definition. This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + }, + }, + "write-only-attr-with-required-and-optional": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Required: true, + Optional: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Definition", + "WriteOnly Attributes must be set with only one of Required or Optional. This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + }, + }, + "write-only-attr-with-computed-required-and-optional": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + WriteOnly: true, + Required: true, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Invalid Attribute Definition", + "WriteOnly Attributes must be set with only one of Required or Optional. This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + }, + }, + "write-only-attr-set-no-client-capability": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + // Client indicating it doesn't support write-only attributes + WriteOnlyAttributesAllowed: false, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "hello world!"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Required: true, + WriteOnly: true, + Type: types.StringType, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "WriteOnly Attribute Not Allowed", + "The resource contains a non-null value for WriteOnly attribute test. "+ + "Write-only attributes are only supported in Terraform 1.11 and later.", + ), + }, + }, + }, } for name, tc := range testCases { @@ -1786,6 +2055,32 @@ func TestAttributeValidateBool(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithBoolValidators{ + Validators: []validator.Bool{ + testvalidator.Bool{ + ValidateBoolMethod: func(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected BoolRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.BoolValue(true), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithBoolValidators{ Validators: []validator.Bool{ @@ -1990,6 +2285,32 @@ func TestAttributeValidateFloat32(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithFloat32Validators{ + Validators: []validator.Float32{ + testvalidator.Float32{ + ValidateFloat32Method: func(ctx context.Context, req validator.Float32Request, resp *validator.Float32Response) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected Float32Request.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.Float32Value(0.1), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithFloat32Validators{ Validators: []validator.Float32{ @@ -2194,6 +2515,32 @@ func TestAttributeValidateFloat64(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithFloat64Validators{ + Validators: []validator.Float64{ + testvalidator.Float64{ + ValidateFloat64Method: func(ctx context.Context, req validator.Float64Request, resp *validator.Float64Response) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected Float64Request.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.Float64Value(0.2), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithFloat64Validators{ Validators: []validator.Float64{ @@ -2398,6 +2745,32 @@ func TestAttributeValidateInt32(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithInt32Validators{ + Validators: []validator.Int32{ + testvalidator.Int32{ + ValidateInt32Method: func(ctx context.Context, req validator.Int32Request, resp *validator.Int32Response) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected Int32Request.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.Int32Value(1), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithInt32Validators{ Validators: []validator.Int32{ @@ -2602,6 +2975,32 @@ func TestAttributeValidateInt64(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithInt64Validators{ + Validators: []validator.Int64{ + testvalidator.Int64{ + ValidateInt64Method: func(ctx context.Context, req validator.Int64Request, resp *validator.Int64Response) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected Int64Request.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.Int64Value(2), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithInt64Validators{ Validators: []validator.Int64{ @@ -2808,6 +3207,32 @@ func TestAttributeValidateList(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithListValidators{ + Validators: []validator.List{ + testvalidator.List{ + ValidateListMethod: func(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ListRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("test")}), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithListValidators{ ElementType: types.StringType, @@ -3033,6 +3458,35 @@ func TestAttributeValidateMap(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithMapValidators{ + Validators: []validator.Map{ + testvalidator.Map{ + ValidateMapMethod: func(ctx context.Context, req validator.MapRequest, resp *validator.MapResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected MapRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.MapValueMust( + types.StringType, + map[string]attr.Value{"testkey": types.StringValue("testvalue")}, + ), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithMapValidators{ ElementType: types.StringType, @@ -3262,6 +3716,32 @@ func TestAttributeValidateNumber(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithNumberValidators{ + Validators: []validator.Number{ + testvalidator.Number{ + ValidateNumberMethod: func(ctx context.Context, req validator.NumberRequest, resp *validator.NumberResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected NumberRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.NumberValue(big.NewFloat(1.2)), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithNumberValidators{ Validators: []validator.Number{ @@ -3478,6 +3958,35 @@ func TestAttributeValidateObject(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithObjectValidators{ + Validators: []validator.Object{ + testvalidator.Object{ + ValidateObjectMethod: func(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ObjectRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.ObjectValueMust( + map[string]attr.Type{"testattr": types.StringType}, + map[string]attr.Value{"testattr": types.StringValue("testvalue")}, + ), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithObjectValidators{ AttributeTypes: map[string]attr.Type{ @@ -3715,6 +4224,32 @@ func TestAttributeValidateSet(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithSetValidators{ + Validators: []validator.Set{ + testvalidator.Set{ + ValidateSetMethod: func(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected SetRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.SetValueMust(types.StringType, []attr.Value{types.StringValue("test")}), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithSetValidators{ ElementType: types.StringType, @@ -3932,6 +4467,32 @@ func TestAttributeValidateString(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithStringValidators{ + Validators: []validator.String{ + testvalidator.String{ + ValidateStringMethod: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected StringRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.StringValue("testVal"), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithStringValidators{ Validators: []validator.String{ @@ -4136,6 +4697,32 @@ func TestAttributeValidateDynamic(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + attribute: testschema.AttributeWithDynamicValidators{ + Validators: []validator.Dynamic{ + testvalidator.Dynamic{ + ValidateDynamicMethod: func(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected DynamicRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.DynamicValue(types.StringValue("test")), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { attribute: testschema.AttributeWithDynamicValidators{ Validators: []validator.Dynamic{ @@ -4373,6 +4960,32 @@ func TestNestedAttributeObjectValidateObject(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + object: testschema.NestedAttributeObjectWithValidators{ + Validators: []validator.Object{ + testvalidator.Object{ + ValidateObjectMethod: func(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ObjectRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: testAttributeConfig, + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, + "request-config": { object: testschema.NestedAttributeObjectWithValidators{ Validators: []validator.Object{ diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index d65b1b5c7..fcf3898f8 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -76,6 +76,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req ValidateAttributeR AttributeConfig: value, AttributePath: req.AttributePath.AtListIndex(idx), AttributePathExpression: req.AttributePathExpression.AtListIndex(idx), + ClientCapabilities: req.ClientCapabilities, Config: req.Config, } nestedBlockObjectResp := &ValidateAttributeResponse{} @@ -110,6 +111,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req ValidateAttributeR AttributeConfig: value, AttributePath: req.AttributePath.AtSetValue(value), AttributePathExpression: req.AttributePathExpression.AtSetValue(value), + ClientCapabilities: req.ClientCapabilities, Config: req.Config, } nestedBlockObjectResp := &ValidateAttributeResponse{} @@ -143,6 +145,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req ValidateAttributeR AttributeConfig: o, AttributePath: req.AttributePath, AttributePathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, Config: req.Config, } nestedBlockObjectResp := &ValidateAttributeResponse{} @@ -203,10 +206,11 @@ func BlockValidateList(ctx context.Context, block fwxschema.BlockWithListValidat } validateReq := validator.ListRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, blockValidator := range block.ListValidators() { @@ -268,10 +272,11 @@ func BlockValidateObject(ctx context.Context, block fwxschema.BlockWithObjectVal } validateReq := validator.ObjectRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, blockValidator := range block.ObjectValidators() { @@ -333,10 +338,11 @@ func BlockValidateSet(ctx context.Context, block fwxschema.BlockWithSetValidator } validateReq := validator.SetRequest{ - Config: req.Config, - ConfigValue: configValue, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: configValue, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, blockValidator := range block.SetValidators() { @@ -395,10 +401,11 @@ func NestedBlockObjectValidate(ctx context.Context, o fwschema.NestedBlockObject } validateReq := validator.ObjectRequest{ - Config: req.Config, - ConfigValue: object, - Path: req.AttributePath, - PathExpression: req.AttributePathExpression, + ClientCapabilities: req.ClientCapabilities, + Config: req.Config, + ConfigValue: object, + Path: req.AttributePath, + PathExpression: req.AttributePathExpression, } for _, objectValidator := range objectWithValidators.ObjectValidators() { @@ -432,6 +439,7 @@ func NestedBlockObjectValidate(ctx context.Context, o fwschema.NestedBlockObject nestedAttrReq := ValidateAttributeRequest{ AttributePath: req.AttributePath.AtName(nestedName), AttributePathExpression: req.AttributePathExpression.AtName(nestedName), + ClientCapabilities: req.ClientCapabilities, Config: req.Config, } nestedAttrResp := &ValidateAttributeResponse{} @@ -445,6 +453,7 @@ func NestedBlockObjectValidate(ctx context.Context, o fwschema.NestedBlockObject nestedBlockReq := ValidateAttributeRequest{ AttributePath: req.AttributePath.AtName(nestedName), AttributePathExpression: req.AttributePathExpression.AtName(nestedName), + ClientCapabilities: req.ClientCapabilities, Config: req.Config, } nestedBlockResp := &ValidateAttributeResponse{} diff --git a/internal/fwserver/block_validation_test.go b/internal/fwserver/block_validation_test.go index 367a0df1c..40726f22f 100644 --- a/internal/fwserver/block_validation_test.go +++ b/internal/fwserver/block_validation_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -20,7 +22,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestBlockValidate(t *testing.T) { @@ -746,6 +747,77 @@ func TestBlockValidate(t *testing.T) { }, }, }, + "list-validation-client-capabilities": { + req: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{WriteOnlyAttributesAllowed: true}, + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: testschema.Schema{ + Blocks: map[string]fwschema.Block{ + "test": testschema.Block{ + NestedObject: testschema.NestedBlockObject{ + Attributes: map[string]fwschema.Attribute{ + "nested_attr": testschema.AttributeWithStringValidators{ + Required: true, + Validators: []validator.String{ + testvalidator.String{ + ValidateStringMethod: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected StringRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + }, + }, + NestingMode: fwschema.BlockNestingModeList, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, "set-no-validation": { req: ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -874,6 +946,77 @@ func TestBlockValidate(t *testing.T) { }, }, }, + "set-validation-client-capabilities": { + req: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{WriteOnlyAttributesAllowed: true}, + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: testschema.Schema{ + Blocks: map[string]fwschema.Block{ + "test": testschema.Block{ + NestedObject: testschema.NestedBlockObject{ + Attributes: map[string]fwschema.Attribute{ + "nested_attr": testschema.AttributeWithStringValidators{ + Required: true, + Validators: []validator.String{ + testvalidator.String{ + ValidateStringMethod: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected StringRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + }, + }, + NestingMode: fwschema.BlockNestingModeSet, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, "single-no-validation": { req: ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -976,6 +1119,64 @@ func TestBlockValidate(t *testing.T) { }, }, }, + "single-validation-client-capabilities": { + req: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{WriteOnlyAttributesAllowed: true}, + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: testschema.Schema{ + Blocks: map[string]fwschema.Block{ + "test": testschema.Block{ + NestedObject: testschema.NestedBlockObject{ + Attributes: map[string]fwschema.Attribute{ + "nested_attr": testschema.AttributeWithStringValidators{ + Required: true, + Validators: []validator.String{ + testvalidator.String{ + ValidateStringMethod: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected StringRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + }, + }, + NestingMode: fwschema.BlockNestingModeSingle, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, } for name, tc := range testCases { @@ -1085,6 +1286,44 @@ func TestBlockValidateList(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + block: testschema.BlockWithListValidators{ + Attributes: map[string]fwschema.Attribute{ + "testattr": testschema.AttributeWithStringValidators{}, + }, + Validators: []validator.List{ + testvalidator.List{ + ValidateListMethod: func(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ListRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.ListValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{"testattr": types.StringType}, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{"testattr": types.StringType}, + map[string]attr.Value{"testattr": types.StringValue("test")}, + ), + }, + ), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, "request-config": { block: testschema.BlockWithListValidators{ Attributes: map[string]fwschema.Attribute{ @@ -1402,6 +1641,37 @@ func TestBlockValidateObject(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + block: testschema.BlockWithObjectValidators{ + Attributes: map[string]fwschema.Attribute{ + "testattr": testschema.AttributeWithStringValidators{}, + }, + Validators: []validator.Object{ + testvalidator.Object{ + ValidateObjectMethod: func(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ObjectRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.ObjectValueMust( + map[string]attr.Type{"testattr": types.StringType}, + map[string]attr.Value{"testattr": types.StringValue("test")}, + ), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, "request-config": { block: testschema.BlockWithObjectValidators{ Attributes: map[string]fwschema.Attribute{ @@ -1679,6 +1949,44 @@ func TestBlockValidateSet(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + block: testschema.BlockWithSetValidators{ + Attributes: map[string]fwschema.Attribute{ + "testattr": testschema.AttributeWithStringValidators{}, + }, + Validators: []validator.Set{ + testvalidator.Set{ + ValidateSetMethod: func(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected SetRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: types.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{"testattr": types.StringType}, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{"testattr": types.StringType}, + map[string]attr.Value{"testattr": types.StringValue("test")}, + ), + }, + ), + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, "request-config": { block: testschema.BlockWithSetValidators{ Attributes: map[string]fwschema.Attribute{ @@ -2069,6 +2377,31 @@ func TestNestedBlockObjectValidateObject(t *testing.T) { response: &ValidateAttributeResponse{}, expected: &ValidateAttributeResponse{}, }, + "request-client-capabilities": { + object: testschema.NestedBlockObjectWithValidators{ + Validators: []validator.Object{ + testvalidator.Object{ + ValidateObjectMethod: func(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError( + "Unexpected ObjectRequest.ClientCapabilities", + "Missing WriteOnlyAttributesAllowed client capability", + ) + } + }, + }, + }, + }, + request: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + AttributeConfig: testAttributeConfig, + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + }, + response: &ValidateAttributeResponse{}, + expected: &ValidateAttributeResponse{}, + }, "request-config": { object: testschema.NestedBlockObjectWithValidators{ Validators: []validator.Object{ diff --git a/internal/fwserver/schema_validation.go b/internal/fwserver/schema_validation.go index 32c50f9d4..dedce9d43 100644 --- a/internal/fwserver/schema_validation.go +++ b/internal/fwserver/schema_validation.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -20,6 +21,11 @@ type ValidateSchemaRequest struct { // interpolation or other functionality that would prevent Terraform // from knowing the value at request time. Config tfsdk.Config + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities validator.ValidateSchemaClientCapabilities } // ValidateSchemaResponse represents a response to a @@ -43,6 +49,7 @@ func SchemaValidate(ctx context.Context, s fwschema.Schema, req ValidateSchemaRe AttributePath: path.Root(name), AttributePathExpression: path.MatchRoot(name), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. @@ -58,6 +65,7 @@ func SchemaValidate(ctx context.Context, s fwschema.Schema, req ValidateSchemaRe AttributePath: path.Root(name), AttributePathExpression: path.MatchRoot(name), Config: req.Config, + ClientCapabilities: req.ClientCapabilities, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. diff --git a/internal/fwserver/server_applyresourcechange_test.go b/internal/fwserver/server_applyresourcechange_test.go index 59e10d76c..41ab43c77 100644 --- a/internal/fwserver/server_applyresourcechange_test.go +++ b/internal/fwserver/server_applyresourcechange_test.go @@ -59,6 +59,31 @@ func TestServerApplyResourceChange(t *testing.T) { TestRequired types.String `tfsdk:"test_required"` } + testSchemaTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_optional_write_only": tftypes.String, + "test_required_write_only": tftypes.String, + }, + } + + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test_optional_write_only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "test_required_write_only": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + } + + type testSchemaDataWriteOnly struct { + TestOptionalWriteOnly types.String `tfsdk:"test_optional_write_only"` + TestRequiredWriteOnly types.String `tfsdk:"test_required_write_only"` + } + testProviderMetaType := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_provider_meta_attribute": tftypes.String, @@ -398,6 +423,53 @@ func TestServerApplyResourceChange(t *testing.T) { Private: testEmptyPrivate, }, }, + "create-response-newstate-write-only": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_required_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchemaWriteOnly, + }, + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_required_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchemaWriteOnly, + }, + PriorState: testEmptyState, + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data testSchemaDataWriteOnly + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, nil), + "test_required_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + Private: testEmptyPrivate, + }, + }, "create-response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -1259,6 +1331,59 @@ func TestServerApplyResourceChange(t *testing.T) { Private: testEmptyPrivate, }, }, + "update-response-newstate-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_required_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchemaWriteOnly, + }, + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_required_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchemaWriteOnly, + }, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, "old-optional-value"), + "test_required_write_only": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchemaWriteOnly, + }, + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.Resource{ + CreateMethod: func(_ context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Create") + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Delete") + }, + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data testSchemaDataWriteOnly + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_optional_write_only": tftypes.NewValue(tftypes.String, nil), + "test_required_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + Private: testEmptyPrivate, + }, + }, "update-response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_createresource.go b/internal/fwserver/server_createresource.go index 30c491690..d5a0aef2e 100644 --- a/internal/fwserver/server_createresource.go +++ b/internal/fwserver/server_createresource.go @@ -156,11 +156,21 @@ func (s *Server) CreateResource(ctx context.Context, req *CreateResourceRequest, return } - if semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { - return + if !semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { + logging.FrameworkDebug(ctx, "State updated due to semantic equality") + + resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue } - logging.FrameworkDebug(ctx, "State updated due to semantic equality") + // Set any write-only attributes in the state to null + modifiedState, err := tftypes.Transform(resp.NewState.Raw, NullifyWriteOnlyAttributes(ctx, resp.NewState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying State", + "There was an unexpected error modifying the NewState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } - resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue + resp.NewState.Raw = modifiedState } diff --git a/internal/fwserver/server_createresource_test.go b/internal/fwserver/server_createresource_test.go index d3e83c31b..988a268c9 100644 --- a/internal/fwserver/server_createresource_test.go +++ b/internal/fwserver/server_createresource_test.go @@ -33,6 +33,13 @@ func TestServerCreateResource(t *testing.T) { }, } + testSchemaTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_required": tftypes.String, + "test_write_only": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -76,6 +83,18 @@ func TestServerCreateResource(t *testing.T) { }, } + testSchemaWithWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test_required": schema.StringAttribute{ + Required: true, + }, + "test_write_only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + } + testEmptyState := &tfsdk.State{ Raw: tftypes.NewValue(testSchemaType, nil), Schema: testSchema, @@ -91,6 +110,11 @@ func TestServerCreateResource(t *testing.T) { TestRequired testtypes.StringValueWithSemanticEquals `tfsdk:"test_required"` } + type testSchemaDataWriteOnly struct { + TestRequired types.String `tfsdk:"test_required"` + TestWriteOnly types.String `tfsdk:"test_write_only"` + } + testProviderMetaType := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_provider_meta_attribute": tftypes.String, @@ -506,6 +530,39 @@ func TestServerCreateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-newstate-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.CreateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWithWriteOnly, + }, + ResourceSchema: testSchemaWithWriteOnly, + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data testSchemaDataWriteOnly + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.CreateResourceResponse{ + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWithWriteOnly, + }, + Private: testEmptyPrivate, + }, + }, "response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 7288cc3e7..4abe204cd 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -142,6 +142,18 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta return } + // Set any write-only attributes in the import state to null + modifiedState, err := tftypes.Transform(importResp.State.Raw, NullifyWriteOnlyAttributes(ctx, importResp.State.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Import State", + "There was an unexpected error modifying the Import State. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + importResp.State.Raw = modifiedState + if importResp.State.Raw.Equal(req.EmptyState.Raw) { resp.Diagnostics.AddError( "Missing Resource Import State", diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index bd0275a12..d605ef3d9 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -33,12 +33,26 @@ func TestServerImportResourceState(t *testing.T) { }, } + testTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "write-only": tftypes.String, + "required": tftypes.String, + }, + } + testEmptyStateValue := tftypes.NewValue(testType, map[string]tftypes.Value{ "id": tftypes.NewValue(tftypes.String, nil), "optional": tftypes.NewValue(tftypes.String, nil), "required": tftypes.NewValue(tftypes.String, nil), }) + testEmptyStateValueWriteOnly := tftypes.NewValue(testTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, nil), + "write-only": tftypes.NewValue(tftypes.String, nil), + "required": tftypes.NewValue(tftypes.String, nil), + }) + testUnknownStateValue := tftypes.NewValue(testType, tftypes.UnknownValue) testStateValue := tftypes.NewValue(testType, map[string]tftypes.Value{ @@ -61,11 +75,31 @@ func TestServerImportResourceState(t *testing.T) { }, } + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "write-only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "required": schema.StringAttribute{ + Required: true, + }, + }, + } + testEmptyState := &tfsdk.State{ Raw: testEmptyStateValue, Schema: testSchema, } + testEmptyStateWriteOnly := &tfsdk.State{ + Raw: testEmptyStateValueWriteOnly, + Schema: testSchemaWriteOnly, + } + testUnknownState := &tfsdk.State{ Raw: testUnknownStateValue, Schema: testSchema, @@ -416,6 +450,39 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "response-importedresources-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyStateWriteOnly, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("write-only"), "write-only-val")...) + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: tfsdk.State{ + Raw: tftypes.NewValue(testTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id"), + "write-only": tftypes.NewValue(tftypes.String, nil), + "required": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_moveresourcestate.go b/internal/fwserver/server_moveresourcestate.go index 480e4a956..1a832f3fe 100644 --- a/internal/fwserver/server_moveresourcestate.go +++ b/internal/fwserver/server_moveresourcestate.go @@ -205,6 +205,18 @@ func (s *Server) MoveResourceState(ctx context.Context, req *MoveResourceStateRe return } + // Set any write-only attributes in the move resource state to null + modifiedState, err := tftypes.Transform(moveStateResp.TargetState.Raw, NullifyWriteOnlyAttributes(ctx, moveStateResp.TargetState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Move Resource State", + "There was an unexpected error modifying the Move Resource State. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + moveStateResp.TargetState.Raw = modifiedState + // If the implement has set the state in any way, return the response. if !moveStateResp.TargetState.Raw.Equal(tftypes.NewValue(req.TargetResourceSchema.Type().TerraformType(ctx), nil)) { resp.Diagnostics = moveStateResp.Diagnostics diff --git a/internal/fwserver/server_moveresourcestate_test.go b/internal/fwserver/server_moveresourcestate_test.go index 1a8e0fb77..931b84a76 100644 --- a/internal/fwserver/server_moveresourcestate_test.go +++ b/internal/fwserver/server_moveresourcestate_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestServerMoveResourceState(t *testing.T) { @@ -40,6 +41,22 @@ func TestServerMoveResourceState(t *testing.T) { } schemaType := testSchema.Type().TerraformType(ctx) + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "write_only_attribute": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "required_attribute": schema.StringAttribute{ + Required: true, + }, + }, + } + schemaTypeWriteOnly := testSchemaWriteOnly.Type().TerraformType(ctx) + testCases := map[string]struct { server *fwserver.Server request *fwserver.MoveResourceStateRequest @@ -757,6 +774,44 @@ func TestServerMoveResourceState(t *testing.T) { }, }, }, + "response-TargetState-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.MoveResourceStateRequest{ + SourceRawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "write_only_attribute": nil, + "required_attribute": true, + }), + TargetResource: &testprovider.ResourceWithMoveState{ + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("write_only_attribute"), "movestate-val")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + }, + TargetResourceSchema: testSchemaWriteOnly, + TargetTypeName: "test_resource", + }, + expectedResponse: &fwserver.MoveResourceStateResponse{ + TargetPrivate: privatestate.EmptyData(ctx), + TargetState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "write_only_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchemaWriteOnly, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index d66fc4897..aacd2c998 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fromtftypes" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/logging" @@ -338,6 +339,18 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange "Ensure all resource plan modifiers do not attempt to change resource plan data from being a null value if the request plan is a null value.", ) } + + // Set any write-only attributes in the plan to null + modifiedPlan, err := tftypes.Transform(resp.PlannedState.Raw, NullifyWriteOnlyAttributes(ctx, resp.PlannedState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Planned State", + "There was an unexpected error modifying the PlannedState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + resp.PlannedState.Raw = modifiedPlan } func MarkComputedNilsAsUnknown(ctx context.Context, config tftypes.Value, resourceSchema fwschema.Schema) func(*tftypes.AttributePath, tftypes.Value) (tftypes.Value, error) { @@ -500,6 +513,67 @@ func NormaliseRequiresReplace(ctx context.Context, rs path.Paths) path.Paths { return ret[:j] } +// RequiredWriteOnlyNilsAttributePaths returns a tftypes.Walk() function +// that populates reqWriteOnlyNilsPaths with the paths of Required and WriteOnly +// attributes that have a null value. +func RequiredWriteOnlyNilsAttributePaths(ctx context.Context, schema fwschema.Schema, reqWriteOnlyNilsPaths *path.Paths) func(path *tftypes.AttributePath, value tftypes.Value) (bool, error) { + return func(attrPath *tftypes.AttributePath, value tftypes.Value) (bool, error) { + // we are only modifying attributes, not the entire resource + if len(attrPath.Steps()) < 1 { + return true, nil + } + + ctx = logging.FrameworkWithAttributePath(ctx, attrPath.String()) + + attribute, err := schema.AttributeAtTerraformPath(ctx, attrPath) + + if err != nil { + if errors.Is(err, fwschema.ErrPathInsideAtomicAttribute) { + // atomic attributes can be nested block objects that contain child writeOnly attributes + return true, nil + } + + if errors.Is(err, fwschema.ErrPathIsBlock) { + // blocks do not have the writeOnly field but can contain child writeOnly attributes + return true, nil + } + + if errors.Is(err, fwschema.ErrPathInsideDynamicAttribute) { + return false, nil + } + + logging.FrameworkError(ctx, "couldn't find attribute in resource schema") + return false, fmt.Errorf("couldn't find attribute in resource schema: %w", err) + } + + if attribute.IsWriteOnly() { + if attribute.IsRequired() && value.IsNull() { + fwPath, diags := fromtftypes.AttributePath(ctx, attrPath, schema) + if diags.HasError() { + for _, diagErr := range diags.Errors() { + logging.FrameworkError(ctx, + "Error converting tftypes.AttributePath to path.Path", + map[string]any{ + logging.KeyError: diagErr.Detail(), + }, + ) + } + + return false, fmt.Errorf("couldn't convert tftypes.AttributePath to path.Path") + } + reqWriteOnlyNilsPaths.Append(fwPath) + + // if the value is nil, there is no need to continue walking + return false, nil + } + // check for any writeOnly child attributes + return true, nil + } + + return false, nil + } +} + // planToState returns a *tfsdk.State with a copied value from a tfsdk.Plan. func planToState(plan tfsdk.Plan) *tfsdk.State { return &tfsdk.State{ diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 66e693935..4e22b36c6 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "math/big" + "sort" "testing" "github.com/google/go-cmp/cmp" @@ -363,6 +364,305 @@ func TestMarkComputedNilsAsUnknown(t *testing.T) { } } +func TestRequiredWriteOnlyNilsAttributePath(t *testing.T) { + t.Parallel() + + s := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "string-value": schema.StringAttribute{ + Required: true, + }, + "string-nil-optional-writeonly": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-value-optional-writeonly": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-nil-required-writeonly": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-value-required-writeonly": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "list-value": schema.ListAttribute{ + ElementType: types.StringType, + Required: true, + }, + "list-nil-optional-writeonly": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + WriteOnly: true, + }, + "list-value-optional-writeonly": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + WriteOnly: true, + }, + "list-nil-required-writeonly": schema.ListAttribute{ + ElementType: types.StringType, + Required: true, + WriteOnly: true, + }, + "list-value-required-writeonly": schema.ListAttribute{ + ElementType: types.StringType, + Required: true, + WriteOnly: true, + }, + "dynamic-value": schema.DynamicAttribute{ + Required: true, + }, + "dynamic-nil-optional-writeonly": schema.DynamicAttribute{ + Optional: true, + WriteOnly: true, + }, + "dynamic-value-optional-writeonly": schema.DynamicAttribute{ + Optional: true, + WriteOnly: true, + }, + "dynamic-nil-required-writeonly": schema.DynamicAttribute{ + Required: true, + WriteOnly: true, + }, + "dynamic-value-required-writeonly": schema.DynamicAttribute{ + Required: true, + WriteOnly: true, + }, + // underlying values of dynamic attributes should be left alone + "dynamic-value-with-underlying-list-required-writeonly": schema.DynamicAttribute{ + Required: true, + WriteOnly: true, + }, + "object-nil-required-writeonly": schema.ObjectAttribute{ + AttributeTypes: map[string]attr.Type{ + "string-nil": types.StringType, + "string-set": types.StringType, + }, + Required: true, + WriteOnly: true, + }, + "object-value-required-writeonly": schema.ObjectAttribute{ + AttributeTypes: map[string]attr.Type{ + "string-nil": types.StringType, + "string-set": types.StringType, + }, + Required: true, + WriteOnly: true, + }, + "nested-nil-required-writeonly": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + Required: true, + WriteOnly: true, + }, + "nested-value-required-writeonly": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + Required: true, + WriteOnly: true, + }, + "optional-nested-value-required-writeonly-attributes": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "block-nil-required-writeonly-attributes": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + }, + }, + "block-value-required-writeonly-attributes": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Required: true, + WriteOnly: true, + }, + }, + }, + }, + }, + } + input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ + "string-value": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil-optional-writeonly": tftypes.NewValue(tftypes.String, nil), + "string-value-optional-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil-required-writeonly": tftypes.NewValue(tftypes.String, nil), + "string-value-required-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), + "list-value": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{tftypes.NewValue(tftypes.String, "hello, world")}), + "list-nil-optional-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, nil), + "list-value-optional-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{tftypes.NewValue(tftypes.String, "hello, world")}), + "list-nil-required-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, nil), + "list-value-required-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{ + tftypes.NewValue(tftypes.String, "hello, world"), + tftypes.NewValue(tftypes.String, nil), + }), + "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil-optional-writeonly": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-optional-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil-required-writeonly": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-required-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-value-with-underlying-list-required-writeonly": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Bool, + }, + []tftypes.Value{ + tftypes.NewValue(tftypes.Bool, true), + tftypes.NewValue(tftypes.Bool, nil), + }, + ), + "object-nil-required-writeonly": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "object-value-required-writeonly": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "foo"), + }), + "nested-nil-required-writeonly": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "nested-value-required-writeonly": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "bar"), + }), + "optional-nested-value-required-writeonly-attributes": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "bar"), + }), + "block-nil-required-writeonly-attributes": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, nil), + "block-value-required-writeonly-attributes": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "bar"), + }), + }), + }) + expected := path.Paths{ + path.Root("block-value-required-writeonly-attributes"). + AtSetValue(types.ObjectValueMust( + map[string]attr.Type{ + "string-nil": types.StringType, + "string-set": types.StringType, + }, + map[string]attr.Value{ + "string-nil": types.StringNull(), + "string-set": types.StringValue("bar"), + }, + )). + AtName("string-nil"), + path.Root("dynamic-nil-required-writeonly"), + path.Root("list-nil-required-writeonly"), + path.Root("nested-value-required-writeonly").AtName("string-nil"), + path.Root("object-nil-required-writeonly"), + path.Root("optional-nested-value-required-writeonly-attributes").AtName("string-nil"), + path.Root("string-nil-required-writeonly"), + path.Root("nested-nil-required-writeonly"), + } + + var got path.Paths + err := tftypes.Walk(input, fwserver.RequiredWriteOnlyNilsAttributePaths(context.Background(), s, &got)) + if err != nil { + t.Errorf("Unexpected error: %s", err) + return + } + + sort.Slice(got, func(i, j int) bool { + return got[i].String() < got[j].String() + }) + + sort.Slice(expected, func(i, j int) bool { + return expected[i].String() < expected[j].String() + }) + + if diff := cmp.Diff(got, expected, cmpopts.EquateEmpty()); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } +} + func TestNormaliseRequiresReplace(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 628a2e445..d260e2912 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -6,6 +6,8 @@ package fwserver import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/logging" @@ -157,11 +159,21 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res return } - if semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { - return + if !semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { + logging.FrameworkDebug(ctx, "State updated due to semantic equality") + + resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue } - logging.FrameworkDebug(ctx, "State updated due to semantic equality") + // Set any write-only attributes in the state to null + modifiedState, err := tftypes.Transform(resp.NewState.Raw, NullifyWriteOnlyAttributes(ctx, resp.NewState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying State", + "There was an unexpected error modifying the NewState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } - resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue + resp.NewState.Raw = modifiedState } diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index a9520edd4..f6711551a 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -34,11 +34,23 @@ func TestServerReadResource(t *testing.T) { }, } + testTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_write_only": tftypes.String, + "test_required": tftypes.String, + }, + } + testCurrentStateValue := tftypes.NewValue(testType, map[string]tftypes.Value{ "test_computed": tftypes.NewValue(tftypes.String, nil), "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), }) + testCurrentStateValueWriteOnly := tftypes.NewValue(testTypeWriteOnly, map[string]tftypes.Value{ + "test_write_only": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), + }) + testNewStateValue := tftypes.NewValue(testType, map[string]tftypes.Value{ "test_computed": tftypes.NewValue(tftypes.String, "test-newstate-value"), "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), @@ -55,6 +67,18 @@ func TestServerReadResource(t *testing.T) { }, } + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test_write_only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "test_required": schema.StringAttribute{ + Required: true, + }, + }, + } + testSchemaWithSemanticEquals := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -97,6 +121,11 @@ func TestServerReadResource(t *testing.T) { Schema: testSchema, } + testCurrentStateWriteOnly := &tfsdk.State{ + Raw: testCurrentStateValueWriteOnly, + Schema: testSchemaWriteOnly, + } + testNewState := &tfsdk.State{ Raw: testNewStateValue, Schema: testSchema, @@ -562,6 +591,38 @@ func TestServerReadResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-state-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentStateWriteOnly, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data struct { + TestWriteOnly types.String `tfsdk:"test_write_only"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + data.TestWriteOnly = types.StringValue("test-write-only-value") + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testTypeWriteOnly, map[string]tftypes.Value{ + "test_write_only": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), + }), + Schema: testSchemaWriteOnly, + }, + Private: testEmptyPrivate, + }, + }, "response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_updateresource.go b/internal/fwserver/server_updateresource.go index 9112c35c2..ad1d9f998 100644 --- a/internal/fwserver/server_updateresource.go +++ b/internal/fwserver/server_updateresource.go @@ -169,11 +169,21 @@ func (s *Server) UpdateResource(ctx context.Context, req *UpdateResourceRequest, return } - if semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { - return + if !semanticEqualityResp.NewData.TerraformValue.Equal(resp.NewState.Raw) { + logging.FrameworkDebug(ctx, "State updated due to semantic equality") + + resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue } - logging.FrameworkDebug(ctx, "State updated due to semantic equality") + // Set any write-only attributes in the state to null + modifiedState, err := tftypes.Transform(resp.NewState.Raw, NullifyWriteOnlyAttributes(ctx, resp.NewState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying State", + "There was an unexpected error modifying the NewState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } - resp.NewState.Raw = semanticEqualityResp.NewData.TerraformValue + resp.NewState.Raw = modifiedState } diff --git a/internal/fwserver/server_updateresource_test.go b/internal/fwserver/server_updateresource_test.go index 6ad7a38d6..5c0bb5c7d 100644 --- a/internal/fwserver/server_updateresource_test.go +++ b/internal/fwserver/server_updateresource_test.go @@ -34,6 +34,13 @@ func TestServerUpdateResource(t *testing.T) { }, } + testSchemaTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_required": tftypes.String, + "test_write_only": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -77,6 +84,18 @@ func TestServerUpdateResource(t *testing.T) { }, } + testSchemaWithWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test_required": schema.StringAttribute{ + Required: true, + }, + "test_write_only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -87,6 +106,11 @@ func TestServerUpdateResource(t *testing.T) { TestRequired testtypes.StringValueWithSemanticEquals `tfsdk:"test_required"` } + type testSchemaDataWriteOnly struct { + TestRequired types.String `tfsdk:"test_required"` + TestWriteOnly types.String `tfsdk:"test_write_only"` + } + testProviderMetaType := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_provider_meta_attribute": tftypes.String, @@ -777,6 +801,39 @@ func TestServerUpdateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-newstate-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpdateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWithWriteOnly, + }, + ResourceSchema: testSchemaWithWriteOnly, + Resource: &testprovider.Resource{ + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data testSchemaDataWriteOnly + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + expectedResponse: &fwserver.UpdateResourceResponse{ + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWithWriteOnly, + }, + Private: testEmptyPrivate, + }, + }, "response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_upgraderesourcestate.go b/internal/fwserver/server_upgraderesourcestate.go index b2cc340e5..536c3dfd0 100644 --- a/internal/fwserver/server_upgraderesourcestate.go +++ b/internal/fwserver/server_upgraderesourcestate.go @@ -225,9 +225,19 @@ func (s *Server) UpgradeResourceState(ctx context.Context, req *UpgradeResourceS return } + // Set any write-only attributes in the state to null + modifiedState, err := tftypes.Transform(upgradedStateValue, NullifyWriteOnlyAttributes(ctx, req.ResourceSchema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Upgraded Resource State", + "There was an unexpected error modifying the Upgraded Resource State. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + resp.UpgradedState = &tfsdk.State{ Schema: req.ResourceSchema, - Raw: upgradedStateValue, + Raw: modifiedState, } return @@ -243,5 +253,27 @@ func (s *Server) UpgradeResourceState(ctx context.Context, req *UpgradeResourceS return } + // Set any write-only attributes in the state to null + modifiedState, err := tftypes.Transform(upgradeResourceStateResponse.State.Raw, NullifyWriteOnlyAttributes(ctx, req.ResourceSchema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Upgraded Resource State", + "There was an unexpected error modifying the Upgraded Resource State. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + upgradeResourceStateResponse.State.Raw = modifiedState + + // If the write-only nullification results in a null state, then this is a provider error + if upgradeResourceStateResponse.State.Raw.Type() == nil || upgradeResourceStateResponse.State.Raw.IsNull() { + resp.Diagnostics.AddError( + "Missing Upgraded Resource State", + fmt.Sprintf("After attempting a resource state upgrade to version %d, the provider did not return any state data. ", req.Version)+ + "Preventing the unexpected loss of resource state data. "+ + "This is always an issue with the Terraform Provider and should be reported to the provider developer.", + ) + return + } + resp.UpgradedState = &upgradeResourceStateResponse.State } diff --git a/internal/fwserver/server_upgraderesourcestate_test.go b/internal/fwserver/server_upgraderesourcestate_test.go index b720bed7e..1e733874f 100644 --- a/internal/fwserver/server_upgraderesourcestate_test.go +++ b/internal/fwserver/server_upgraderesourcestate_test.go @@ -42,6 +42,23 @@ func TestServerUpgradeResourceState(t *testing.T) { } schemaType := testSchema.Type().TerraformType(ctx) + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "write_only_attribute": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "required_attribute": schema.StringAttribute{ + Required: true, + }, + }, + Version: 1, // Must be above 0 + } + schemaTypeWriteOnly := testSchemaWriteOnly.Type().TerraformType(ctx) + testCases := map[string]struct { server *fwserver.Server request *fwserver.UpgradeResourceStateRequest @@ -342,6 +359,71 @@ func TestServerUpgradeResourceState(t *testing.T) { }, }, }, + "RawState-DynamicValue-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpgradeResourceStateRequest{ + RawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.ResourceWithUpgradeState{ + Resource: &testprovider.Resource{}, + UpgradeStateMethod: func(ctx context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var rawState struct { + Id string `json:"id"` + RequiredAttribute bool `json:"required_attribute"` + } + + if err := json.Unmarshal(req.RawState.JSON, &rawState); err != nil { + resp.Diagnostics.AddError( + "Unable to Unmarshal Prior State", + err.Error(), + ) + return + } + + dynamicValue, err := tfprotov6.NewDynamicValue( + schemaTypeWriteOnly, + tftypes.NewValue(schemaTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, rawState.Id), + "write_only_attribute": tftypes.NewValue(tftypes.String, "write-only-dynamic-value"), + "required_attribute": tftypes.NewValue(tftypes.String, fmt.Sprintf("%t", rawState.RequiredAttribute)), + }), + ) + + if err != nil { + resp.Diagnostics.AddError( + "Unable to Create Upgraded State", + err.Error(), + ) + return + } + + resp.DynamicValue = &dynamicValue + }, + }, + } + }, + }, + Version: 0, + }, + expectedResponse: &fwserver.UpgradeResourceStateResponse{ + UpgradedState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "write_only_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchemaWriteOnly, + }, + }, + }, "ResourceType-UpgradeState-not-implemented": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -517,6 +599,72 @@ func TestServerUpgradeResourceState(t *testing.T) { }, }, }, + "PriorSchema-and-State-write-only-nullification": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpgradeResourceStateRequest{ + RawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.ResourceWithUpgradeState{ + Resource: &testprovider.Resource{}, + UpgradeStateMethod: func(ctx context.Context) map[int64]resource.StateUpgrader { + return map[int64]resource.StateUpgrader{ + 0: { + PriorSchema: &schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + "required_attribute": schema.BoolAttribute{ + Required: true, + }, + }, + }, + StateUpgrader: func(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { + var priorStateData struct { + Id string `tfsdk:"id"` + RequiredAttribute bool `tfsdk:"required_attribute"` + } + + resp.Diagnostics.Append(req.State.Get(ctx, &priorStateData)...) + + if resp.Diagnostics.HasError() { + return + } + + upgradedStateData := struct { + Id string `tfsdk:"id"` + WriteOnlyAttribute string `tfsdk:"write_only_attribute"` + RequiredAttribute string `tfsdk:"required_attribute"` + }{ + Id: priorStateData.Id, + WriteOnlyAttribute: "write-only-upgraded-state", + RequiredAttribute: fmt.Sprintf("%t", priorStateData.RequiredAttribute), + } + + resp.Diagnostics.Append(resp.State.Set(ctx, upgradedStateData)...) + }, + }, + } + }, + }, + Version: 0, + }, + expectedResponse: &fwserver.UpgradeResourceStateResponse{ + UpgradedState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaTypeWriteOnly, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "write_only_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchemaWriteOnly, + }, + }, + }, "PriorSchema-and-State-json-mismatch": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_validatedatasourceconfig.go b/internal/fwserver/server_validatedatasourceconfig.go index 3379b15ad..33653982d 100644 --- a/internal/fwserver/server_validatedatasourceconfig.go +++ b/internal/fwserver/server_validatedatasourceconfig.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -96,8 +97,17 @@ func (s *Server) ValidateDataSourceConfig(ctx context.Context, req *ValidateData resp.Diagnostics.Append(vdscResp.Diagnostics...) } + schemaCapabilities := validator.ValidateSchemaClientCapabilities{ + // The SchemaValidate function is shared between provider, resource, + // data source and ephemeral resource schemas; however, WriteOnlyAttributesAllowed + // capability is only valid for resource schemas, so this is explicitly set to false + // for all other schema types. + WriteOnlyAttributesAllowed: false, + } + validateSchemaReq := ValidateSchemaRequest{ - Config: *req.Config, + ClientCapabilities: schemaCapabilities, + Config: *req.Config, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. diff --git a/internal/fwserver/server_validateephemeralresourceconfig.go b/internal/fwserver/server_validateephemeralresourceconfig.go index a99a0dbfb..6956af068 100644 --- a/internal/fwserver/server_validateephemeralresourceconfig.go +++ b/internal/fwserver/server_validateephemeralresourceconfig.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -96,8 +97,17 @@ func (s *Server) ValidateEphemeralResourceConfig(ctx context.Context, req *Valid resp.Diagnostics.Append(vdscResp.Diagnostics...) } + schemaCapabilities := validator.ValidateSchemaClientCapabilities{ + // The SchemaValidate function is shared between provider, resource, + // data source and ephemeral resource schemas; however, WriteOnlyAttributesAllowed + // capability is only valid for resource schemas, so this is explicitly set to false + // for all other schema types. + WriteOnlyAttributesAllowed: false, + } + validateSchemaReq := ValidateSchemaRequest{ - Config: *req.Config, + ClientCapabilities: schemaCapabilities, + Config: *req.Config, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. diff --git a/internal/fwserver/server_validateproviderconfig.go b/internal/fwserver/server_validateproviderconfig.go index 588f021c2..0109e6e07 100644 --- a/internal/fwserver/server_validateproviderconfig.go +++ b/internal/fwserver/server_validateproviderconfig.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/logging" "github.com/hashicorp/terraform-plugin-framework/provider" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -77,8 +78,17 @@ func (s *Server) ValidateProviderConfig(ctx context.Context, req *ValidateProvid resp.Diagnostics.Append(vpcRes.Diagnostics...) } + schemaCapabilities := validator.ValidateSchemaClientCapabilities{ + // The SchemaValidate function is shared between provider, resource, + // data source and ephemeral resource schemas; however, WriteOnlyAttributesAllowed + // capability is only valid for resource schemas, so this is explicitly set to false + // for all other schema types. + WriteOnlyAttributesAllowed: false, + } + validateSchemaReq := ValidateSchemaRequest{ - Config: *req.Config, + ClientCapabilities: schemaCapabilities, + Config: *req.Config, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. diff --git a/internal/fwserver/server_validateresourceconfig.go b/internal/fwserver/server_validateresourceconfig.go index 79e8ae9b7..591ce5a2f 100644 --- a/internal/fwserver/server_validateresourceconfig.go +++ b/internal/fwserver/server_validateresourceconfig.go @@ -9,14 +9,16 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/logging" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) // ValidateResourceConfigRequest is the framework server request for the // ValidateResourceConfig RPC. type ValidateResourceConfigRequest struct { - Config *tfsdk.Config - Resource resource.Resource + ClientCapabilities resource.ValidateConfigClientCapabilities + Config *tfsdk.Config + Resource resource.Resource } // ValidateResourceConfigResponse is the framework server response for the @@ -51,7 +53,8 @@ func (s *Server) ValidateResourceConfig(ctx context.Context, req *ValidateResour } vdscReq := resource.ValidateConfigRequest{ - Config: *req.Config, + ClientCapabilities: req.ClientCapabilities, + Config: *req.Config, } if resourceWithConfigValidators, ok := req.Resource.(resource.ResourceWithConfigValidators); ok { @@ -96,8 +99,13 @@ func (s *Server) ValidateResourceConfig(ctx context.Context, req *ValidateResour resp.Diagnostics.Append(vdscResp.Diagnostics...) } + schemaCapabilities := validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: req.ClientCapabilities.WriteOnlyAttributesAllowed, + } + validateSchemaReq := ValidateSchemaRequest{ - Config: *req.Config, + ClientCapabilities: schemaCapabilities, + Config: *req.Config, } // Instantiate a new response for each request to prevent validators // from modifying or removing diagnostics. diff --git a/internal/fwserver/server_validateresourceconfig_test.go b/internal/fwserver/server_validateresourceconfig_test.go index 859612031..489d6a234 100644 --- a/internal/fwserver/server_validateresourceconfig_test.go +++ b/internal/fwserver/server_validateresourceconfig_test.go @@ -8,6 +8,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestServerValidateResourceConfig(t *testing.T) { @@ -47,6 +48,10 @@ func TestServerValidateResourceConfig(t *testing.T) { Schema: testSchema, } + testClientCapabilities := resource.ValidateConfigClientCapabilities{ + WriteOnlyAttributesAllowed: true, + } + testSchemaAttributeValidator := schema.Schema{ Attributes: map[string]schema.Attribute{ "test": schema.StringAttribute{ @@ -69,6 +74,28 @@ func TestServerValidateResourceConfig(t *testing.T) { Schema: testSchemaAttributeValidator, } + testSchemaAttributeValidatorClientCapabilities := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + testvalidator.String{ + ValidateStringMethod: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError("Incorrect req.ClientCapabilities", "expected WriteOnlyAttributesAllowed client capability") + } + }, + }, + }, + }, + }, + } + + testConfigAttributeValidatorClientCapabilities := tfsdk.Config{ + Raw: testValue, + Schema: testSchemaAttributeValidatorClientCapabilities, + } + testSchemaAttributeValidatorError := schema.Schema{ Attributes: map[string]schema.Attribute{ "test": schema.StringAttribute{ @@ -128,6 +155,21 @@ func TestServerValidateResourceConfig(t *testing.T) { }, expectedResponse: &fwserver.ValidateResourceConfigResponse{}, }, + "request-config-AttributeValidator-client-capabilities": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: testClientCapabilities, + Config: &testConfigAttributeValidatorClientCapabilities, + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchemaAttributeValidatorClientCapabilities + }, + }, + }, + expectedResponse: &fwserver.ValidateResourceConfigResponse{}, + }, "request-config-AttributeValidator-diagnostic": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -185,6 +227,34 @@ func TestServerValidateResourceConfig(t *testing.T) { }, expectedResponse: &fwserver.ValidateResourceConfigResponse{}, }, + "request-config-ResourceWithConfigValidators-client-capabilities": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: testClientCapabilities, + Config: &testConfig, + Resource: &testprovider.ResourceWithConfigValidators{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + }, + ConfigValidatorsMethod: func(ctx context.Context) []resource.ConfigValidator { + return []resource.ConfigValidator{ + &testprovider.ResourceConfigValidator{ + ValidateResourceMethod: func(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError("Incorrect req.ClientCapabilities", "expected WriteOnlyAttributesAllowed client capability") + } + }, + }, + } + }, + }, + }, + expectedResponse: &fwserver.ValidateResourceConfigResponse{}, + }, "request-config-ResourceWithConfigValidators-diagnostics": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -259,6 +329,28 @@ func TestServerValidateResourceConfig(t *testing.T) { }, expectedResponse: &fwserver.ValidateResourceConfigResponse{}, }, + "request-config-ResourceWithValidateConfig-client-capabilities": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ValidateResourceConfigRequest{ + ClientCapabilities: testClientCapabilities, + Config: &testConfig, + Resource: &testprovider.ResourceWithValidateConfig{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + }, + ValidateConfigMethod: func(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed { + resp.Diagnostics.AddError("Incorrect req.ClientCapabilities", "expected WriteOnlyAttributesAllowed client capability") + } + }, + }, + }, + expectedResponse: &fwserver.ValidateResourceConfigResponse{}, + }, "request-config-ResourceWithValidateConfig-diagnostic": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/write_only_nullification.go b/internal/fwserver/write_only_nullification.go new file mode 100644 index 000000000..5bfc77609 --- /dev/null +++ b/internal/fwserver/write_only_nullification.go @@ -0,0 +1,77 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + "errors" + "fmt" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// NullifyWriteOnlyAttributes transforms a tftypes.Value, setting all write-only attribute values +// to null according to the given managed resource schema. This function is called in all managed +// resource RPCs before a response is sent to Terraform Core. Terraform Core expects all write-only +// attribute values to be null to prevent data consistency errors. This can technically be done +// manually by the provider developers, but the Framework is handling it instead for convenience. +func NullifyWriteOnlyAttributes(ctx context.Context, resourceSchema fwschema.Schema) func(*tftypes.AttributePath, tftypes.Value) (tftypes.Value, error) { + return func(path *tftypes.AttributePath, val tftypes.Value) (tftypes.Value, error) { + ctx = logging.FrameworkWithAttributePath(ctx, path.String()) + + // we are only modifying attributes, not the entire resource + if len(path.Steps()) < 1 { + return val, nil + } + + attribute, err := resourceSchema.AttributeAtTerraformPath(ctx, path) + + if err != nil { + if errors.Is(err, fwschema.ErrPathInsideAtomicAttribute) { + // ignore attributes/elements inside schema.Attributes, they have no schema of their own + logging.FrameworkTrace(ctx, "attribute is a non-schema attribute, not nullifying") + return val, nil + } + + if errors.Is(err, fwschema.ErrPathIsBlock) { + // ignore blocks, they do not have a writeOnly field + logging.FrameworkTrace(ctx, "attribute is a block, not nullifying") + return val, nil + } + + if errors.Is(err, fwschema.ErrPathInsideDynamicAttribute) { + // ignore attributes/elements inside schema.DynamicAttribute, they have no schema of their own + logging.FrameworkTrace(ctx, "attribute is inside of a dynamic attribute, not nullifying") + return val, nil + } + + logging.FrameworkError(ctx, "couldn't find attribute in resource schema") + + return tftypes.Value{}, fmt.Errorf("couldn't find attribute in resource schema: %w", err) + } + + // Value type from new state to create null with + newValueType := attribute.GetType().TerraformType(ctx) + + // If the attribute is dynamic set the new value type to DynamicPseudoType + // instead of the underlying concrete type + // TODO: verify if this is the correct behavior once Terraform Core implementation is complete + _, isDynamic := attribute.GetType().(basetypes.DynamicTypable) + if isDynamic { + newValueType = tftypes.DynamicPseudoType + } + + if attribute.IsWriteOnly() && !val.IsNull() { + logging.FrameworkDebug(ctx, "Nullifying write-only attribute in the newState") + + return tftypes.NewValue(newValueType, nil), nil + } + + return val, nil + } +} diff --git a/internal/fwserver/write_only_nullification_test.go b/internal/fwserver/write_only_nullification_test.go new file mode 100644 index 000000000..7aba06585 --- /dev/null +++ b/internal/fwserver/write_only_nullification_test.go @@ -0,0 +1,1710 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNullifyWriteOnlyAttributes(t *testing.T) { + t.Parallel() + + s := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "string-value": schema.StringAttribute{ + Required: true, + }, + "string-nil": schema.StringAttribute{ + Optional: true, + }, + "string-nil-write-only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-value-write-only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "dynamic-value": schema.DynamicAttribute{ + Required: true, + }, + "dynamic-nil": schema.DynamicAttribute{ + Optional: true, + }, + "dynamic-underlying-string-nil-computed": schema.DynamicAttribute{ + WriteOnly: true, + }, + "dynamic-nil-write-only": schema.DynamicAttribute{ + Optional: true, + WriteOnly: true, + }, + "dynamic-value-write-only": schema.DynamicAttribute{ + Optional: true, + WriteOnly: true, + }, + "dynamic-value-with-underlying-list-write-only": schema.DynamicAttribute{ + Optional: true, + WriteOnly: true, + }, + "object-nil-write-only": schema.ObjectAttribute{ + AttributeTypes: map[string]attr.Type{ + "string-nil": types.StringType, + "string-set": types.StringType, + }, + Optional: true, + WriteOnly: true, + }, + "object-value-write-only": schema.ObjectAttribute{ + AttributeTypes: map[string]attr.Type{ + "string-nil": types.StringType, + "string-set": types.StringType, + }, + Optional: true, + WriteOnly: true, + }, + "nested-nil-write-only": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + "nested-value-write-only": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "block-nil-write-only": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + "block-value-write-only": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string-nil": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "string-set": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + } + input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ + "string-value": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), + "string-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-underlying-string-nil-computed": tftypes.NewValue(tftypes.String, nil), + "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-value-with-underlying-list-write-only": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Bool, + }, + []tftypes.Value{ + tftypes.NewValue(tftypes.Bool, true), + tftypes.NewValue(tftypes.Bool, false), + }, + ), + "object-nil-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "object-value-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "foo"), + }), + "nested-nil-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "nested-value-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "bar"), + }), + "block-nil-write-only": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, nil), + "block-value-write-only": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, "bar"), + }), + }), + }) + expected := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ + "string-value": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), + "string-value-write-only": tftypes.NewValue(tftypes.String, nil), + "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-underlying-string-nil-computed": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-with-underlying-list-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "object-nil-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "object-value-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "nested-nil-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "nested-value-write-only": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, nil), + "block-nil-write-only": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, nil), + "block-value-write-only": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string-nil": tftypes.String, + "string-set": tftypes.String, + }, + }, map[string]tftypes.Value{ + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-set": tftypes.NewValue(tftypes.String, nil), + }), + }), + }) + + got, err := tftypes.Transform(input, NullifyWriteOnlyAttributes(context.Background(), s)) + if err != nil { + t.Errorf("Unexpected error: %s", err) + return + } + + diff, err := expected.Diff(got) + if err != nil { + t.Errorf("Error diffing values: %s", err) + return + } + for _, valDiff := range diff { + t.Errorf("Unexpected diff at path %v: expected: %v, got: %v", valDiff.Path, valDiff.Value1, valDiff.Value2) + } +} + +func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { + t.Parallel() + nestedObjectType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + }, + } + + s := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "single-nested-attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-single-nested-attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + }, + "nested-single-nested-attribute-wo": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + }, + "single-nested-attribute-wo": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-single-nested-attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + }, + "nested-single-nested-attribute-wo": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + "map-nested-attribute": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-map-nested-attribute": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + "nested-map-nested-attribute-wo": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "map-nested-attribute-wo": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-map-nested-attribute": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-map-nested-attribute-wo": schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + "list-nested-attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-list-nested-attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-list-nested-attribute-wo": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "list-nested-attribute-wo": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-list-nested-attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-list-nested-attribute-wo": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + "set-nested-attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-set-nested-attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "set-nested-attribute-wo": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-set-nested-attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "single-nested-block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-single-nested-attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + }, + "nested-single-nested-attribute-wo": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "nested-single-nested-block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-single-nested-attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + }, + "nested-single-nested-attribute-wo": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + "list-nested-block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-list-nested-attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-list-nested-attribute-wo": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "nested-list-nested-block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-list-nested-attribute": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-list-nested-attribute-wo": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + "set-nested-block": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-set-nested-attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + Blocks: map[string]schema.Block{ + "nested-set-nested-block": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + "nested-set-nested-attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + }, + "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "nested-string": schema.StringAttribute{ + Optional: true, + }, + "nested-string-wo": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + }, + } + input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ + "single-nested-attribute": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "single-nested-attribute-wo": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "map-nested-attribute": tftypes.NewValue(tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, + }, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-map-nested-attribute": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-map-nested-attribute-wo": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "map-nested-attribute-wo": tftypes.NewValue(tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, + }, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-map-nested-attribute": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-map-nested-attribute-wo": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "list-nested-attribute": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "set-nested-attribute": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + "single-nested-block": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + "nested-single-nested-block": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + "nested-single-nested-block": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + "list-nested-block": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-list-nested-block": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + }), + }), + "set-nested-block": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-block": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-block": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-set-nested-block": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), + }), + }), + }), + }), + }), + }), + }) + expected := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ + "single-nested-attribute": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, nil), + }), + "single-nested-attribute-wo": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, nil), + "map-nested-attribute": tftypes.NewValue(tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, + }, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-map-nested-attribute": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, map[string]tftypes.Value{ + "keyA": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-map-nested-attribute-wo": tftypes.NewValue(tftypes.Map{ElementType: nestedObjectType}, nil), + }), + }), + "map-nested-attribute-wo": tftypes.NewValue(tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-map-nested-attribute": tftypes.Map{ElementType: nestedObjectType}, + "nested-map-nested-attribute-wo": tftypes.Map{ElementType: nestedObjectType}, + }, + }, + }, nil), + "list-nested-attribute": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, nil), + }), + }), + "list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, nil), + "set-nested-attribute": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), + }), + }), + "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, nil), + "single-nested-block": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + "nested-single-nested-block": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, nil), + "nested-single-nested-block": tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-single-nested-attribute": nestedObjectType, + "nested-single-nested-attribute-wo": nestedObjectType, + }, + }, + map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-single-nested-attribute": tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + "nested-single-nested-attribute-wo": tftypes.NewValue(nestedObjectType, nil), + }), + }), + "list-nested-block": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-block": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, nil), + "nested-list-nested-block": tftypes.NewValue(tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-list-nested-attribute": tftypes.List{ElementType: nestedObjectType}, + "nested-list-nested-attribute-wo": tftypes.List{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-list-nested-attribute": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-list-nested-attribute-wo": tftypes.NewValue(tftypes.List{ElementType: nestedObjectType}, nil), + }), + }), + }), + }), + "set-nested-block": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-block": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-block": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), + "nested-set-nested-block": tftypes.NewValue(tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, + }, []tftypes.Value{ + tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested-string": tftypes.String, + "nested-string-wo": tftypes.String, + "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, + "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, + }, + }, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ + tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ + "nested-string": tftypes.NewValue(tftypes.String, "foo"), + "nested-string-wo": tftypes.NewValue(tftypes.String, nil), + }), + }), + "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), + }), + }), + }), + }), + }) + got, err := tftypes.Transform(input, NullifyWriteOnlyAttributes(context.Background(), s)) + if err != nil { + t.Errorf("Unexpected error: %s", err) + return + } + + diff, err := expected.Diff(got) + if err != nil { + t.Errorf("Error diffing values: %s", err) + return + } + for _, valDiff := range diff { + t.Errorf("Unexpected diff at path %v: expected: %v, got: %v", valDiff.Path, valDiff.Value1, valDiff.Value2) + } +} diff --git a/internal/proto6server/server_validateresourceconfig_test.go b/internal/proto6server/server_validateresourceconfig_test.go index 11e911745..36ea3f91c 100644 --- a/internal/proto6server/server_validateresourceconfig_test.go +++ b/internal/proto6server/server_validateresourceconfig_test.go @@ -8,12 +8,13 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestServerValidateResourceConfig(t *testing.T) { diff --git a/internal/testing/testschema/attribute.go b/internal/testing/testschema/attribute.go index fccc26b42..979db68eb 100644 --- a/internal/testing/testschema/attribute.go +++ b/internal/testing/testschema/attribute.go @@ -4,9 +4,10 @@ package testschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ fwschema.Attribute = Attribute{} @@ -19,6 +20,7 @@ type Attribute struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -77,3 +79,8 @@ func (a Attribute) IsRequired() bool { func (a Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a Attribute) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithbooldefault.go b/internal/testing/testschema/attributewithbooldefault.go index 5d7b25671..66edc07e5 100644 --- a/internal/testing/testschema/attributewithbooldefault.go +++ b/internal/testing/testschema/attributewithbooldefault.go @@ -22,6 +22,7 @@ type AttributeWithBoolDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Bool } @@ -85,3 +86,8 @@ func (a AttributeWithBoolDefaultValue) IsRequired() bool { func (a AttributeWithBoolDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithboolplanmodifiers.go b/internal/testing/testschema/attributewithboolplanmodifiers.go index d27661894..397f18186 100644 --- a/internal/testing/testschema/attributewithboolplanmodifiers.go +++ b/internal/testing/testschema/attributewithboolplanmodifiers.go @@ -4,12 +4,13 @@ package testschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ fwxschema.AttributeWithBoolPlanModifiers = AttributeWithBoolPlanModifiers{} @@ -22,6 +23,7 @@ type AttributeWithBoolPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Bool } @@ -85,3 +87,8 @@ func (a AttributeWithBoolPlanModifiers) IsRequired() bool { func (a AttributeWithBoolPlanModifiers) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithboolvalidators.go b/internal/testing/testschema/attributewithboolvalidators.go index 5cc0943ca..044c25cf4 100644 --- a/internal/testing/testschema/attributewithboolvalidators.go +++ b/internal/testing/testschema/attributewithboolvalidators.go @@ -4,12 +4,13 @@ package testschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ fwxschema.AttributeWithBoolValidators = AttributeWithBoolValidators{} @@ -22,6 +23,7 @@ type AttributeWithBoolValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Bool } @@ -85,3 +87,8 @@ func (a AttributeWithBoolValidators) IsRequired() bool { func (a AttributeWithBoolValidators) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolValidators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithdynamicdefault.go b/internal/testing/testschema/attributewithdynamicdefault.go index d366beb9a..b562132a8 100644 --- a/internal/testing/testschema/attributewithdynamicdefault.go +++ b/internal/testing/testschema/attributewithdynamicdefault.go @@ -22,6 +22,7 @@ type AttributeWithDynamicDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Dynamic } @@ -85,3 +86,8 @@ func (a AttributeWithDynamicDefaultValue) IsRequired() bool { func (a AttributeWithDynamicDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithdynamicplanmodifiers.go b/internal/testing/testschema/attributewithdynamicplanmodifiers.go index abb7ca6bf..74a80587a 100644 --- a/internal/testing/testschema/attributewithdynamicplanmodifiers.go +++ b/internal/testing/testschema/attributewithdynamicplanmodifiers.go @@ -22,6 +22,7 @@ type AttributeWithDynamicPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Dynamic } @@ -85,3 +86,8 @@ func (a AttributeWithDynamicPlanModifiers) IsSensitive() bool { func (a AttributeWithDynamicPlanModifiers) DynamicPlanModifiers() []planmodifier.Dynamic { return a.PlanModifiers } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithdynamicvalidators.go b/internal/testing/testschema/attributewithdynamicvalidators.go index 1fe086775..e4ef1024e 100644 --- a/internal/testing/testschema/attributewithdynamicvalidators.go +++ b/internal/testing/testschema/attributewithdynamicvalidators.go @@ -4,12 +4,13 @@ package testschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ fwxschema.AttributeWithDynamicValidators = AttributeWithDynamicValidators{} @@ -22,6 +23,7 @@ type AttributeWithDynamicValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Dynamic } @@ -85,3 +87,8 @@ func (a AttributeWithDynamicValidators) IsSensitive() bool { func (a AttributeWithDynamicValidators) DynamicValidators() []validator.Dynamic { return a.Validators } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicValidators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat32default.go b/internal/testing/testschema/attributewithfloat32default.go index e1dabdd87..c3aeb627d 100644 --- a/internal/testing/testschema/attributewithfloat32default.go +++ b/internal/testing/testschema/attributewithfloat32default.go @@ -22,6 +22,7 @@ type AttributeWithFloat32DefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Float32 } @@ -85,3 +86,8 @@ func (a AttributeWithFloat32DefaultValue) IsRequired() bool { func (a AttributeWithFloat32DefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32DefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat32planmodifiers.go b/internal/testing/testschema/attributewithfloat32planmodifiers.go index b3ef10ebc..f93d87229 100644 --- a/internal/testing/testschema/attributewithfloat32planmodifiers.go +++ b/internal/testing/testschema/attributewithfloat32planmodifiers.go @@ -23,6 +23,7 @@ type AttributeWithFloat32PlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Float32 } @@ -86,3 +87,8 @@ func (a AttributeWithFloat32PlanModifiers) IsRequired() bool { func (a AttributeWithFloat32PlanModifiers) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32PlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat32validators.go b/internal/testing/testschema/attributewithfloat32validators.go index d9d38f9ea..7fb02a5ad 100644 --- a/internal/testing/testschema/attributewithfloat32validators.go +++ b/internal/testing/testschema/attributewithfloat32validators.go @@ -23,6 +23,7 @@ type AttributeWithFloat32Validators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Float32 } @@ -86,3 +87,8 @@ func (a AttributeWithFloat32Validators) IsRequired() bool { func (a AttributeWithFloat32Validators) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32Validators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat64default.go b/internal/testing/testschema/attributewithfloat64default.go index 33b717f33..484ec37d9 100644 --- a/internal/testing/testschema/attributewithfloat64default.go +++ b/internal/testing/testschema/attributewithfloat64default.go @@ -22,6 +22,7 @@ type AttributeWithFloat64DefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Float64 } @@ -85,3 +86,8 @@ func (a AttributeWithFloat64DefaultValue) IsRequired() bool { func (a AttributeWithFloat64DefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64DefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat64planmodifiers.go b/internal/testing/testschema/attributewithfloat64planmodifiers.go index 6273cbd5d..ba04291da 100644 --- a/internal/testing/testschema/attributewithfloat64planmodifiers.go +++ b/internal/testing/testschema/attributewithfloat64planmodifiers.go @@ -22,6 +22,7 @@ type AttributeWithFloat64PlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Float64 } @@ -85,3 +86,8 @@ func (a AttributeWithFloat64PlanModifiers) IsRequired() bool { func (a AttributeWithFloat64PlanModifiers) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64PlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithfloat64validators.go b/internal/testing/testschema/attributewithfloat64validators.go index b2b2b5f70..02ef17704 100644 --- a/internal/testing/testschema/attributewithfloat64validators.go +++ b/internal/testing/testschema/attributewithfloat64validators.go @@ -22,6 +22,7 @@ type AttributeWithFloat64Validators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Float64 } @@ -85,3 +86,8 @@ func (a AttributeWithFloat64Validators) IsRequired() bool { func (a AttributeWithFloat64Validators) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64Validators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint32default.go b/internal/testing/testschema/attributewithint32default.go index bbfe22af8..f332bae41 100644 --- a/internal/testing/testschema/attributewithint32default.go +++ b/internal/testing/testschema/attributewithint32default.go @@ -22,6 +22,7 @@ type AttributeWithInt32DefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Int32 } @@ -85,3 +86,8 @@ func (a AttributeWithInt32DefaultValue) IsRequired() bool { func (a AttributeWithInt32DefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32DefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint32planmodifiers.go b/internal/testing/testschema/attributewithint32planmodifiers.go index aff453d9c..7f131df58 100644 --- a/internal/testing/testschema/attributewithint32planmodifiers.go +++ b/internal/testing/testschema/attributewithint32planmodifiers.go @@ -23,6 +23,7 @@ type AttributeWithInt32PlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Int32 } @@ -86,3 +87,8 @@ func (a AttributeWithInt32PlanModifiers) IsRequired() bool { func (a AttributeWithInt32PlanModifiers) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32PlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint32validators.go b/internal/testing/testschema/attributewithint32validators.go index 7c6913bcc..8a4546e9e 100644 --- a/internal/testing/testschema/attributewithint32validators.go +++ b/internal/testing/testschema/attributewithint32validators.go @@ -23,6 +23,7 @@ type AttributeWithInt32Validators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Int32 } @@ -86,3 +87,8 @@ func (a AttributeWithInt32Validators) IsRequired() bool { func (a AttributeWithInt32Validators) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32Validators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint64default.go b/internal/testing/testschema/attributewithint64default.go index ca9e12b96..574a88a58 100644 --- a/internal/testing/testschema/attributewithint64default.go +++ b/internal/testing/testschema/attributewithint64default.go @@ -22,6 +22,7 @@ type AttributeWithInt64DefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Int64 } @@ -85,3 +86,8 @@ func (a AttributeWithInt64DefaultValue) IsRequired() bool { func (a AttributeWithInt64DefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64DefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint64planmodifiers.go b/internal/testing/testschema/attributewithint64planmodifiers.go index 368a865c0..e21a43251 100644 --- a/internal/testing/testschema/attributewithint64planmodifiers.go +++ b/internal/testing/testschema/attributewithint64planmodifiers.go @@ -22,6 +22,7 @@ type AttributeWithInt64PlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Int64 } @@ -85,3 +86,8 @@ func (a AttributeWithInt64PlanModifiers) IsRequired() bool { func (a AttributeWithInt64PlanModifiers) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64PlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithint64validators.go b/internal/testing/testschema/attributewithint64validators.go index 07cf28bd7..c4e23e166 100644 --- a/internal/testing/testschema/attributewithint64validators.go +++ b/internal/testing/testschema/attributewithint64validators.go @@ -22,6 +22,7 @@ type AttributeWithInt64Validators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Int64 } @@ -85,3 +86,8 @@ func (a AttributeWithInt64Validators) IsRequired() bool { func (a AttributeWithInt64Validators) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64Validators) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithlistdefault.go b/internal/testing/testschema/attributewithlistdefault.go index 1f6c65d5a..ff23c5215 100644 --- a/internal/testing/testschema/attributewithlistdefault.go +++ b/internal/testing/testschema/attributewithlistdefault.go @@ -23,6 +23,7 @@ type AttributeWithListDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.List } @@ -88,3 +89,8 @@ func (a AttributeWithListDefaultValue) IsRequired() bool { func (a AttributeWithListDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithListDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithlistplanmodifiers.go b/internal/testing/testschema/attributewithlistplanmodifiers.go index 08ed953fa..fbb50c332 100644 --- a/internal/testing/testschema/attributewithlistplanmodifiers.go +++ b/internal/testing/testschema/attributewithlistplanmodifiers.go @@ -23,6 +23,7 @@ type AttributeWithListPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.List } @@ -84,6 +85,11 @@ func (a AttributeWithListPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithListPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // ListPlanModifiers satisfies the fwxschema.AttributeWithListPlanModifiers interface. func (a AttributeWithListPlanModifiers) ListPlanModifiers() []planmodifier.List { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithlistvalidators.go b/internal/testing/testschema/attributewithlistvalidators.go index bb47ca9d6..fefa2eb02 100644 --- a/internal/testing/testschema/attributewithlistvalidators.go +++ b/internal/testing/testschema/attributewithlistvalidators.go @@ -23,6 +23,7 @@ type AttributeWithListValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.List } @@ -84,6 +85,11 @@ func (a AttributeWithListValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithListValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // ListValidators satisfies the fwxschema.AttributeWithListValidators interface. func (a AttributeWithListValidators) ListValidators() []validator.List { return a.Validators diff --git a/internal/testing/testschema/attributewithmapdefault.go b/internal/testing/testschema/attributewithmapdefault.go index a8bf1910d..2b223cd1d 100644 --- a/internal/testing/testschema/attributewithmapdefault.go +++ b/internal/testing/testschema/attributewithmapdefault.go @@ -23,6 +23,7 @@ type AttributeWithMapDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Map } @@ -88,3 +89,8 @@ func (a AttributeWithMapDefaultValue) IsRequired() bool { func (a AttributeWithMapDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithMapDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithmapplanmodifiers.go b/internal/testing/testschema/attributewithmapplanmodifiers.go index 1d067e1d4..06c7b2fe4 100644 --- a/internal/testing/testschema/attributewithmapplanmodifiers.go +++ b/internal/testing/testschema/attributewithmapplanmodifiers.go @@ -23,6 +23,7 @@ type AttributeWithMapPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Map } @@ -84,6 +85,11 @@ func (a AttributeWithMapPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithMapPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // MapPlanModifiers satisfies the fwxschema.AttributeWithMapPlanModifiers interface. func (a AttributeWithMapPlanModifiers) MapPlanModifiers() []planmodifier.Map { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithmapvalidators.go b/internal/testing/testschema/attributewithmapvalidators.go index 1469ceee2..5e3a9dac8 100644 --- a/internal/testing/testschema/attributewithmapvalidators.go +++ b/internal/testing/testschema/attributewithmapvalidators.go @@ -23,6 +23,7 @@ type AttributeWithMapValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Map } @@ -84,6 +85,11 @@ func (a AttributeWithMapValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithMapValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // MapValidators satisfies the fwxschema.AttributeWithMapValidators interface. func (a AttributeWithMapValidators) MapValidators() []validator.Map { return a.Validators diff --git a/internal/testing/testschema/attributewithnumberdefault.go b/internal/testing/testschema/attributewithnumberdefault.go index 7bca51169..effa79507 100644 --- a/internal/testing/testschema/attributewithnumberdefault.go +++ b/internal/testing/testschema/attributewithnumberdefault.go @@ -22,6 +22,7 @@ type AttributeWithNumberDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Number } @@ -85,3 +86,8 @@ func (a AttributeWithNumberDefaultValue) IsRequired() bool { func (a AttributeWithNumberDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithnumberplanmodifiers.go b/internal/testing/testschema/attributewithnumberplanmodifiers.go index 7073a1e72..cf9299778 100644 --- a/internal/testing/testschema/attributewithnumberplanmodifiers.go +++ b/internal/testing/testschema/attributewithnumberplanmodifiers.go @@ -22,6 +22,7 @@ type AttributeWithNumberPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Number } @@ -81,6 +82,11 @@ func (a AttributeWithNumberPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // NumberPlanModifiers satisfies the fwxschema.AttributeWithNumberPlanModifiers interface. func (a AttributeWithNumberPlanModifiers) NumberPlanModifiers() []planmodifier.Number { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithnumbervalidators.go b/internal/testing/testschema/attributewithnumbervalidators.go index 0e63b7db3..af1869d38 100644 --- a/internal/testing/testschema/attributewithnumbervalidators.go +++ b/internal/testing/testschema/attributewithnumbervalidators.go @@ -22,6 +22,7 @@ type AttributeWithNumberValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Number } @@ -81,6 +82,11 @@ func (a AttributeWithNumberValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // NumberValidators satisfies the fwxschema.AttributeWithNumberValidators interface. func (a AttributeWithNumberValidators) NumberValidators() []validator.Number { return a.Validators diff --git a/internal/testing/testschema/attributewithobjectdefault.go b/internal/testing/testschema/attributewithobjectdefault.go index 54e0e594e..ed25e0a90 100644 --- a/internal/testing/testschema/attributewithobjectdefault.go +++ b/internal/testing/testschema/attributewithobjectdefault.go @@ -23,6 +23,7 @@ type AttributeWithObjectDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Object } @@ -88,3 +89,8 @@ func (a AttributeWithObjectDefaultValue) IsRequired() bool { func (a AttributeWithObjectDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithobjectplanmodifiers.go b/internal/testing/testschema/attributewithobjectplanmodifiers.go index 87f5933e2..e4ec023bd 100644 --- a/internal/testing/testschema/attributewithobjectplanmodifiers.go +++ b/internal/testing/testschema/attributewithobjectplanmodifiers.go @@ -23,6 +23,7 @@ type AttributeWithObjectPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Object } @@ -84,6 +85,11 @@ func (a AttributeWithObjectPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectPlanModifiers satisfies the fwxschema.AttributeWithObjectPlanModifiers interface. func (a AttributeWithObjectPlanModifiers) ObjectPlanModifiers() []planmodifier.Object { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithobjectvalidators.go b/internal/testing/testschema/attributewithobjectvalidators.go index 854be0dbd..534c47cf6 100644 --- a/internal/testing/testschema/attributewithobjectvalidators.go +++ b/internal/testing/testschema/attributewithobjectvalidators.go @@ -23,6 +23,7 @@ type AttributeWithObjectValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Object } @@ -84,6 +85,11 @@ func (a AttributeWithObjectValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectValidators satisfies the fwxschema.AttributeWithObjectValidators interface. func (a AttributeWithObjectValidators) ObjectValidators() []validator.Object { return a.Validators diff --git a/internal/testing/testschema/attributewithsetdefault.go b/internal/testing/testschema/attributewithsetdefault.go index 351ce17b8..f770f956c 100644 --- a/internal/testing/testschema/attributewithsetdefault.go +++ b/internal/testing/testschema/attributewithsetdefault.go @@ -23,6 +23,7 @@ type AttributeWithSetDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.Set } @@ -88,3 +89,8 @@ func (a AttributeWithSetDefaultValue) IsRequired() bool { func (a AttributeWithSetDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithSetDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithsetplanmodifiers.go b/internal/testing/testschema/attributewithsetplanmodifiers.go index 4efb38ac4..a36f5adc4 100644 --- a/internal/testing/testschema/attributewithsetplanmodifiers.go +++ b/internal/testing/testschema/attributewithsetplanmodifiers.go @@ -4,12 +4,13 @@ package testschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ fwxschema.AttributeWithSetPlanModifiers = AttributeWithSetPlanModifiers{} @@ -23,6 +24,7 @@ type AttributeWithSetPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.Set } @@ -84,6 +86,11 @@ func (a AttributeWithSetPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithSetPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // SetPlanModifiers satisfies the fwxschema.AttributeWithSetPlanModifiers interface. func (a AttributeWithSetPlanModifiers) SetPlanModifiers() []planmodifier.Set { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithsetvalidators.go b/internal/testing/testschema/attributewithsetvalidators.go index 217b11b21..32bc5256f 100644 --- a/internal/testing/testschema/attributewithsetvalidators.go +++ b/internal/testing/testschema/attributewithsetvalidators.go @@ -23,6 +23,7 @@ type AttributeWithSetValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.Set } @@ -84,6 +85,11 @@ func (a AttributeWithSetValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithSetValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // SetValidators satisfies the fwxschema.AttributeWithSetValidators interface. func (a AttributeWithSetValidators) SetValidators() []validator.Set { return a.Validators diff --git a/internal/testing/testschema/attributewithstringdefault.go b/internal/testing/testschema/attributewithstringdefault.go index 88e93f004..01e1e6f4e 100644 --- a/internal/testing/testschema/attributewithstringdefault.go +++ b/internal/testing/testschema/attributewithstringdefault.go @@ -22,6 +22,7 @@ type AttributeWithStringDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Default defaults.String } @@ -85,3 +86,8 @@ func (a AttributeWithStringDefaultValue) IsRequired() bool { func (a AttributeWithStringDefaultValue) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithStringDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/attributewithstringplanmodifiers.go b/internal/testing/testschema/attributewithstringplanmodifiers.go index cbf324d66..d1b5af8d5 100644 --- a/internal/testing/testschema/attributewithstringplanmodifiers.go +++ b/internal/testing/testschema/attributewithstringplanmodifiers.go @@ -22,6 +22,7 @@ type AttributeWithStringPlanModifiers struct { Optional bool Required bool Sensitive bool + WriteOnly bool PlanModifiers []planmodifier.String } @@ -81,6 +82,11 @@ func (a AttributeWithStringPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithStringPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // StringPlanModifiers satisfies the fwxschema.AttributeWithStringPlanModifiers interface. func (a AttributeWithStringPlanModifiers) StringPlanModifiers() []planmodifier.String { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithstringvalidators.go b/internal/testing/testschema/attributewithstringvalidators.go index a864dd314..e1ddf7e59 100644 --- a/internal/testing/testschema/attributewithstringvalidators.go +++ b/internal/testing/testschema/attributewithstringvalidators.go @@ -22,6 +22,7 @@ type AttributeWithStringValidators struct { Optional bool Required bool Sensitive bool + WriteOnly bool Validators []validator.String } @@ -81,6 +82,11 @@ func (a AttributeWithStringValidators) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a AttributeWithStringValidators) IsWriteOnly() bool { + return a.WriteOnly +} + // StringValidators satisfies the fwxschema.AttributeWithStringValidators interface. func (a AttributeWithStringValidators) StringValidators() []validator.String { return a.Validators diff --git a/internal/testing/testschema/nested_attribute.go b/internal/testing/testschema/nested_attribute.go index f078f7cb9..f37301359 100644 --- a/internal/testing/testschema/nested_attribute.go +++ b/internal/testing/testschema/nested_attribute.go @@ -27,6 +27,7 @@ type NestedAttribute struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -157,3 +158,8 @@ func (a NestedAttribute) IsRequired() bool { func (a NestedAttribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttribute) IsWriteOnly() bool { + return a.WriteOnly +} diff --git a/internal/testing/testschema/nested_attribute_with_list_default.go b/internal/testing/testschema/nested_attribute_with_list_default.go index 0980629cd..a1b70cd87 100644 --- a/internal/testing/testschema/nested_attribute_with_list_default.go +++ b/internal/testing/testschema/nested_attribute_with_list_default.go @@ -27,6 +27,7 @@ type NestedAttributeWithListDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithListDefaultValue) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} + // ListDefaultValue satisfies the fwschema.AttributeWithListDefaultValue interface. func (a NestedAttributeWithListDefaultValue) ListDefaultValue() defaults.List { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go index e20d89df8..2904c21ee 100644 --- a/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go @@ -27,6 +27,7 @@ type NestedAttributeWithListPlanModifiers struct { PlanModifiers []planmodifier.List Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithListPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // ListPlanModifiers satisfies the fwxschema.AttributeWithListPlanModifiers interface. func (a NestedAttributeWithListPlanModifiers) ListPlanModifiers() []planmodifier.List { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_map_default.go b/internal/testing/testschema/nested_attribute_with_map_default.go index 128ae137c..6eac9d3ff 100644 --- a/internal/testing/testschema/nested_attribute_with_map_default.go +++ b/internal/testing/testschema/nested_attribute_with_map_default.go @@ -27,6 +27,7 @@ type NestedAttributeWithMapDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithMapDefaultValue) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} + // MapDefaultValue satisfies the fwschema.AttributeWithMapDefaultValue interface. func (a NestedAttributeWithMapDefaultValue) MapDefaultValue() defaults.Map { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go index 27ba35b45..35f2a0732 100644 --- a/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go @@ -27,6 +27,7 @@ type NestedAttributeWithMapPlanModifiers struct { PlanModifiers []planmodifier.Map Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithMapPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // MapPlanModifiers satisfies the fwxschema.AttributeWithMapPlanModifiers interface. func (a NestedAttributeWithMapPlanModifiers) MapPlanModifiers() []planmodifier.Map { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_object_default.go b/internal/testing/testschema/nested_attribute_with_object_default.go index 0f6a96489..e8579c2ac 100644 --- a/internal/testing/testschema/nested_attribute_with_object_default.go +++ b/internal/testing/testschema/nested_attribute_with_object_default.go @@ -28,6 +28,7 @@ type NestedAttributeWithObjectDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -105,6 +106,11 @@ func (a NestedAttributeWithObjectDefaultValue) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectDefaultValue satisfies the fwschema.AttributeWithListDefaultValue interface. func (a NestedAttributeWithObjectDefaultValue) ObjectDefaultValue() defaults.Object { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go index 00f303165..2d86af989 100644 --- a/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go @@ -26,6 +26,7 @@ type NestedAttributeWithObjectPlanModifiers struct { PlanModifiers []planmodifier.Object Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -99,6 +100,11 @@ func (a NestedAttributeWithObjectPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectPlanModifiers satisfies the fwxschema.AttributeWithObjectPlanModifiers interface. func (a NestedAttributeWithObjectPlanModifiers) ObjectPlanModifiers() []planmodifier.Object { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_set_default.go b/internal/testing/testschema/nested_attribute_with_set_default.go index a8f39e9a6..3a80c28a7 100644 --- a/internal/testing/testschema/nested_attribute_with_set_default.go +++ b/internal/testing/testschema/nested_attribute_with_set_default.go @@ -27,6 +27,7 @@ type NestedAttributeWithSetDefaultValue struct { Optional bool Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithSetDefaultValue) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetDefaultValue) IsWriteOnly() bool { + return a.WriteOnly +} + // MapDefaultValue satisfies the fwschema.AttributeWithMapDefaultValue interface. func (a NestedAttributeWithSetDefaultValue) SetDefaultValue() defaults.Set { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go index 6c988d86a..1d886e70e 100644 --- a/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go @@ -27,6 +27,7 @@ type NestedAttributeWithSetPlanModifiers struct { PlanModifiers []planmodifier.Set Required bool Sensitive bool + WriteOnly bool Type attr.Type } @@ -102,6 +103,11 @@ func (a NestedAttributeWithSetPlanModifiers) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetPlanModifiers) IsWriteOnly() bool { + return a.WriteOnly +} + // SetPlanModifiers satisfies the fwxschema.AttributeWithSetPlanModifiers interface. func (a NestedAttributeWithSetPlanModifiers) SetPlanModifiers() []planmodifier.Set { return a.PlanModifiers diff --git a/internal/toproto5/getproviderschema_test.go b/internal/toproto5/getproviderschema_test.go index 7a6cd4761..104814f5d 100644 --- a/internal/toproto5/getproviderschema_test.go +++ b/internal/toproto5/getproviderschema_test.go @@ -278,6 +278,38 @@ func TestGetProviderSchemaResponse(t *testing.T) { ResourceSchemas: map[string]*tfprotov5.Schema{}, }, }, + "data-source-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]fwschema.Schema{ + "test_data_source": datasourceschema.Schema{ + Attributes: map[string]datasourceschema.Attribute{ + "test_attribute": datasourceschema.BoolAttribute{ + Computed: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov5.Schema{ + "test_data_source": { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Computed: true, + Name: "test_attribute", + WriteOnly: false, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{}, + Functions: map[string]*tfprotov5.Function{}, + ResourceSchemas: map[string]*tfprotov5.Schema{}, + }, + }, "data-source-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ DataSourceSchemas: map[string]fwschema.Schema{ @@ -1338,6 +1370,38 @@ func TestGetProviderSchemaResponse(t *testing.T) { ResourceSchemas: map[string]*tfprotov5.Schema{}, }, }, + "ephemeral-resource-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + EphemeralResourceSchemas: map[string]fwschema.Schema{ + "test_ephemeral_resource": ephemeralschema.Schema{ + Attributes: map[string]ephemeralschema.Attribute{ + "test_attribute": ephemeralschema.BoolAttribute{ + Computed: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov5.Schema{}, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{ + "test_ephemeral_resource": { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Computed: true, + Name: "test_attribute", + WriteOnly: false, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + Functions: map[string]*tfprotov5.Function{}, + ResourceSchemas: map[string]*tfprotov5.Schema{}, + }, + }, "ephemeral-resource-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ EphemeralResourceSchemas: map[string]fwschema.Schema{ @@ -2461,6 +2525,35 @@ func TestGetProviderSchemaResponse(t *testing.T) { ResourceSchemas: map[string]*tfprotov5.Schema{}, }, }, + "provider-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + Provider: providerschema.Schema{ + Attributes: map[string]providerschema.Attribute{ + "test_attribute": providerschema.BoolAttribute{ + Optional: true, + }, + }, + }, + }, + expected: &tfprotov5.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov5.Schema{}, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{}, + Functions: map[string]*tfprotov5.Function{}, + Provider: &tfprotov5.Schema{ + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "test_attribute", + Optional: true, + WriteOnly: false, + Type: tftypes.Bool, + }, + }, + }, + }, + ResourceSchemas: map[string]*tfprotov5.Schema{}, + }, + }, "provider-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ Provider: providerschema.Schema{ @@ -4042,6 +4135,39 @@ func TestGetProviderSchemaResponse(t *testing.T) { }, }, }, + "resource-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + ResourceSchemas: map[string]fwschema.Schema{ + "test_resource": resourceschema.Schema{ + Attributes: map[string]resourceschema.Attribute{ + "test_attribute": resourceschema.BoolAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov5.Schema{}, + EphemeralResourceSchemas: map[string]*tfprotov5.Schema{}, + Functions: map[string]*tfprotov5.Function{}, + ResourceSchemas: map[string]*tfprotov5.Schema{ + "test_resource": { + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Optional: true, + Name: "test_attribute", + WriteOnly: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + }, "resource-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ ResourceSchemas: map[string]fwschema.Schema{ diff --git a/internal/toproto5/schema_attribute.go b/internal/toproto5/schema_attribute.go index 74d8fa551..c9bc37e3a 100644 --- a/internal/toproto5/schema_attribute.go +++ b/internal/toproto5/schema_attribute.go @@ -6,9 +6,10 @@ package toproto5 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-go/tfprotov5" "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" ) // SchemaAttribute returns the *tfprotov5.SchemaAttribute equivalent of an @@ -34,6 +35,7 @@ func SchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePa Computed: a.IsComputed(), Sensitive: a.IsSensitive(), Type: a.GetType().TerraformType(ctx), + WriteOnly: a.IsWriteOnly(), } if a.GetDeprecationMessage() != "" { diff --git a/internal/toproto6/getproviderschema_test.go b/internal/toproto6/getproviderschema_test.go index 2df173187..161ae67a3 100644 --- a/internal/toproto6/getproviderschema_test.go +++ b/internal/toproto6/getproviderschema_test.go @@ -278,6 +278,38 @@ func TestGetProviderSchemaResponse(t *testing.T) { ResourceSchemas: map[string]*tfprotov6.Schema{}, }, }, + "data-source-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]fwschema.Schema{ + "test_data_source": datasourceschema.Schema{ + Attributes: map[string]datasourceschema.Attribute{ + "test_attribute": datasourceschema.BoolAttribute{ + Computed: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov6.Schema{ + "test_data_source": { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Computed: true, + Name: "test_attribute", + WriteOnly: false, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + Functions: map[string]*tfprotov6.Function{}, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{}, + ResourceSchemas: map[string]*tfprotov6.Schema{}, + }, + }, "data-source-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ DataSourceSchemas: map[string]fwschema.Schema{ @@ -2526,6 +2558,35 @@ func TestGetProviderSchemaResponse(t *testing.T) { ResourceSchemas: map[string]*tfprotov6.Schema{}, }, }, + "provider-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + Provider: providerschema.Schema{ + Attributes: map[string]providerschema.Attribute{ + "test_attribute": providerschema.BoolAttribute{ + Optional: true, + }, + }, + }, + }, + expected: &tfprotov6.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov6.Schema{}, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{}, + Functions: map[string]*tfprotov6.Function{}, + Provider: &tfprotov6.Schema{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "test_attribute", + Optional: true, + WriteOnly: false, + Type: tftypes.Bool, + }, + }, + }, + }, + ResourceSchemas: map[string]*tfprotov6.Schema{}, + }, + }, "provider-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ Provider: providerschema.Schema{ @@ -4183,6 +4244,39 @@ func TestGetProviderSchemaResponse(t *testing.T) { }, }, }, + "resource-attribute-write-only": { + input: &fwserver.GetProviderSchemaResponse{ + ResourceSchemas: map[string]fwschema.Schema{ + "test_resource": resourceschema.Schema{ + Attributes: map[string]resourceschema.Attribute{ + "test_attribute": resourceschema.BoolAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetProviderSchemaResponse{ + DataSourceSchemas: map[string]*tfprotov6.Schema{}, + EphemeralResourceSchemas: map[string]*tfprotov6.Schema{}, + Functions: map[string]*tfprotov6.Function{}, + ResourceSchemas: map[string]*tfprotov6.Schema{ + "test_resource": { + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Optional: true, + Name: "test_attribute", + WriteOnly: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + }, "resource-attribute-type-bool": { input: &fwserver.GetProviderSchemaResponse{ ResourceSchemas: map[string]fwschema.Schema{ diff --git a/internal/toproto6/schema_attribute.go b/internal/toproto6/schema_attribute.go index 492a5a2ab..d020a95d2 100644 --- a/internal/toproto6/schema_attribute.go +++ b/internal/toproto6/schema_attribute.go @@ -7,9 +7,10 @@ import ( "context" "sort" - "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" ) // SchemaAttribute returns the *tfprotov6.SchemaAttribute equivalent of an @@ -27,6 +28,7 @@ func SchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePa Computed: a.IsComputed(), Sensitive: a.IsSensitive(), Type: a.GetType().TerraformType(ctx), + WriteOnly: a.IsWriteOnly(), } if a.GetDeprecationMessage() != "" { diff --git a/provider/metaschema/bool_attribute.go b/provider/metaschema/bool_attribute.go index 6d96a516a..374296341 100644 --- a/provider/metaschema/bool_attribute.go +++ b/provider/metaschema/bool_attribute.go @@ -4,11 +4,12 @@ package metaschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -117,3 +118,9 @@ func (a BoolAttribute) IsRequired() bool { func (a BoolAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a BoolAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/bool_attribute_test.go b/provider/metaschema/bool_attribute_test.go index 617225b54..50b3e06bb 100644 --- a/provider/metaschema/bool_attribute_test.go +++ b/provider/metaschema/bool_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -370,3 +371,31 @@ func TestBoolAttributeIsSensitive(t *testing.T) { }) } } + +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/float64_attribute.go b/provider/metaschema/float64_attribute.go index 8a4478655..ac2b79b0a 100644 --- a/provider/metaschema/float64_attribute.go +++ b/provider/metaschema/float64_attribute.go @@ -4,11 +4,12 @@ package metaschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -120,3 +121,9 @@ func (a Float64Attribute) IsRequired() bool { func (a Float64Attribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Float64Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/float64_attribute_test.go b/provider/metaschema/float64_attribute_test.go index 71cb2f688..f6f5ebd7f 100644 --- a/provider/metaschema/float64_attribute_test.go +++ b/provider/metaschema/float64_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -370,3 +371,31 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/int64_attribute.go b/provider/metaschema/int64_attribute.go index 8751d574e..aeccd7030 100644 --- a/provider/metaschema/int64_attribute.go +++ b/provider/metaschema/int64_attribute.go @@ -4,11 +4,12 @@ package metaschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -120,3 +121,9 @@ func (a Int64Attribute) IsRequired() bool { func (a Int64Attribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Int64Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/int64_attribute_test.go b/provider/metaschema/int64_attribute_test.go index 28efcebd7..2aa4d585e 100644 --- a/provider/metaschema/int64_attribute_test.go +++ b/provider/metaschema/int64_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -370,3 +371,31 @@ func TestInt64AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/list_attribute.go b/provider/metaschema/list_attribute.go index a3ff30e65..187d9c47c 100644 --- a/provider/metaschema/list_attribute.go +++ b/provider/metaschema/list_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -134,6 +135,12 @@ func (a ListAttribute) IsSensitive() bool { return false } +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ListAttribute) IsWriteOnly() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/list_attribute_test.go b/provider/metaschema/list_attribute_test.go index 6c864e8d6..424c1d602 100644 --- a/provider/metaschema/list_attribute_test.go +++ b/provider/metaschema/list_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -379,6 +380,34 @@ func TestListAttributeIsSensitive(t *testing.T) { } } +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/list_nested_attribute.go b/provider/metaschema/list_nested_attribute.go index a6b4f875a..0fa1b8221 100644 --- a/provider/metaschema/list_nested_attribute.go +++ b/provider/metaschema/list_nested_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -159,3 +160,9 @@ func (a ListNestedAttribute) IsRequired() bool { func (a ListNestedAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ListNestedAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/list_nested_attribute_test.go b/provider/metaschema/list_nested_attribute_test.go index dc6188822..c2aeae811 100644 --- a/provider/metaschema/list_nested_attribute_test.go +++ b/provider/metaschema/list_nested_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -546,3 +547,31 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { }) } } + +func TestListNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/map_attribute.go b/provider/metaschema/map_attribute.go index 51ee02edb..9103231ff 100644 --- a/provider/metaschema/map_attribute.go +++ b/provider/metaschema/map_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -137,6 +138,12 @@ func (a MapAttribute) IsSensitive() bool { return false } +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a MapAttribute) IsWriteOnly() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/map_attribute_test.go b/provider/metaschema/map_attribute_test.go index 0956d8cc4..52ee19318 100644 --- a/provider/metaschema/map_attribute_test.go +++ b/provider/metaschema/map_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -379,6 +380,34 @@ func TestMapAttributeIsSensitive(t *testing.T) { } } +func TestMapAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/map_nested_attribute.go b/provider/metaschema/map_nested_attribute.go index 47a3b4348..587c56c0a 100644 --- a/provider/metaschema/map_nested_attribute.go +++ b/provider/metaschema/map_nested_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -159,3 +160,9 @@ func (a MapNestedAttribute) IsRequired() bool { func (a MapNestedAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a MapNestedAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/map_nested_attribute_test.go b/provider/metaschema/map_nested_attribute_test.go index 7c320b1bf..88eb68b26 100644 --- a/provider/metaschema/map_nested_attribute_test.go +++ b/provider/metaschema/map_nested_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -546,3 +547,31 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { }) } } + +func TestMapNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/number_attribute.go b/provider/metaschema/number_attribute.go index 86e45b8ab..511e7000a 100644 --- a/provider/metaschema/number_attribute.go +++ b/provider/metaschema/number_attribute.go @@ -4,11 +4,12 @@ package metaschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -121,3 +122,9 @@ func (a NumberAttribute) IsRequired() bool { func (a NumberAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a NumberAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/number_attribute_test.go b/provider/metaschema/number_attribute_test.go index 819f2d2a1..588dd4e0b 100644 --- a/provider/metaschema/number_attribute_test.go +++ b/provider/metaschema/number_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -370,3 +371,31 @@ func TestNumberAttributeIsSensitive(t *testing.T) { }) } } + +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/object_attribute.go b/provider/metaschema/object_attribute.go index aa4c67be9..aabe40d4c 100644 --- a/provider/metaschema/object_attribute.go +++ b/provider/metaschema/object_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -136,6 +137,12 @@ func (a ObjectAttribute) IsSensitive() bool { return false } +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ObjectAttribute) IsWriteOnly() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/object_attribute_test.go b/provider/metaschema/object_attribute_test.go index e24db5dd1..f439faf0a 100644 --- a/provider/metaschema/object_attribute_test.go +++ b/provider/metaschema/object_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -385,6 +386,34 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } } +func TestObjectAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ObjectAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestObjectAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/set_attribute.go b/provider/metaschema/set_attribute.go index 919713075..f7d3e4112 100644 --- a/provider/metaschema/set_attribute.go +++ b/provider/metaschema/set_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -132,6 +133,12 @@ func (a SetAttribute) IsSensitive() bool { return false } +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SetAttribute) IsWriteOnly() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/set_attribute_test.go b/provider/metaschema/set_attribute_test.go index 8cb5a95d5..620209b6f 100644 --- a/provider/metaschema/set_attribute_test.go +++ b/provider/metaschema/set_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -18,7 +20,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -379,6 +380,34 @@ func TestSetAttributeIsSensitive(t *testing.T) { } } +func TestSetAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/set_nested_attribute.go b/provider/metaschema/set_nested_attribute.go index 233866a00..a3c6fbf9e 100644 --- a/provider/metaschema/set_nested_attribute.go +++ b/provider/metaschema/set_nested_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -154,3 +155,9 @@ func (a SetNestedAttribute) IsRequired() bool { func (a SetNestedAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SetNestedAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/set_nested_attribute_test.go b/provider/metaschema/set_nested_attribute_test.go index 623fa3668..d0d86508c 100644 --- a/provider/metaschema/set_nested_attribute_test.go +++ b/provider/metaschema/set_nested_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -546,3 +547,31 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { }) } } + +func TestSetNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/single_nested_attribute.go b/provider/metaschema/single_nested_attribute.go index 0ed1a22fd..160fb1c80 100644 --- a/provider/metaschema/single_nested_attribute.go +++ b/provider/metaschema/single_nested_attribute.go @@ -6,11 +6,12 @@ package metaschema import ( "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -174,3 +175,9 @@ func (a SingleNestedAttribute) IsRequired() bool { func (a SingleNestedAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SingleNestedAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/single_nested_attribute_test.go b/provider/metaschema/single_nested_attribute_test.go index f7c7cbda5..f04d55634 100644 --- a/provider/metaschema/single_nested_attribute_test.go +++ b/provider/metaschema/single_nested_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -510,3 +511,31 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SingleNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/string_attribute.go b/provider/metaschema/string_attribute.go index 3a14d0721..fe25c5014 100644 --- a/provider/metaschema/string_attribute.go +++ b/provider/metaschema/string_attribute.go @@ -4,11 +4,12 @@ package metaschema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -117,3 +118,9 @@ func (a StringAttribute) IsRequired() bool { func (a StringAttribute) IsSensitive() bool { return false } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider meta schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a StringAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/metaschema/string_attribute_test.go b/provider/metaschema/string_attribute_test.go index 544fa268a..00f34d5b2 100644 --- a/provider/metaschema/string_attribute_test.go +++ b/provider/metaschema/string_attribute_test.go @@ -9,13 +9,14 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -370,3 +371,31 @@ func TestStringAttributeIsSensitive(t *testing.T) { }) } } + +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: metaschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/bool_attribute.go b/provider/schema/bool_attribute.go index c411062e0..0502821ca 100644 --- a/provider/schema/bool_attribute.go +++ b/provider/schema/bool_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -178,3 +179,9 @@ func (a BoolAttribute) IsRequired() bool { func (a BoolAttribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a BoolAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/bool_attribute_test.go b/provider/schema/bool_attribute_test.go index 043f2a896..50e4af6ed 100644 --- a/provider/schema/bool_attribute_test.go +++ b/provider/schema/bool_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -417,3 +418,31 @@ func TestBoolAttributeIsSensitive(t *testing.T) { }) } } + +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/dynamic_attribute.go b/provider/schema/dynamic_attribute.go index c738d348d..4b31279f8 100644 --- a/provider/schema/dynamic_attribute.go +++ b/provider/schema/dynamic_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -175,3 +176,9 @@ func (a DynamicAttribute) IsSensitive() bool { func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { return a.Validators } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a DynamicAttribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/dynamic_attribute_test.go b/provider/schema/dynamic_attribute_test.go index c06eaf811..0779fe460 100644 --- a/provider/schema/dynamic_attribute_test.go +++ b/provider/schema/dynamic_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -384,6 +385,34 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } } +func TestDynamicAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestDynamicAttributeDynamicValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/float32_attribute.go b/provider/schema/float32_attribute.go index a36c5c435..8e62dc96d 100644 --- a/provider/schema/float32_attribute.go +++ b/provider/schema/float32_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -181,3 +182,9 @@ func (a Float32Attribute) IsRequired() bool { func (a Float32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Float32Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/float32_attribute_test.go b/provider/schema/float32_attribute_test.go index e779b4a0a..107216df6 100644 --- a/provider/schema/float32_attribute_test.go +++ b/provider/schema/float32_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -417,3 +418,31 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/float64_attribute.go b/provider/schema/float64_attribute.go index 786965e3a..f0c0cc008 100644 --- a/provider/schema/float64_attribute.go +++ b/provider/schema/float64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -181,3 +182,9 @@ func (a Float64Attribute) IsRequired() bool { func (a Float64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Float64Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/float64_attribute_test.go b/provider/schema/float64_attribute_test.go index 96604d0e7..b08486708 100644 --- a/provider/schema/float64_attribute_test.go +++ b/provider/schema/float64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -417,3 +418,31 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { }) } } + +func TestFloat64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/int32_attribute.go b/provider/schema/int32_attribute.go index 16ff58f0f..1f8c60c39 100644 --- a/provider/schema/int32_attribute.go +++ b/provider/schema/int32_attribute.go @@ -182,3 +182,9 @@ func (a Int32Attribute) IsRequired() bool { func (a Int32Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Int32Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/int32_attribute_test.go b/provider/schema/int32_attribute_test.go index b3ac0687c..a612fb42f 100644 --- a/provider/schema/int32_attribute_test.go +++ b/provider/schema/int32_attribute_test.go @@ -418,3 +418,31 @@ func TestInt32AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/int64_attribute.go b/provider/schema/int64_attribute.go index 3fd9713f1..25243bbf1 100644 --- a/provider/schema/int64_attribute.go +++ b/provider/schema/int64_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -181,3 +182,9 @@ func (a Int64Attribute) IsRequired() bool { func (a Int64Attribute) IsSensitive() bool { return a.Sensitive } + +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a Int64Attribute) IsWriteOnly() bool { + return false +} diff --git a/provider/schema/int64_attribute_test.go b/provider/schema/int64_attribute_test.go index f5e457663..42efcaed4 100644 --- a/provider/schema/int64_attribute_test.go +++ b/provider/schema/int64_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -417,3 +418,31 @@ func TestInt64AttributeIsSensitive(t *testing.T) { }) } } + +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/list_attribute.go b/provider/schema/list_attribute.go index e733b297f..b85848b58 100644 --- a/provider/schema/list_attribute.go +++ b/provider/schema/list_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -195,6 +196,12 @@ func (a ListAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ListAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListAttribute) ListValidators() []validator.List { return a.Validators diff --git a/provider/schema/list_attribute_test.go b/provider/schema/list_attribute_test.go index 3baa9382b..2a529ae13 100644 --- a/provider/schema/list_attribute_test.go +++ b/provider/schema/list_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -392,6 +393,34 @@ func TestListAttributeIsSensitive(t *testing.T) { } } +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/list_nested_attribute.go b/provider/schema/list_nested_attribute.go index 0c82da4ad..700299c05 100644 --- a/provider/schema/list_nested_attribute.go +++ b/provider/schema/list_nested_attribute.go @@ -7,6 +7,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -14,7 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -223,6 +224,12 @@ func (a ListNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ListNestedAttribute) IsWriteOnly() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/provider/schema/list_nested_attribute_test.go b/provider/schema/list_nested_attribute_test.go index 43d76a612..33d6f1723 100644 --- a/provider/schema/list_nested_attribute_test.go +++ b/provider/schema/list_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -563,6 +564,34 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } } +func TestListNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListNestedAttributeListValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/map_attribute.go b/provider/schema/map_attribute.go index 77dc2b61d..82b5a05d7 100644 --- a/provider/schema/map_attribute.go +++ b/provider/schema/map_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -198,6 +199,12 @@ func (a MapAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a MapAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/provider/schema/map_attribute_test.go b/provider/schema/map_attribute_test.go index 0bbef5f25..8c5548e6d 100644 --- a/provider/schema/map_attribute_test.go +++ b/provider/schema/map_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -392,6 +393,34 @@ func TestMapAttributeIsSensitive(t *testing.T) { } } +func TestMapAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapAttributeMapValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/map_nested_attribute.go b/provider/schema/map_nested_attribute.go index 2eed2fa08..14fb4092b 100644 --- a/provider/schema/map_nested_attribute.go +++ b/provider/schema/map_nested_attribute.go @@ -223,6 +223,12 @@ func (a MapNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a MapNestedAttribute) IsWriteOnly() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/provider/schema/map_nested_attribute_test.go b/provider/schema/map_nested_attribute_test.go index 193960869..129321f22 100644 --- a/provider/schema/map_nested_attribute_test.go +++ b/provider/schema/map_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -563,6 +564,34 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } } +func TestMapNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapNestedAttributeMapNestedValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/number_attribute.go b/provider/schema/number_attribute.go index f3e90e2b7..bb6ffc6d6 100644 --- a/provider/schema/number_attribute.go +++ b/provider/schema/number_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -178,6 +179,12 @@ func (a NumberAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a NumberAttribute) IsWriteOnly() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/provider/schema/number_attribute_test.go b/provider/schema/number_attribute_test.go index b957cc73f..306e975e5 100644 --- a/provider/schema/number_attribute_test.go +++ b/provider/schema/number_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -384,6 +385,34 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } } +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestNumberAttributeNumberValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/object_attribute.go b/provider/schema/object_attribute.go index 3041f5a79..c5c81a1ba 100644 --- a/provider/schema/object_attribute.go +++ b/provider/schema/object_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -197,6 +198,12 @@ func (a ObjectAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a ObjectAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/provider/schema/object_attribute_test.go b/provider/schema/object_attribute_test.go index 01f65b864..089d03f71 100644 --- a/provider/schema/object_attribute_test.go +++ b/provider/schema/object_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -398,6 +399,34 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } } +func TestObjectAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestObjectAttributeObjectValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/set_attribute.go b/provider/schema/set_attribute.go index 3297452b7..eaf73344e 100644 --- a/provider/schema/set_attribute.go +++ b/provider/schema/set_attribute.go @@ -6,6 +6,8 @@ package schema import ( "context" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -13,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -193,6 +194,12 @@ func (a SetAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SetAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/provider/schema/set_attribute_test.go b/provider/schema/set_attribute_test.go index 862b2d8dd..42aec99d1 100644 --- a/provider/schema/set_attribute_test.go +++ b/provider/schema/set_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -392,6 +393,34 @@ func TestSetAttributeIsSensitive(t *testing.T) { } } +func TestSetAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/set_nested_attribute.go b/provider/schema/set_nested_attribute.go index 7a2fb6060..a9dad64a7 100644 --- a/provider/schema/set_nested_attribute.go +++ b/provider/schema/set_nested_attribute.go @@ -219,6 +219,12 @@ func (a SetNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SetNestedAttribute) IsWriteOnly() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/provider/schema/set_nested_attribute_test.go b/provider/schema/set_nested_attribute_test.go index 163604a9c..942cd2a25 100644 --- a/provider/schema/set_nested_attribute_test.go +++ b/provider/schema/set_nested_attribute_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -19,7 +21,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -563,6 +564,34 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } } +func TestSetNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetNestedAttributeSetValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/single_nested_attribute.go b/provider/schema/single_nested_attribute.go index 4aa669bf1..aded0988e 100644 --- a/provider/schema/single_nested_attribute.go +++ b/provider/schema/single_nested_attribute.go @@ -233,6 +233,12 @@ func (a SingleNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a SingleNestedAttribute) IsWriteOnly() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/provider/schema/single_nested_attribute_test.go b/provider/schema/single_nested_attribute_test.go index a471cbc20..6d40629a2 100644 --- a/provider/schema/single_nested_attribute_test.go +++ b/provider/schema/single_nested_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -524,6 +525,34 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } } +func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSingleNestedAttributeObjectValidators(t *testing.T) { t.Parallel() diff --git a/provider/schema/string_attribute.go b/provider/schema/string_attribute.go index 7ab7a0c42..eda7a02c4 100644 --- a/provider/schema/string_attribute.go +++ b/provider/schema/string_attribute.go @@ -4,13 +4,14 @@ package schema import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -174,6 +175,12 @@ func (a StringAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns false as write-only attributes are not relevant to provider schemas, +// as these schemas describe data explicitly not saved to any artifact. +func (a StringAttribute) IsWriteOnly() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/provider/schema/string_attribute_test.go b/provider/schema/string_attribute_test.go index 4d24ef73d..58b0cafeb 100644 --- a/provider/schema/string_attribute_test.go +++ b/provider/schema/string_attribute_test.go @@ -9,6 +9,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { @@ -384,6 +385,34 @@ func TestStringAttributeIsSensitive(t *testing.T) { } } +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestStringAttributeStringValidators(t *testing.T) { t.Parallel() diff --git a/resource/schema/bool_attribute.go b/resource/schema/bool_attribute.go index abb0b8708..fa80f565c 100644 --- a/resource/schema/bool_attribute.go +++ b/resource/schema/bool_attribute.go @@ -152,6 +152,16 @@ type BoolAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Bool + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -229,6 +239,11 @@ func (a BoolAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a BoolAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/bool_attribute_test.go b/resource/schema/bool_attribute_test.go index d4b85ef2d..e2d77dd42 100644 --- a/resource/schema/bool_attribute_test.go +++ b/resource/schema/bool_attribute_test.go @@ -512,6 +512,40 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } } +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.BoolAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestBoolAttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamic_attribute.go b/resource/schema/dynamic_attribute.go index 7b97625d9..e06600ab4 100644 --- a/resource/schema/dynamic_attribute.go +++ b/resource/schema/dynamic_attribute.go @@ -153,6 +153,16 @@ type DynamicAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Dynamic + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -215,6 +225,11 @@ func (a DynamicAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a DynamicAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // DynamicDefaultValue returns the Default field value. func (a DynamicAttribute) DynamicDefaultValue() defaults.Dynamic { return a.Default diff --git a/resource/schema/dynamic_attribute_test.go b/resource/schema/dynamic_attribute_test.go index f99dc598c..93c5010f3 100644 --- a/resource/schema/dynamic_attribute_test.go +++ b/resource/schema/dynamic_attribute_test.go @@ -397,6 +397,40 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } } +func TestDynamicAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.DynamicAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestDynamicAttributeDynamicDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32_attribute.go b/resource/schema/float32_attribute.go index 9e8e7a22a..3064b4ed9 100644 --- a/resource/schema/float32_attribute.go +++ b/resource/schema/float32_attribute.go @@ -155,6 +155,16 @@ type Float32Attribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Float32 + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -232,6 +242,11 @@ func (a Float32Attribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a Float32Attribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/float32_attribute_test.go b/resource/schema/float32_attribute_test.go index 75f1a9637..3c90d0081 100644 --- a/resource/schema/float32_attribute_test.go +++ b/resource/schema/float32_attribute_test.go @@ -512,6 +512,40 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { } } +func TestFloat32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.Float32Attribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestFloat32AttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64_attribute.go b/resource/schema/float64_attribute.go index 7d762a4a2..205af3f98 100644 --- a/resource/schema/float64_attribute.go +++ b/resource/schema/float64_attribute.go @@ -155,6 +155,16 @@ type Float64Attribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Float64 + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -232,6 +242,11 @@ func (a Float64Attribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a Float64Attribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/float64_attribute_test.go b/resource/schema/float64_attribute_test.go index a7d155c9f..939080080 100644 --- a/resource/schema/float64_attribute_test.go +++ b/resource/schema/float64_attribute_test.go @@ -512,6 +512,40 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } } +func TestFloat64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.Float64Attribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestFloat64AttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32_attribute.go b/resource/schema/int32_attribute.go index 41b74bcf3..d3f97d60b 100644 --- a/resource/schema/int32_attribute.go +++ b/resource/schema/int32_attribute.go @@ -155,6 +155,16 @@ type Int32Attribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Int32 + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -232,6 +242,11 @@ func (a Int32Attribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a Int32Attribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/int32_attribute_test.go b/resource/schema/int32_attribute_test.go index 152d957f7..48eb3d2f2 100644 --- a/resource/schema/int32_attribute_test.go +++ b/resource/schema/int32_attribute_test.go @@ -512,6 +512,40 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } } +func TestInt32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.Int32Attribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestInt32AttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64_attribute.go b/resource/schema/int64_attribute.go index 65ec795e9..c65eb41fa 100644 --- a/resource/schema/int64_attribute.go +++ b/resource/schema/int64_attribute.go @@ -155,6 +155,16 @@ type Int64Attribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Int64 + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -232,6 +242,11 @@ func (a Int64Attribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a Int64Attribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/int64_attribute_test.go b/resource/schema/int64_attribute_test.go index a961c1fb8..032f523c9 100644 --- a/resource/schema/int64_attribute_test.go +++ b/resource/schema/int64_attribute_test.go @@ -512,6 +512,40 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } } +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.Int64Attribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestInt64AttributeValidateImplementation(t *testing.T) { t.Parallel() diff --git a/resource/schema/list_attribute.go b/resource/schema/list_attribute.go index 1dc0e0e8c..9c1536dbe 100644 --- a/resource/schema/list_attribute.go +++ b/resource/schema/list_attribute.go @@ -168,6 +168,16 @@ type ListAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.List + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a list @@ -232,6 +242,11 @@ func (a ListAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a ListAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ListDefaultValue returns the Default field value. func (a ListAttribute) ListDefaultValue() defaults.List { return a.Default diff --git a/resource/schema/list_attribute_test.go b/resource/schema/list_attribute_test.go index 784ec70d6..07db0c333 100644 --- a/resource/schema/list_attribute_test.go +++ b/resource/schema/list_attribute_test.go @@ -403,6 +403,40 @@ func TestListAttributeIsSensitive(t *testing.T) { } } +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.ListAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListAttributeListDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/list_nested_attribute.go b/resource/schema/list_nested_attribute.go index 95fd2ba01..ee1845bb5 100644 --- a/resource/schema/list_nested_attribute.go +++ b/resource/schema/list_nested_attribute.go @@ -178,6 +178,19 @@ type ListNestedAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.List + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // If WriteOnly is true for a nested attribute, all of its child attributes + // must also set WriteOnly to true and no child attribute can be Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -260,6 +273,11 @@ func (a ListNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a ListNestedAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ListDefaultValue returns the Default field value. func (a ListNestedAttribute) ListDefaultValue() defaults.List { return a.Default @@ -284,6 +302,14 @@ func (a ListNestedAttribute) ValidateImplementation(ctx context.Context, req fws resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } + + if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path)) + } + if a.ListDefaultValue() != nil { if !a.IsComputed() { resp.Diagnostics.Append(nonComputedAttributeWithDefaultDiag(req.Path)) diff --git a/resource/schema/list_nested_attribute_test.go b/resource/schema/list_nested_attribute_test.go index a73c7a843..290fd0dc7 100644 --- a/resource/schema/list_nested_attribute_test.go +++ b/resource/schema/list_nested_attribute_test.go @@ -574,6 +574,40 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } } +func TestListNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestListNestedAttributeListDefaultValue(t *testing.T) { t.Parallel() @@ -909,6 +943,94 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, + "computed-without-child-writeOnly-no-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "computed-with-child-writeOnly-error-diagnostic": { + attribute: schema.ListNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/schema/map_attribute.go b/resource/schema/map_attribute.go index ac50f63f8..f76a295c3 100644 --- a/resource/schema/map_attribute.go +++ b/resource/schema/map_attribute.go @@ -171,6 +171,16 @@ type MapAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Map + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a map @@ -235,6 +245,11 @@ func (a MapAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a MapAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // MapDefaultValue returns the Default field value. func (a MapAttribute) MapDefaultValue() defaults.Map { return a.Default diff --git a/resource/schema/map_attribute_test.go b/resource/schema/map_attribute_test.go index 4f56036d5..9e48ea787 100644 --- a/resource/schema/map_attribute_test.go +++ b/resource/schema/map_attribute_test.go @@ -403,6 +403,40 @@ func TestMapAttributeIsSensitive(t *testing.T) { } } +func TestMapAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.MapAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapAttributeMapDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/map_nested_attribute.go b/resource/schema/map_nested_attribute.go index ab2230b3b..db868f726 100644 --- a/resource/schema/map_nested_attribute.go +++ b/resource/schema/map_nested_attribute.go @@ -178,6 +178,19 @@ type MapNestedAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Map + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // If WriteOnly is true for a nested attribute, all of its child attributes + // must also set WriteOnly to true and no child attribute can be Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -260,6 +273,11 @@ func (a MapNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a MapNestedAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // MapDefaultValue returns the Default field value. func (a MapNestedAttribute) MapDefaultValue() defaults.Map { return a.Default @@ -284,6 +302,14 @@ func (a MapNestedAttribute) ValidateImplementation(ctx context.Context, req fwsc resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } + + if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path)) + } + if a.MapDefaultValue() != nil { if !a.IsComputed() { resp.Diagnostics.Append(nonComputedAttributeWithDefaultDiag(req.Path)) diff --git a/resource/schema/map_nested_attribute_test.go b/resource/schema/map_nested_attribute_test.go index 1fd1a7624..4c7a72489 100644 --- a/resource/schema/map_nested_attribute_test.go +++ b/resource/schema/map_nested_attribute_test.go @@ -574,6 +574,40 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } } +func TestMapNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestMapNestedAttributeMapNestedDefaultValue(t *testing.T) { t.Parallel() @@ -909,6 +943,94 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, + "computed-without-child-writeOnly-no-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "computed-with-child-writeOnly-error-diagnostic": { + attribute: schema.MapNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/schema/number_attribute.go b/resource/schema/number_attribute.go index d2b9c59af..8f367592e 100644 --- a/resource/schema/number_attribute.go +++ b/resource/schema/number_attribute.go @@ -156,6 +156,16 @@ type NumberAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Number + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -218,6 +228,11 @@ func (a NumberAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a NumberAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // NumberDefaultValue returns the Default field value. func (a NumberAttribute) NumberDefaultValue() defaults.Number { return a.Default diff --git a/resource/schema/number_attribute_test.go b/resource/schema/number_attribute_test.go index 2bc8731ca..e9ff78cc8 100644 --- a/resource/schema/number_attribute_test.go +++ b/resource/schema/number_attribute_test.go @@ -398,6 +398,40 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } } +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.NumberAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestNumberAttributeNumberDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/object_attribute.go b/resource/schema/object_attribute.go index 7b9fe6a56..03f35aa00 100644 --- a/resource/schema/object_attribute.go +++ b/resource/schema/object_attribute.go @@ -170,6 +170,16 @@ type ObjectAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Object + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into an @@ -234,6 +244,11 @@ func (a ObjectAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a ObjectAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectDefaultValue returns the Default field value. func (a ObjectAttribute) ObjectDefaultValue() defaults.Object { return a.Default diff --git a/resource/schema/object_attribute_test.go b/resource/schema/object_attribute_test.go index 071e2293c..8f1b6cc3c 100644 --- a/resource/schema/object_attribute_test.go +++ b/resource/schema/object_attribute_test.go @@ -409,6 +409,40 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } } +func TestObjectAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.ObjectAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestObjectAttributeObjectDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/set_attribute.go b/resource/schema/set_attribute.go index 7a54221bb..843f58fb3 100644 --- a/resource/schema/set_attribute.go +++ b/resource/schema/set_attribute.go @@ -166,6 +166,16 @@ type SetAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Set + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a set @@ -230,6 +240,11 @@ func (a SetAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a SetAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // SetDefaultValue returns the Default field value. func (a SetAttribute) SetDefaultValue() defaults.Set { return a.Default diff --git a/resource/schema/set_attribute_test.go b/resource/schema/set_attribute_test.go index 98483f724..9f7c6d0ac 100644 --- a/resource/schema/set_attribute_test.go +++ b/resource/schema/set_attribute_test.go @@ -403,6 +403,40 @@ func TestSetAttributeIsSensitive(t *testing.T) { } } +func TestSetAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.SetAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetAttributeSetDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/set_nested_attribute.go b/resource/schema/set_nested_attribute.go index dee37b591..56c449307 100644 --- a/resource/schema/set_nested_attribute.go +++ b/resource/schema/set_nested_attribute.go @@ -173,6 +173,19 @@ type SetNestedAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Set + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // If WriteOnly is true for a nested attribute, all of its child attributes + // must also set WriteOnly to true and no child attribute can be Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -255,6 +268,11 @@ func (a SetNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a SetNestedAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // SetDefaultValue returns the Default field value. func (a SetNestedAttribute) SetDefaultValue() defaults.Set { return a.Default @@ -279,6 +297,14 @@ func (a SetNestedAttribute) ValidateImplementation(ctx context.Context, req fwsc resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } + + if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path)) + } + if a.SetDefaultValue() != nil { if !a.IsComputed() { resp.Diagnostics.Append(nonComputedAttributeWithDefaultDiag(req.Path)) diff --git a/resource/schema/set_nested_attribute_test.go b/resource/schema/set_nested_attribute_test.go index d148db457..8ea3cb58c 100644 --- a/resource/schema/set_nested_attribute_test.go +++ b/resource/schema/set_nested_attribute_test.go @@ -574,6 +574,40 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } } +func TestSetNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.SetNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSetNestedAttributeSetDefaultValue(t *testing.T) { t.Parallel() @@ -909,6 +943,94 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, + "computed-without-child-writeOnly-no-error-diagnostic": { + attribute: schema.SetNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "computed-with-child-writeOnly-error-diagnostic": { + attribute: schema.SetNestedAttribute{ + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/schema/single_nested_attribute.go b/resource/schema/single_nested_attribute.go index 3dbda942a..12cab9800 100644 --- a/resource/schema/single_nested_attribute.go +++ b/resource/schema/single_nested_attribute.go @@ -167,6 +167,16 @@ type SingleNestedAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Object + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -271,6 +281,11 @@ func (a SingleNestedAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a SingleNestedAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // ObjectDefaultValue returns the Default field value. func (a SingleNestedAttribute) ObjectDefaultValue() defaults.Object { return a.Default @@ -295,6 +310,14 @@ func (a SingleNestedAttribute) ValidateImplementation(ctx context.Context, req f resp.Diagnostics.Append(nonComputedAttributeWithDefaultDiag(req.Path)) } + if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) + } + + if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path)) + } + if a.ObjectDefaultValue() != nil { if !a.IsComputed() { resp.Diagnostics.Append(nonComputedAttributeWithDefaultDiag(req.Path)) diff --git a/resource/schema/single_nested_attribute_test.go b/resource/schema/single_nested_attribute_test.go index 1f20b65b3..f1b666566 100644 --- a/resource/schema/single_nested_attribute_test.go +++ b/resource/schema/single_nested_attribute_test.go @@ -538,6 +538,40 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } } +func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.SingleNestedAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestSingleNestedAttributeObjectDefaultValue(t *testing.T) { t.Parallel() @@ -819,6 +853,86 @@ func TestSingleNestedAttributeValidateImplementation(t *testing.T) { }, }, }, + "writeOnly-with-child-writeOnly-no-error-diagnostic": { + attribute: schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "writeOnly-without-child-writeOnly-error-diagnostic": { + attribute: schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ + "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + ), + }, + }, + }, + "computed-without-child-writeOnly-no-error-diagnostic": { + attribute: schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + Computed: true, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "computed-with-child-writeOnly-error-diagnostic": { + attribute: schema.SingleNestedAttribute{ + Computed: true, + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/schema/string_attribute.go b/resource/schema/string_attribute.go index 7e3b8a1c2..693327016 100644 --- a/resource/schema/string_attribute.go +++ b/resource/schema/string_attribute.go @@ -152,6 +152,16 @@ type StringAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.String + + // WriteOnly indicates that Terraform will not store this attribute value + // in the plan or state artifacts. + // If WriteOnly is true, either Optional or Required must also be true. + // WriteOnly cannot be set with Computed. + // + // This functionality is only supported in Terraform 1.11 and later. + // Practitioners that choose a value for this attribute with older + // versions of Terraform will receive an error. + WriteOnly bool } // ApplyTerraform5AttributePathStep always returns an error as it is not @@ -214,6 +224,11 @@ func (a StringAttribute) IsSensitive() bool { return a.Sensitive } +// IsWriteOnly returns the WriteOnly field value. +func (a StringAttribute) IsWriteOnly() bool { + return a.WriteOnly +} + // StringDefaultValue returns the Default field value. func (a StringAttribute) StringDefaultValue() defaults.String { return a.Default diff --git a/resource/schema/string_attribute_test.go b/resource/schema/string_attribute_test.go index 65bc15b10..20f5ccf36 100644 --- a/resource/schema/string_attribute_test.go +++ b/resource/schema/string_attribute_test.go @@ -397,6 +397,40 @@ func TestStringAttributeIsSensitive(t *testing.T) { } } +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: schema.StringAttribute{}, + expected: false, + }, + "writeOnly": { + attribute: schema.StringAttribute{ + WriteOnly: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + func TestStringAttributeStringDefaultValue(t *testing.T) { t.Parallel() diff --git a/resource/schema/write_only_nested_attribute_validation_test.go b/resource/schema/write_only_nested_attribute_validation_test.go new file mode 100644 index 000000000..acc2ca1de --- /dev/null +++ b/resource/schema/write_only_nested_attribute_validation_test.go @@ -0,0 +1,1281 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package schema_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { + t.Parallel() + tests := map[string]struct { + nestedAttr metaschema.NestedAttribute + expected bool + }{ + "empty nested attribute returns true": { + nestedAttr: schema.ListNestedAttribute{}, + expected: true, + }, + "list nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with one non-writeOnly child attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with one non-writeOnly child nested attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with one non-writeOnly nested child attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with one non-writeOnly child attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with one non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with one non-writeOnly nested child attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with one non-writeOnly child attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with one non-writeOnly child nested attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with one non-writeOnly nested child attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "single nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + expected: true, + }, + "single nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + expected: false, + }, + "single nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + expected: true, + }, + "single nested attribute with one non-writeOnly child attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + expected: false, + }, + "single nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: false, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + expected: false, + }, + "single nested attribute with one non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "single nested attribute with one non-writeOnly nested child attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + expected: false, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + if got := fwschema.ContainsAllWriteOnlyChildAttributes(tt.nestedAttr); got != tt.expected { + t.Errorf("ContainsAllWriteOnlyChildAttributes() = %v, want %v", got, tt.expected) + } + }) + } +} + +func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { + t.Parallel() + tests := map[string]struct { + nestedAttr metaschema.NestedAttribute + expected bool + }{ + "empty nested attribute returns false": { + nestedAttr: schema.ListNestedAttribute{}, + expected: false, + }, + "list nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with one non-writeOnly child attribute returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "list nested attribute with one non-writeOnly child nested attribute returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list nested attribute with one non-writeOnly nested child attribute returns true": { + nestedAttr: schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with one non-writeOnly child attribute returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "set nested attribute with one non-writeOnly child nested attribute returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested attribute with one non-writeOnly nested child attribute returns true": { + nestedAttr: schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with one non-writeOnly child attribute returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "map nested attribute with one non-writeOnly child nested attribute returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "map nested attribute with one non-writeOnly nested child attribute returns true": { + nestedAttr: schema.MapNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "map_nested_attribute": schema.MapNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + + "single nested attribute with writeOnly child attribute returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + expected: true, + }, + "single nested attribute with non-writeOnly child attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + expected: false, + }, + "single nested attribute with multiple writeOnly child attributes returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + expected: true, + }, + "single nested attribute with one non-writeOnly child attribute returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + expected: true, + }, + "single nested attribute with writeOnly child nested attributes returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested attribute with non-writeOnly child nested attribute returns false": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: false, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + expected: false, + }, + "single nested attribute with one non-writeOnly child nested attribute returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: true, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested attribute with one non-writeOnly nested child attribute returns true": { + nestedAttr: schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + WriteOnly: false, + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + expected: true, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + if got := fwschema.ContainsAnyWriteOnlyChildAttributes(tt.nestedAttr); got != tt.expected { + t.Errorf("ContainsAllWriteOnlyChildAttributes() = %v, want %v", got, tt.expected) + } + }) + } +} diff --git a/resource/validate_config.go b/resource/validate_config.go index 40e1213bf..f35c4aa3f 100644 --- a/resource/validate_config.go +++ b/resource/validate_config.go @@ -8,6 +8,17 @@ import ( "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) +// ValidateConfigClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the +// ValidateResourceConfig RPC, such as forward-compatible Terraform behavior +// changes. +type ValidateConfigClientCapabilities struct { + // WriteOnlyAttributesAllowed indicates that the Terraform client + // initiating the request supports write-only attributes for managed + // resources. + WriteOnlyAttributesAllowed bool +} + // ValidateConfigRequest represents a request to validate the // configuration of a resource. An instance of this request struct is // supplied as an argument to the Resource ValidateConfig receiver method @@ -19,6 +30,11 @@ type ValidateConfigRequest struct { // interpolation or other functionality that would prevent Terraform // from knowing the value at request time. Config tfsdk.Config + + // ClientCapabilities defines optionally supported protocol features for + // the ValidateResourceConfig RPC, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateConfigClientCapabilities } // ValidateConfigResponse represents a response to a diff --git a/schema/validator/bool.go b/schema/validator/bool.go index 64115e713..26f6df1a4 100644 --- a/schema/validator/bool.go +++ b/schema/validator/bool.go @@ -35,6 +35,11 @@ type BoolRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Bool + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // BoolResponse is a response to a BoolRequest. diff --git a/schema/validator/client_capabilities.go b/schema/validator/client_capabilities.go new file mode 100644 index 000000000..8f0bbe9a0 --- /dev/null +++ b/schema/validator/client_capabilities.go @@ -0,0 +1,17 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validator + +// ValidateSchemaClientCapabilities allows Terraform to publish information +// regarding optionally supported protocol features for the schema validation +// RPCs, such as forward-compatible Terraform behavior changes. +type ValidateSchemaClientCapabilities struct { + // WriteOnlyAttributesAllowed indicates that the Terraform client + // initiating the request supports write-only attributes for managed + // resources. + // + // This client capability is only populated during managed resource schema + // validation. + WriteOnlyAttributesAllowed bool +} diff --git a/schema/validator/dynamic.go b/schema/validator/dynamic.go index b035175a1..e5d2cb58c 100644 --- a/schema/validator/dynamic.go +++ b/schema/validator/dynamic.go @@ -35,6 +35,11 @@ type DynamicRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Dynamic + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // DynamicResponse is a response to a DynamicRequest. diff --git a/schema/validator/float32.go b/schema/validator/float32.go index c1cd8421d..9f38507b2 100644 --- a/schema/validator/float32.go +++ b/schema/validator/float32.go @@ -35,6 +35,11 @@ type Float32Request struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Float32 + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // Float32Response is a response to a Float32Request. diff --git a/schema/validator/float64.go b/schema/validator/float64.go index f09111ac7..7c788d8f3 100644 --- a/schema/validator/float64.go +++ b/schema/validator/float64.go @@ -35,6 +35,11 @@ type Float64Request struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Float64 + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // Float64Response is a response to a Float64Request. diff --git a/schema/validator/int32.go b/schema/validator/int32.go index 2cbbc3cc2..d13185226 100644 --- a/schema/validator/int32.go +++ b/schema/validator/int32.go @@ -35,6 +35,11 @@ type Int32Request struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Int32 + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // Int32Response is a response to a Int32Request. diff --git a/schema/validator/int64.go b/schema/validator/int64.go index 8e8accdcb..061ab1cf8 100644 --- a/schema/validator/int64.go +++ b/schema/validator/int64.go @@ -35,6 +35,11 @@ type Int64Request struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Int64 + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // Int64Response is a response to a Int64Request. diff --git a/schema/validator/list.go b/schema/validator/list.go index e5b6083d8..e2dc5ecd1 100644 --- a/schema/validator/list.go +++ b/schema/validator/list.go @@ -35,6 +35,11 @@ type ListRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.List + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // ListResponse is a response to a ListRequest. diff --git a/schema/validator/map.go b/schema/validator/map.go index 2a41cc7ac..eda09a239 100644 --- a/schema/validator/map.go +++ b/schema/validator/map.go @@ -35,6 +35,11 @@ type MapRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Map + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // MapResponse is a response to a MapRequest. diff --git a/schema/validator/number.go b/schema/validator/number.go index ef7692c20..2bccf9ac6 100644 --- a/schema/validator/number.go +++ b/schema/validator/number.go @@ -35,6 +35,11 @@ type NumberRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Number + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // NumberResponse is a response to a NumberRequest. diff --git a/schema/validator/object.go b/schema/validator/object.go index 88029e0ad..a2d96a24c 100644 --- a/schema/validator/object.go +++ b/schema/validator/object.go @@ -35,6 +35,11 @@ type ObjectRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Object + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // ObjectResponse is a response to a ObjectRequest. diff --git a/schema/validator/set.go b/schema/validator/set.go index ce7cdea34..f3aaf0b0f 100644 --- a/schema/validator/set.go +++ b/schema/validator/set.go @@ -35,6 +35,11 @@ type SetRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.Set + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // SetResponse is a response to a SetRequest. diff --git a/schema/validator/string.go b/schema/validator/string.go index b453a7bfc..4427e2691 100644 --- a/schema/validator/string.go +++ b/schema/validator/string.go @@ -35,6 +35,11 @@ type StringRequest struct { // ConfigValue contains the value of the attribute for validation from the configuration. ConfigValue types.String + + // ClientCapabilities defines optionally supported protocol features for + // schema validation RPCs, such as forward-compatible Terraform + // behavior changes. + ClientCapabilities ValidateSchemaClientCapabilities } // StringResponse is a response to a StringRequest. diff --git a/website/data/plugin-framework-nav-data.json b/website/data/plugin-framework-nav-data.json index 2297fad3c..b41e7901f 100644 --- a/website/data/plugin-framework-nav-data.json +++ b/website/data/plugin-framework-nav-data.json @@ -98,6 +98,10 @@ { "title": "Timeouts", "path": "resources/timeouts" + }, + { + "title": "Write-only Arguments", + "path": "resources/write-only-arguments" } ] }, diff --git a/website/docs/plugin/framework/handling-data/attributes/bool.mdx b/website/docs/plugin/framework/handling-data/attributes/bool.mdx index 576baff50..b1b7eb2e1 100644 --- a/website/docs/plugin/framework/handling-data/attributes/bool.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/bool.mdx @@ -105,6 +105,18 @@ The [`boolplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugi Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx b/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx index 44701cc24..66da060bf 100644 --- a/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx @@ -134,6 +134,18 @@ The [`dynamicplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-pl Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/float32.mdx b/website/docs/plugin/framework/handling-data/attributes/float32.mdx index 5f710dafe..f0a654718 100644 --- a/website/docs/plugin/framework/handling-data/attributes/float32.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/float32.mdx @@ -111,6 +111,18 @@ The [`float32planmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-pl Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/float64.mdx b/website/docs/plugin/framework/handling-data/attributes/float64.mdx index da31351b6..a43e8b7ab 100644 --- a/website/docs/plugin/framework/handling-data/attributes/float64.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/float64.mdx @@ -111,6 +111,18 @@ The [`float64planmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-pl Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/int32.mdx b/website/docs/plugin/framework/handling-data/attributes/int32.mdx index f0c4ad56c..cd9c443e9 100644 --- a/website/docs/plugin/framework/handling-data/attributes/int32.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/int32.mdx @@ -111,6 +111,18 @@ The [`int32planmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plug Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/int64.mdx b/website/docs/plugin/framework/handling-data/attributes/int64.mdx index 416976e45..6bba8efe4 100644 --- a/website/docs/plugin/framework/handling-data/attributes/int64.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/int64.mdx @@ -111,6 +111,18 @@ The [`int64planmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plug Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx index ea0272133..1a3bd86e6 100644 --- a/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx @@ -159,6 +159,20 @@ The [`listplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugi Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + +If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/list.mdx b/website/docs/plugin/framework/handling-data/attributes/list.mdx index 2104e2475..50ffcf6b5 100644 --- a/website/docs/plugin/framework/handling-data/attributes/list.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/list.mdx @@ -128,6 +128,18 @@ The [`listplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugi Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx index 210f25a9d..53424755a 100644 --- a/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx @@ -159,6 +159,20 @@ The [`mapplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + +If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/map.mdx b/website/docs/plugin/framework/handling-data/attributes/map.mdx index 7eb65aa89..a5b3faea9 100644 --- a/website/docs/plugin/framework/handling-data/attributes/map.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/map.mdx @@ -131,6 +131,18 @@ The [`mapplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/number.mdx b/website/docs/plugin/framework/handling-data/attributes/number.mdx index 4fb48a1ba..270a35070 100644 --- a/website/docs/plugin/framework/handling-data/attributes/number.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/number.mdx @@ -111,6 +111,18 @@ The [`numberplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plu Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/object.mdx b/website/docs/plugin/framework/handling-data/attributes/object.mdx index d7f2c837d..de7b56083 100644 --- a/website/docs/plugin/framework/handling-data/attributes/object.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/object.mdx @@ -165,6 +165,18 @@ Only the object attribute itself, not individual sub-attributes, can define its Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx index 01ce7e51f..9e4f8afe9 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx @@ -159,6 +159,20 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + +If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/set.mdx b/website/docs/plugin/framework/handling-data/attributes/set.mdx index 512489aad..9f3d7dcd3 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set.mdx @@ -128,6 +128,18 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx index 56675912f..24e52a883 100644 --- a/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx @@ -155,6 +155,20 @@ The [`objectplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plu Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + +If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/string.mdx b/website/docs/plugin/framework/handling-data/attributes/string.mdx index d423109dd..6d414dbff 100644 --- a/website/docs/plugin/framework/handling-data/attributes/string.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/string.mdx @@ -112,6 +112,18 @@ The [`stringplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plu Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. +### WriteOnly + + + + Only managed resources implement this concept. + + + +Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). +Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) +and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. + ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/resources/index.mdx b/website/docs/plugin/framework/resources/index.mdx index 61d8b5798..6aa9000c8 100644 --- a/website/docs/plugin/framework/resources/index.mdx +++ b/website/docs/plugin/framework/resources/index.mdx @@ -30,6 +30,7 @@ Further documentation is available for deeper resource concepts: - [Upgrade state](/terraform/plugin/framework/resources/state-upgrade) to transparently update state data outside plans. - [Validate](/terraform/plugin/framework/resources/validate-configuration) practitioner configuration against acceptable values. - [Timeouts](/terraform/plugin/framework/resources/timeouts) in practitioner configuration for use in resource create, read, update and delete functions. +- [Write-only Arguments](/terraform/plugin/framework/resources/write-only-arguments) are special types of attributes that can accept [ephemeral values](/terraform/language/resources/ephemeral) and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. ## Define Resource Type diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx new file mode 100644 index 000000000..b418e34f0 --- /dev/null +++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx @@ -0,0 +1,112 @@ +--- +page_title: 'Plugin Development - Framework: Write-only Arguments' +description: >- + How to implement write-only arguments with the provider development framework. +--- + +# Write-only Arguments + +Write-only arguments are managed resource attributes that are configured by practitioners but are not persisted to the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. +Write-only arguments should be used to handle secret values that do not need to be persisted in Terraform state, such as passwords, API keys, etc. +The provider is expected to be the terminal point for an ephemeral value, +which should either use the value by making the appropriate change to the API or ignore the value. Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) and are not required to be consistent between plan and apply operations. + +## General Concepts + +The following are high level differences between `Required`/`Optional` arguments and write-only arguments: + +- Write-only arguments can accept ephemeral and non-ephemeral values + +- Write-only argument values are only available in the configuration. The prior state, planned state, and final state values for +write-only arguments should always be `null`. + - Provider developers do not need to explicitly set write-only argument values to `null` after using them as the plugin framework will handle the nullification of write-only arguments for all RPCs. + +- Any value that is set for a write-only argument in the state or plan (during [Plan Modification](/terraform/plugin/framework/resources/plan-modification)) by the provider will be reverted to `null` by plugin framework before the RPC response is sent to Terraform. + +- Write-only argument values cannot produce a Terraform plan difference. + - This is because the prior state value for a write-only argument will always be `null` and the planned/final state value will also be `null`, therefore, it cannot produce a diff on its own. + - The one exception to this case is if the write-only argument is added to `requires_replace` during Plan Modification (i.e., using the [`RequiresReplace()`](/terraform/plugin/framework/resources/plan-modification#requiresreplace) plan modifier), in that case, the write-only argument will always cause a diff/trigger a resource recreation + +- Since write-only arguments can accept ephemeral values, write-only argument configuration values are not expected to be consistent between plan and apply. + +## Schema + +An attribute can be made write-only by setting the `WriteOnly` field to `true` in the schema. Attributes with `WriteOnly` set to `true` must also have +one of `Required` or `Optional` set to `true`. If a nested attribute has `WriteOnly` set to `true`, all child attributes must also have `WriteOnly` set to `true`. +`Computed` cannot be set to true for write-only arguments. + +**Schema example:** + +```go +"password_wo": schema.StringAttribute{ + Required: true, + WriteOnly: true, +}, +``` + +## Retrieving Write-only Values + +Write-only argument values should be retrieved from the configuration instead of the plan. Refer to [accessing values](/terraform/plugin/framework/handling-data/accessing-values) for more details on +retrieving values from configuration. + +## PreferWriteOnlyAttribute Validators + +The `PreferWriteOnlyAttribute()` validators available in the [`terraform-plugin-framework-validators` Go module](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators) +can be used when you have a write-only version of an existing attribute, and you want to encourage practitioners to use the write-only version whenever possible. + +The validator returns a warning if the Terraform client is 1.11 or above and the value of the existing attribute is non-null. + +`PreferWriteOnlyAttribute()` is available as a resource-level validator in the [`resourcevalidator` package](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator) or +as an attribute-level validator in the `[type]validator` packages (i.e., [`stringvalidator` package](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator)) + +Usage: + +```go +// Resource-level validator +// Used inside a resource.Resource type ConfigValidators method + _ = []resource.ConfigValidator{ + // Throws a warning diagnostic encouraging practitioners to use + // password_wo if password has a known value + resourcevalidator.PreferWriteOnlyAttribute( + path.MatchRoot("password"), + path.MatchRoot("password_wo"), + ), + } + +// Attribute-level validator +// Used within a Schema method of a Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "password": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + // Throws a warning diagnostic encouraging practitioners to use + // password_wo if password has a known value. + stringvalidator.PreferWriteOnlyAttribute( + path.MatchRoot("password_wo"), + ), + }, + }, + "password_wo": schema.StringAttribute{ + WriteOnly: true, + Optional: true, + }, + }, + } +``` + +```hcl +resource "example_db_instance" "ex" { + username = "foo" + password = "bar" # returns a warning encouraging practitioners to use `password_wo` instead. +} +``` + +## Best Practices + +Since write-only arguments have no prior values, user intent or value changes cannot be determined with a write-only argument alone. To determine when to use/not use a write-only argument value in your provider, we recommend one of the following: + +- Pair write-only arguments with a configuration attribute (required or optional) to “trigger” the use of the write-only argument + - For example, a `password_wo` write-only argument can be paired with a configured `password_wo_version` attribute. When the `password_wo_version` is modified, the provider will send the `password_wo` value to the API. +- Use a keepers attribute (which is used in the [Random Provider](https://registry.terraform.io/providers/hashicorp/random/latest/docs#resource-keepers)) that will take in arbitrary key-pair values. Whenever there is a change to the `keepers` attribute, the provider will use the write-only argument value. +- Use the resource's [private state] to store secure hashes of write-only argument values, the provider will then use the hash to determine if a write-only argument value has changed in later Terraform runs. \ No newline at end of file From bf1d023e7887c45e4fcaa4dd3b80641a0a94602b Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Wed, 12 Feb 2025 10:29:33 -0500 Subject: [PATCH 10/42] internal/fwschemadata: Rewrite `SetValue` semantic equality logic to ignore order (#1064) * quick rewrite of semantic equality for sets * add unit tests for fix * Revert "quick rewrite of semantic equality for sets" This reverts commit 37749fd91ce93bc9490f52ab6a1c6ab96b2cc52e. * fix the semantic equal custom type bump * Revert "Revert "quick rewrite of semantic equality for sets"" This reverts commit b37dde16679cbbcd2e1927dfc31cf999195d6213. * changelog * small change * comment * comment updates * remove unnecessary check on prior element length --- .../unreleased/BUG FIXES-20250117-110109.yaml | 6 + .../value_semantic_equality_set.go | 59 +-- .../value_semantic_equality_set_test.go | 368 ++++++++++++++++++ .../testtypes/stringwithsemanticequals.go | 19 +- 4 files changed, 424 insertions(+), 28 deletions(-) create mode 100644 .changes/unreleased/BUG FIXES-20250117-110109.yaml diff --git a/.changes/unreleased/BUG FIXES-20250117-110109.yaml b/.changes/unreleased/BUG FIXES-20250117-110109.yaml new file mode 100644 index 000000000..8ce6b651d --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20250117-110109.yaml @@ -0,0 +1,6 @@ +kind: BUG FIXES +body: 'internal/fwschemadata: Set semantic equality logic has been adjusted and will + now ignore order of elements during comparison.' +time: 2025-01-17T11:01:09.848503-05:00 +custom: + Issue: "1061" diff --git a/internal/fwschemadata/value_semantic_equality_set.go b/internal/fwschemadata/value_semantic_equality_set.go index 1afe626f4..4ad2ea946 100644 --- a/internal/fwschemadata/value_semantic_equality_set.go +++ b/internal/fwschemadata/value_semantic_equality_set.go @@ -136,33 +136,40 @@ func ValueSemanticEqualitySetElements(ctx context.Context, req ValueSemanticEqua // Ensure new value always contains all of proposed new value newValueElements[idx] = proposedNewValueElement - if idx >= len(priorValueElements) { - continue + // Loop through all prior value elements and see if there are any semantically equal elements + for pIdx, priorValueElement := range priorValueElements { + elementReq := ValueSemanticEqualityRequest{ + Path: req.Path.AtSetValue(proposedNewValueElement), + PriorValue: priorValueElement, + ProposedNewValue: proposedNewValueElement, + } + elementResp := &ValueSemanticEqualityResponse{ + NewValue: elementReq.ProposedNewValue, + } + + ValueSemanticEquality(ctx, elementReq, elementResp) + + resp.Diagnostics.Append(elementResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + + if elementResp.NewValue.Equal(elementReq.ProposedNewValue) { + // This prior value element didn't match, but there could be other elements that do + continue + } + + // Prior state was kept, meaning that we found a semantically equal element + updatedElements = true + + // Remove the semantically equal element from the slice of candidates + priorValueElements = append(priorValueElements[:pIdx], priorValueElements[pIdx+1:]...) + + // Order doesn't matter, so we can just set the prior state element to this index + newValueElements[idx] = elementResp.NewValue + break } - - elementReq := ValueSemanticEqualityRequest{ - Path: req.Path.AtSetValue(proposedNewValueElement), - PriorValue: priorValueElements[idx], - ProposedNewValue: proposedNewValueElement, - } - elementResp := &ValueSemanticEqualityResponse{ - NewValue: elementReq.ProposedNewValue, - } - - ValueSemanticEquality(ctx, elementReq, elementResp) - - resp.Diagnostics.Append(elementResp.Diagnostics...) - - if resp.Diagnostics.HasError() { - return - } - - if elementResp.NewValue.Equal(elementReq.ProposedNewValue) { - continue - } - - updatedElements = true - newValueElements[idx] = elementResp.NewValue } // No changes required if the elements were not updated. diff --git a/internal/fwschemadata/value_semantic_equality_set_test.go b/internal/fwschemadata/value_semantic_equality_set_test.go index 59b34856b..d5fa25c85 100644 --- a/internal/fwschemadata/value_semantic_equality_set_test.go +++ b/internal/fwschemadata/value_semantic_equality_set_test.go @@ -50,6 +50,34 @@ func TestValueSemanticEqualitySet(t *testing.T) { ), }, }, + "SetValue-diff-order": { + request: fwschemadata.ValueSemanticEqualityRequest{ + Path: path.Root("test"), + PriorValue: types.SetValueMust( + types.StringType, + []attr.Value{ + types.StringValue("prior"), + types.StringValue("value"), + }, + ), + ProposedNewValue: types.SetValueMust( + types.StringType, + []attr.Value{ + types.StringValue("value"), + types.StringValue("new"), + }, + ), + }, + expected: &fwschemadata.ValueSemanticEqualityResponse{ + NewValue: types.SetValueMust( + types.StringType, + []attr.Value{ + types.StringValue("value"), + types.StringValue("new"), + }, + ), + }, + }, // ElementType with semantic equality "SetValue-StringValuableWithSemanticEquals-true": { request: fwschemadata.ValueSemanticEqualityRequest{ @@ -91,6 +119,64 @@ func TestValueSemanticEqualitySet(t *testing.T) { ), }, }, + "SetValue-StringValuableWithSemanticEquals-true-diff-order": { + request: fwschemadata.ValueSemanticEqualityRequest{ + Path: path.Root("test"), + PriorValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + }, + }, + }, + ), + ProposedNewValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + }, + }, + }, + ), + }, + expected: &fwschemadata.ValueSemanticEqualityResponse{ + NewValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + }, + }, + }, + ), + }, + }, "SetValue-StringValuableWithSemanticEquals-false": { request: fwschemadata.ValueSemanticEqualityRequest{ Path: path.Root("test"), @@ -131,6 +217,58 @@ func TestValueSemanticEqualitySet(t *testing.T) { ), }, }, + "SetValue-StringValuableWithSemanticEquals-false-diff-order": { + request: fwschemadata.ValueSemanticEqualityRequest{ + Path: path.Root("test"), + PriorValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("prior"), + SemanticEquals: false, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + }, + ), + ProposedNewValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("new"), + SemanticEquals: false, + }, + }, + ), + }, + expected: &fwschemadata.ValueSemanticEqualityResponse{ + NewValue: types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("new"), + SemanticEquals: false, + }, + }, + ), + }, + }, "SetValue-StringValuableWithSemanticEquals-diagnostics": { request: fwschemadata.ValueSemanticEqualityRequest{ Path: path.Root("test"), @@ -267,6 +405,136 @@ func TestValueSemanticEqualitySet(t *testing.T) { ), }, }, + "SetValue-SetValue-StringValuableWithSemanticEquals-true-diff-order": { + request: fwschemadata.ValueSemanticEqualityRequest{ + Path: path.Root("test"), + PriorValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{}, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + }, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-789"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-789"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-012"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-012"), + }, + }, + }, + ), + }, + ), + ProposedNewValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{}, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-012"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-012"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-789"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-789"), + }, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + }, + }, + }, + ), + }, + ), + }, + expected: &fwschemadata.ValueSemanticEqualityResponse{ + NewValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{}, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-123"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-123"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-456"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-456"), + }, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{}, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-789"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-789"), + }, + }, + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("keep-lowercase-012"), + SemanticallyEqualTo: testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("KEEP-LOWERCASE-012"), + }, + }, + }, + ), + }, + ), + }, + }, "SetValue-SetValue-StringValuableWithSemanticEquals-false": { request: fwschemadata.ValueSemanticEqualityRequest{ Path: path.Root("test"), @@ -334,6 +602,106 @@ func TestValueSemanticEqualitySet(t *testing.T) { ), }, }, + "SetValue-SetValue-StringValuableWithSemanticEquals-false-diff-order": { + request: fwschemadata.ValueSemanticEqualityRequest{ + Path: path.Root("test"), + PriorValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("prior"), + SemanticEquals: false, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + }, + ), + }, + ), + ProposedNewValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("new"), + SemanticEquals: false, + }, + }, + ), + }, + ), + }, + expected: &fwschemadata.ValueSemanticEqualityResponse{ + NewValue: types.SetValueMust( + types.SetType{ + ElemType: testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + }, + []attr.Value{ + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("value"), + SemanticEquals: false, + }, + }, + ), + types.SetValueMust( + testtypes.StringTypeWithSemanticEquals{ + SemanticEquals: false, + }, + []attr.Value{ + testtypes.StringValueWithSemanticEquals{ + StringValue: types.StringValue("new"), + SemanticEquals: false, + }, + }, + ), + }, + ), + }, + }, "SetValue-SetValue-StringValuableWithSemanticEquals-NewValueElementsGreaterThanPriorValueElements": { request: fwschemadata.ValueSemanticEqualityRequest{ Path: path.Root("test"), diff --git a/internal/testing/testtypes/stringwithsemanticequals.go b/internal/testing/testtypes/stringwithsemanticequals.go index 4baf39d4c..a98c06a24 100644 --- a/internal/testing/testtypes/stringwithsemanticequals.go +++ b/internal/testing/testtypes/stringwithsemanticequals.go @@ -24,7 +24,12 @@ var ( type StringTypeWithSemanticEquals struct { basetypes.StringType - SemanticEquals bool + // Will always return this boolean for semantic equality + SemanticEquals bool + + // Will only return semantic equality as true if the attr.Value matches this + SemanticallyEqualTo attr.Value + SemanticEqualsDiagnostics diag.Diagnostics } @@ -52,6 +57,7 @@ func (t StringTypeWithSemanticEquals) ValueFromString(ctx context.Context, in ba value := StringValueWithSemanticEquals{ StringValue: in, SemanticEquals: t.SemanticEquals, + SemanticallyEqualTo: t.SemanticallyEqualTo, SemanticEqualsDiagnostics: t.SemanticEqualsDiagnostics, } @@ -83,6 +89,7 @@ func (t StringTypeWithSemanticEquals) ValueFromTerraform(ctx context.Context, in func (t StringTypeWithSemanticEquals) ValueType(ctx context.Context) attr.Value { return StringValueWithSemanticEquals{ SemanticEquals: t.SemanticEquals, + SemanticallyEqualTo: t.SemanticallyEqualTo, SemanticEqualsDiagnostics: t.SemanticEqualsDiagnostics, } } @@ -90,7 +97,12 @@ func (t StringTypeWithSemanticEquals) ValueType(ctx context.Context) attr.Value type StringValueWithSemanticEquals struct { basetypes.StringValue - SemanticEquals bool + // Will always return this boolean for semantic equality + SemanticEquals bool + + // Will only return semantic equality as true if the attr.Value matches this + SemanticallyEqualTo attr.Value + SemanticEqualsDiagnostics diag.Diagnostics } @@ -105,6 +117,9 @@ func (v StringValueWithSemanticEquals) Equal(o attr.Value) bool { } func (v StringValueWithSemanticEquals) StringSemanticEquals(ctx context.Context, otherV basetypes.StringValuable) (bool, diag.Diagnostics) { + if v.SemanticallyEqualTo != nil && !v.SemanticallyEqualTo.IsNull() { + return v.SemanticallyEqualTo.Equal(otherV), v.SemanticEqualsDiagnostics + } return v.SemanticEquals, v.SemanticEqualsDiagnostics } From fbfbe5c00f763b8fef627556c8784bda4e305358 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Wed, 12 Feb 2025 11:03:37 -0500 Subject: [PATCH 11/42] chore: Update golangci-lint linters and apply fixes (#1091) --- .golangci.yml | 8 ++--- datasource/schema/bool_attribute_test.go | 24 ------------- datasource/schema/dynamic_attribute_test.go | 24 ------------- datasource/schema/float32_attribute_test.go | 24 ------------- datasource/schema/float64_attribute_test.go | 24 ------------- datasource/schema/int32_attribute_test.go | 24 ------------- datasource/schema/int64_attribute_test.go | 24 ------------- datasource/schema/list_attribute_test.go | 26 -------------- .../schema/list_nested_attribute_test.go | 28 --------------- datasource/schema/list_nested_block_test.go | 18 ---------- datasource/schema/map_attribute_test.go | 26 -------------- .../schema/map_nested_attribute_test.go | 28 --------------- .../schema/nested_attribute_object_test.go | 10 ------ datasource/schema/nested_block_object_test.go | 12 ------- datasource/schema/number_attribute_test.go | 24 ------------- datasource/schema/object_attribute_test.go | 26 -------------- datasource/schema/schema_test.go | 26 -------------- datasource/schema/set_attribute_test.go | 26 -------------- .../schema/set_nested_attribute_test.go | 28 --------------- datasource/schema/set_nested_block_test.go | 18 ---------- .../schema/single_nested_attribute_test.go | 26 -------------- datasource/schema/single_nested_block_test.go | 16 --------- datasource/schema/string_attribute_test.go | 24 ------------- diag/diagnostics_test.go | 13 ------- diag/error_diagnostic_test.go | 1 - diag/warning_diagnostic_test.go | 1 - ephemeral/schema/bool_attribute_test.go | 24 ------------- ephemeral/schema/dynamic_attribute_test.go | 24 ------------- ephemeral/schema/float32_attribute_test.go | 24 ------------- ephemeral/schema/float64_attribute_test.go | 24 ------------- ephemeral/schema/int32_attribute_test.go | 24 ------------- ephemeral/schema/int64_attribute_test.go | 24 ------------- ephemeral/schema/list_attribute_test.go | 26 -------------- .../schema/list_nested_attribute_test.go | 28 --------------- ephemeral/schema/list_nested_block_test.go | 18 ---------- ephemeral/schema/map_attribute_test.go | 26 -------------- ephemeral/schema/map_nested_attribute_test.go | 28 --------------- .../schema/nested_attribute_object_test.go | 10 ------ ephemeral/schema/nested_block_object_test.go | 12 ------- ephemeral/schema/number_attribute_test.go | 24 ------------- ephemeral/schema/object_attribute_test.go | 26 -------------- ephemeral/schema/schema_test.go | 26 -------------- ephemeral/schema/set_attribute_test.go | 26 -------------- ephemeral/schema/set_nested_attribute_test.go | 28 --------------- ephemeral/schema/set_nested_block_test.go | 18 ---------- .../schema/single_nested_attribute_test.go | 26 -------------- ephemeral/schema/single_nested_block_test.go | 16 --------- ephemeral/schema/string_attribute_test.go | 24 ------------- function/arguments_data_test.go | 6 ---- function/bool_parameter_test.go | 16 --------- function/bool_return_test.go | 2 -- function/definition_test.go | 2 -- function/dynamic_parameter_test.go | 16 --------- function/dynamic_return_test.go | 2 -- function/float32_parameter_test.go | 16 --------- function/float32_return_test.go | 2 -- function/float64_parameter_test.go | 16 --------- function/float64_return_test.go | 2 -- function/func_error_test.go | 4 --- function/int32_parameter_test.go | 16 --------- function/int32_return_test.go | 2 -- function/int64_parameter_test.go | 16 --------- function/int64_return_test.go | 2 -- function/list_parameter_test.go | 16 --------- function/list_return_test.go | 4 --- function/map_parameter_test.go | 16 --------- function/map_return_test.go | 4 --- function/number_parameter_test.go | 16 --------- function/number_return_test.go | 2 -- function/object_parameter_test.go | 16 --------- function/object_return_test.go | 4 --- function/result_data_test.go | 2 -- function/set_parameter_test.go | 16 --------- function/set_return_test.go | 4 --- function/string_parameter_test.go | 16 --------- function/string_return_test.go | 2 -- .../fromproto5/applyresourcechange_test.go | 2 -- internal/fromproto5/arguments_data_test.go | 4 --- internal/fromproto5/callfunction_test.go | 2 -- .../fromproto5/closeephemeralresource_test.go | 2 -- internal/fromproto5/config_test.go | 2 -- internal/fromproto5/configureprovider_test.go | 2 -- internal/fromproto5/dynamic_value_test.go | 2 -- .../fromproto5/ephemeral_result_data_test.go | 2 -- internal/fromproto5/getfunctions_test.go | 2 -- internal/fromproto5/getmetadata_test.go | 2 -- internal/fromproto5/getproviderschema_test.go | 2 -- .../fromproto5/importresourcestate_test.go | 2 -- internal/fromproto5/moveresourcestate_test.go | 2 -- .../fromproto5/openephemeralresource_test.go | 2 -- internal/fromproto5/plan_test.go | 2 -- .../fromproto5/planresourcechange_test.go | 2 -- .../fromproto5/prepareproviderconfig_test.go | 2 -- internal/fromproto5/providermeta_test.go | 2 -- internal/fromproto5/readdatasource_test.go | 2 -- internal/fromproto5/readresource_test.go | 2 -- .../fromproto5/renewephemeralresource_test.go | 2 -- internal/fromproto5/state_test.go | 2 -- .../fromproto5/upgraderesourcestate_test.go | 2 -- .../validatedatasourceconfig_test.go | 2 -- .../validateephemeralresourceconfig_test.go | 2 -- .../validateresourcetypeconfig_test.go | 2 -- .../fromproto6/applyresourcechange_test.go | 2 -- internal/fromproto6/arguments_data_test.go | 4 --- internal/fromproto6/callfunction_test.go | 2 -- .../fromproto6/closeephemeralresource_test.go | 2 -- internal/fromproto6/config_test.go | 2 -- internal/fromproto6/configureprovider_test.go | 2 -- internal/fromproto6/dynamic_value_test.go | 2 -- .../fromproto6/ephemeral_result_data_test.go | 2 -- internal/fromproto6/getfunctions_test.go | 2 -- internal/fromproto6/getmetadata_test.go | 2 -- internal/fromproto6/getproviderschema_test.go | 2 -- .../fromproto6/importresourcestate_test.go | 2 -- internal/fromproto6/moveresourcestate_test.go | 2 -- .../fromproto6/openephemeralresource_test.go | 2 -- internal/fromproto6/plan_test.go | 2 -- .../fromproto6/planresourcechange_test.go | 2 -- internal/fromproto6/providermeta_test.go | 2 -- internal/fromproto6/readdatasource_test.go | 2 -- internal/fromproto6/readresource_test.go | 2 -- .../fromproto6/renewephemeralresource_test.go | 2 -- internal/fromproto6/state_test.go | 2 -- .../fromproto6/upgraderesourcestate_test.go | 2 -- .../validatedatasourceconfig_test.go | 2 -- .../validateephemeralresourceconfig_test.go | 2 -- .../fromproto6/validateproviderconfig_test.go | 2 -- .../fromproto6/validateresourceconfig_test.go | 2 -- .../fromtftypes/attribute_path_step_test.go | 2 -- internal/fromtftypes/attribute_path_test.go | 2 -- internal/fromtftypes/value_test.go | 2 -- .../attribute_name_validation_test.go | 6 ---- internal/fwschema/schema_test.go | 6 ---- internal/fwschemadata/data_default_test.go | 2 -- .../fwschemadata/data_get_at_path_test.go | 1 - internal/fwschemadata/data_get_test.go | 1 - .../data_nullify_collection_blocks_test.go | 2 -- .../fwschemadata/data_path_exists_test.go | 1 - .../fwschemadata/data_path_matches_test.go | 2 -- .../data_reify_null_collection_blocks_test.go | 2 -- .../fwschemadata/data_set_at_path_test.go | 1 - internal/fwschemadata/data_set_test.go | 1 - .../data_valid_path_expression_test.go | 2 -- internal/fwschemadata/data_value_test.go | 1 - internal/fwschemadata/tftypes_value_test.go | 2 -- .../value_semantic_equality_bool_test.go | 2 -- .../value_semantic_equality_dynamic_test.go | 2 -- .../value_semantic_equality_float32_test.go | 2 -- .../value_semantic_equality_float64_test.go | 2 -- .../value_semantic_equality_int32_test.go | 2 -- .../value_semantic_equality_int64_test.go | 2 -- .../value_semantic_equality_list_test.go | 2 -- .../value_semantic_equality_map_test.go | 2 -- .../value_semantic_equality_number_test.go | 2 -- .../value_semantic_equality_object_test.go | 2 -- .../value_semantic_equality_set_test.go | 2 -- .../value_semantic_equality_string_test.go | 2 -- .../value_semantic_equality_test.go | 2 -- .../attribute_plan_modification_test.go | 27 --------------- .../fwserver/attribute_validation_test.go | 27 --------------- .../fwserver/block_plan_modification_test.go | 9 ----- internal/fwserver/block_validation_test.go | 9 ----- .../fwserver/schema_plan_modification_test.go | 1 - .../fwserver/schema_semantic_equality_test.go | 2 -- internal/fwserver/schema_validation_test.go | 1 - .../server_applyresourcechange_test.go | 2 -- internal/fwserver/server_callfunction_test.go | 2 -- .../server_closeephemeralresource_test.go | 2 -- .../fwserver/server_configureprovider_test.go | 2 -- .../fwserver/server_createresource_test.go | 2 -- .../fwserver/server_deleteresource_test.go | 2 -- internal/fwserver/server_getfunctions_test.go | 2 -- internal/fwserver/server_getmetadata_test.go | 2 -- .../fwserver/server_getproviderschema_test.go | 2 -- .../server_importresourcestate_test.go | 2 -- .../fwserver/server_moveresourcestate_test.go | 2 -- .../server_openephemeralresource_test.go | 2 -- .../server_planresourcechange_test.go | 5 --- .../fwserver/server_readdatasource_test.go | 2 -- internal/fwserver/server_readresource_test.go | 2 -- .../server_renewephemeralresource_test.go | 2 -- .../fwserver/server_updateresource_test.go | 2 -- .../server_upgraderesourcestate_test.go | 2 -- .../server_validatedatasourceconfig_test.go | 2 -- ...er_validateephemeralresourceconfig_test.go | 2 -- .../server_validateproviderconfig_test.go | 2 -- .../server_validateresourceconfig_test.go | 2 -- ...missing_underlying_type_validation_test.go | 2 -- .../static_collection_validation_test.go | 1 - internal/privatestate/data_test.go | 14 -------- .../server_applyresourcechange_test.go | 2 -- .../proto5server/server_callfunction_test.go | 2 -- .../server_closeephemeralresource_test.go | 2 -- .../server_configureprovider_test.go | 2 -- .../proto5server/server_getfunctions_test.go | 2 -- .../proto5server/server_getmetadata_test.go | 2 -- .../server_getproviderschema_test.go | 2 -- .../server_importresourcestate_test.go | 2 -- .../server_moveresourcestate_test.go | 2 -- .../server_openephemeralresource_test.go | 2 -- .../server_planresourcechange_test.go | 2 -- .../server_prepareproviderconfig_test.go | 2 -- .../server_readdatasource_test.go | 2 -- .../proto5server/server_readresource_test.go | 2 -- .../server_renewephemeralresource_test.go | 2 -- .../server_upgraderesourcestate_test.go | 2 -- .../server_validatedatasourceconfig_test.go | 2 -- ...er_validateephemeralresourceconfig_test.go | 2 -- .../server_validateresourcetypeconfig_test.go | 2 -- .../server_applyresourcechange_test.go | 2 -- .../proto6server/server_callfunction_test.go | 2 -- .../server_closeephemeralresource_test.go | 2 -- .../server_configureprovider_test.go | 2 -- .../proto6server/server_getfunctions_test.go | 2 -- .../proto6server/server_getmetadata_test.go | 2 -- .../server_getproviderschema_test.go | 2 -- .../server_importresourcestate_test.go | 2 -- .../server_moveresourcestate_test.go | 2 -- .../server_openephemeralresource_test.go | 2 -- .../server_planresourcechange_test.go | 2 -- .../server_readdatasource_test.go | 2 -- .../proto6server/server_readresource_test.go | 2 -- .../server_renewephemeralresource_test.go | 2 -- .../server_upgraderesourcestate_test.go | 2 -- .../server_validatedataresourceconfig_test.go | 2 -- ...er_validateephemeralresourceconfig_test.go | 2 -- .../server_validateproviderconfig_test.go | 2 -- .../server_validateresourceconfig_test.go | 2 -- internal/reflect/build_value_test.go | 1 - internal/reflect/helpers_test.go | 3 -- internal/reflect/interfaces_test.go | 8 ----- internal/reflect/into_test.go | 2 -- internal/reflect/map_test.go | 1 - internal/reflect/number_test.go | 5 --- internal/reflect/outof_test.go | 2 -- internal/reflect/pointer_test.go | 1 - internal/reflect/primitive_test.go | 2 -- internal/reflect/slice_test.go | 1 - internal/reflect/struct_test.go | 4 --- internal/toproto5/applyresourcechange_test.go | 2 -- internal/toproto5/block_test.go | 1 - internal/toproto5/callfunction_test.go | 2 -- .../toproto5/closeephemeralresource_test.go | 2 -- internal/toproto5/config_test.go | 2 -- internal/toproto5/configureprovider_test.go | 2 -- internal/toproto5/datasourcemetadata_test.go | 2 -- internal/toproto5/diagnostics_test.go | 3 -- internal/toproto5/dynamic_value_test.go | 2 -- .../toproto5/ephemeral_result_data_test.go | 2 -- .../ephemeralresourcemetadata_test.go | 2 -- internal/toproto5/function_test.go | 10 ------ internal/toproto5/getfunctions_test.go | 2 -- internal/toproto5/getmetadata_test.go | 2 -- internal/toproto5/getproviderschema_test.go | 2 -- internal/toproto5/importedresource_test.go | 2 -- internal/toproto5/moveresourcestate_test.go | 2 -- .../toproto5/openephemeralresource_test.go | 2 -- internal/toproto5/planresourcechange_test.go | 2 -- .../toproto5/prepareproviderconfig_test.go | 2 -- internal/toproto5/readdatasource_test.go | 2 -- internal/toproto5/readresource_test.go | 2 -- .../toproto5/renewephemeralresource_test.go | 2 -- internal/toproto5/resourcemetadata_test.go | 2 -- internal/toproto5/schema_attribute_test.go | 1 - internal/toproto5/schema_test.go | 1 - internal/toproto5/server_capabilities_test.go | 2 -- internal/toproto5/state_test.go | 2 -- .../toproto5/upgraderesourcestate_test.go | 2 -- .../toproto5/validatedatasourceconfig_test.go | 2 -- .../validateephemeralresourceconfig_test.go | 2 -- .../validateresourcetypeconfig_test.go | 2 -- internal/toproto6/applyresourcechange_test.go | 2 -- internal/toproto6/block_test.go | 1 - internal/toproto6/callfunction_test.go | 2 -- .../toproto6/closeephemeralresource_test.go | 2 -- internal/toproto6/config_test.go | 2 -- internal/toproto6/configureprovider_test.go | 2 -- internal/toproto6/datasourcemetadata_test.go | 2 -- internal/toproto6/diagnostics_test.go | 3 -- internal/toproto6/dynamic_value_test.go | 2 -- .../toproto6/ephemeral_result_data_test.go | 2 -- .../ephemeralresourcemetadata_test.go | 2 -- internal/toproto6/function_test.go | 10 ------ internal/toproto6/getfunctions_test.go | 2 -- internal/toproto6/getmetadata_test.go | 2 -- internal/toproto6/getproviderschema_test.go | 2 -- internal/toproto6/importedresource_test.go | 2 -- internal/toproto6/moveresourcestate_test.go | 2 -- .../toproto6/openephemeralresource_test.go | 2 -- internal/toproto6/planresourcechange_test.go | 2 -- internal/toproto6/readdatasource_test.go | 2 -- internal/toproto6/readresource_test.go | 2 -- .../toproto6/renewephemeralresource_test.go | 2 -- internal/toproto6/resourcemetadata_test.go | 2 -- internal/toproto6/schema_attribute_test.go | 1 - internal/toproto6/schema_test.go | 1 - internal/toproto6/server_capabilities_test.go | 2 -- internal/toproto6/state_test.go | 2 -- .../toproto6/upgraderesourcestate_test.go | 2 -- .../toproto6/validatedatasourceconfig_test.go | 2 -- .../validateephemeralresourceconfig_test.go | 2 -- .../toproto6/validateproviderconfig_test.go | 2 -- .../toproto6/validateresourceconfig_test.go | 2 -- .../totftypes/attribute_path_step_test.go | 2 -- internal/totftypes/attribute_path_test.go | 2 -- internal/totftypes/attribute_paths_test.go | 2 -- ...pression_step_attribute_name_exact_test.go | 6 ---- ...xpression_step_element_key_int_any_test.go | 6 ---- ...ression_step_element_key_int_exact_test.go | 6 ---- ...ession_step_element_key_string_any_test.go | 6 ---- ...sion_step_element_key_string_exact_test.go | 6 ---- ...ression_step_element_key_value_any_test.go | 6 ---- ...ssion_step_element_key_value_exact_test.go | 6 ---- path/expression_step_parent_test.go | 6 ---- path/expression_steps_test.go | 18 ---------- path/expression_test.go | 34 ------------------- path/expressions_test.go | 8 ----- path/path_step_attribute_name_test.go | 6 ---- path/path_step_element_key_int_test.go | 6 ---- path/path_step_element_key_string_test.go | 6 ---- path/path_step_element_key_value_test.go | 6 ---- path/path_steps_test.go | 14 -------- path/path_test.go | 22 ------------ path/paths_test.go | 6 ---- provider/metaschema/bool_attribute_test.go | 22 ------------ provider/metaschema/float64_attribute_test.go | 22 ------------ provider/metaschema/int64_attribute_test.go | 22 ------------ provider/metaschema/list_attribute_test.go | 24 ------------- .../metaschema/list_nested_attribute_test.go | 24 ------------- provider/metaschema/map_attribute_test.go | 24 ------------- .../metaschema/map_nested_attribute_test.go | 24 ------------- .../nested_attribute_object_test.go | 8 ----- provider/metaschema/number_attribute_test.go | 22 ------------ provider/metaschema/object_attribute_test.go | 24 ------------- provider/metaschema/schema_test.go | 26 -------------- provider/metaschema/set_attribute_test.go | 24 ------------- .../metaschema/set_nested_attribute_test.go | 24 ------------- .../single_nested_attribute_test.go | 24 ------------- provider/metaschema/string_attribute_test.go | 22 ------------ provider/schema/bool_attribute_test.go | 24 ------------- provider/schema/dynamic_attribute_test.go | 24 ------------- provider/schema/float32_attribute_test.go | 24 ------------- provider/schema/float64_attribute_test.go | 24 ------------- provider/schema/int32_attribute_test.go | 24 ------------- provider/schema/int64_attribute_test.go | 24 ------------- provider/schema/list_attribute_test.go | 26 -------------- provider/schema/list_nested_attribute_test.go | 28 --------------- provider/schema/list_nested_block_test.go | 18 ---------- provider/schema/map_attribute_test.go | 26 -------------- provider/schema/map_nested_attribute_test.go | 28 --------------- .../schema/nested_attribute_object_test.go | 10 ------ provider/schema/nested_block_object_test.go | 12 ------- provider/schema/number_attribute_test.go | 24 ------------- provider/schema/object_attribute_test.go | 26 -------------- provider/schema/schema_test.go | 26 -------------- provider/schema/set_attribute_test.go | 26 -------------- provider/schema/set_nested_attribute_test.go | 28 --------------- provider/schema/set_nested_block_test.go | 18 ---------- .../schema/single_nested_attribute_test.go | 26 -------------- provider/schema/single_nested_block_test.go | 16 --------- provider/schema/string_attribute_test.go | 24 ------------- providerserver/serve_opts_test.go | 4 --- resource/schema/bool_attribute_test.go | 30 ---------------- .../schema/booldefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../boolplanmodifier/requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/dynamic_attribute_test.go | 30 ---------------- .../dynamicdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/float32_attribute_test.go | 30 ---------------- .../float32default/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/float64_attribute_test.go | 30 ---------------- .../float64default/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/int32_attribute_test.go | 30 ---------------- .../schema/int32default/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/int64_attribute_test.go | 30 ---------------- .../schema/int64default/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/list_attribute_test.go | 30 ---------------- resource/schema/list_nested_attribute_test.go | 32 ----------------- resource/schema/list_nested_block_test.go | 20 ----------- .../schema/listdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../listplanmodifier/requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/map_attribute_test.go | 30 ---------------- resource/schema/map_nested_attribute_test.go | 32 ----------------- .../schema/mapdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../mapplanmodifier/requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- .../schema/nested_attribute_object_test.go | 12 ------- resource/schema/nested_block_object_test.go | 14 -------- resource/schema/number_attribute_test.go | 30 ---------------- .../schema/numberdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/object_attribute_test.go | 30 ---------------- .../schema/objectdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- resource/schema/schema_test.go | 26 -------------- resource/schema/set_attribute_test.go | 30 ---------------- resource/schema/set_nested_attribute_test.go | 32 ----------------- resource/schema/set_nested_block_test.go | 20 ----------- .../schema/setdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../setplanmodifier/requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- .../schema/single_nested_attribute_test.go | 32 ----------------- resource/schema/single_nested_block_test.go | 18 ---------- resource/schema/string_attribute_test.go | 30 ---------------- .../schema/stringdefault/static_value_test.go | 2 -- .../requires_replace_if_configured_test.go | 2 -- .../requires_replace_if_test.go | 2 -- .../requires_replace_test.go | 2 -- .../use_state_for_unknown_test.go | 2 -- tfsdk/config_test.go | 5 --- tfsdk/convert_test.go | 1 - tfsdk/ephemeral_result_data_test.go | 7 ---- tfsdk/plan_test.go | 7 ---- tfsdk/state_test.go | 5 --- tfsdk/value_as_test.go | 1 - tfsdk/value_from_test.go | 1 - types/basetypes/bool_type_test.go | 1 - types/basetypes/bool_value_test.go | 13 ------- types/basetypes/dynamic_type_test.go | 2 -- types/basetypes/dynamic_value_test.go | 7 ---- types/basetypes/float32_type_test.go | 1 - types/basetypes/float32_value_test.go | 14 -------- types/basetypes/float64_type_test.go | 3 -- types/basetypes/float64_value_test.go | 14 -------- types/basetypes/int32_type_test.go | 1 - types/basetypes/int32_value_test.go | 13 ------- types/basetypes/int64_type_test.go | 1 - types/basetypes/int64_value_test.go | 13 ------- types/basetypes/list_type_test.go | 7 ---- types/basetypes/list_value_test.go | 18 ---------- types/basetypes/map_type_test.go | 7 ---- types/basetypes/map_value_test.go | 18 ---------- types/basetypes/number_type_test.go | 1 - types/basetypes/number_value_test.go | 9 ----- types/basetypes/object_type_test.go | 4 --- types/basetypes/object_value_test.go | 16 --------- types/basetypes/set_type_test.go | 7 ---- types/basetypes/set_value_test.go | 17 ---------- types/basetypes/string_type_test.go | 1 - types/basetypes/string_value_test.go | 13 ------- types/basetypes/tuple_type_test.go | 5 --- types/basetypes/tuple_value_test.go | 14 -------- 477 files changed, 4 insertions(+), 3816 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 429044d2c..1e7c56bf6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,16 +1,17 @@ issues: - max-per-linter: 0 + max-issues-per-linter: 0 max-same-issues: 0 linters: disable-all: true enable: + - copyloopvar - durationcheck - errcheck - - exportloopref - forcetypeassert - gofmt - gosimple + - govet - ineffassign - makezero - misspell @@ -18,11 +19,10 @@ linters: - paralleltest - predeclared - staticcheck - - tenv - unconvert - unparam - unused - - govet + - usetesting run: # Prevent false positive timeouts in CI diff --git a/datasource/schema/bool_attribute_test.go b/datasource/schema/bool_attribute_test.go index b493d892d..ea22a21fc 100644 --- a/datasource/schema/bool_attribute_test.go +++ b/datasource/schema/bool_attribute_test.go @@ -56,8 +56,6 @@ func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestBoolAttributeBoolValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestBoolAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestBoolAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestBoolAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestBoolAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestBoolAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestBoolAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestBoolAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestBoolAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/dynamic_attribute_test.go b/datasource/schema/dynamic_attribute_test.go index 95fe01fb4..7166ae9cb 100644 --- a/datasource/schema/dynamic_attribute_test.go +++ b/datasource/schema/dynamic_attribute_test.go @@ -56,8 +56,6 @@ func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestDynamicAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestDynamicAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestDynamicAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestDynamicAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestDynamicAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestDynamicAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestDynamicAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestDynamicAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestDynamicAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/float32_attribute_test.go b/datasource/schema/float32_attribute_test.go index 1040f6245..3149803e0 100644 --- a/datasource/schema/float32_attribute_test.go +++ b/datasource/schema/float32_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat32AttributeFloat32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestFloat32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestFloat32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestFloat32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/float64_attribute_test.go b/datasource/schema/float64_attribute_test.go index b83e16abd..c73193387 100644 --- a/datasource/schema/float64_attribute_test.go +++ b/datasource/schema/float64_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat64AttributeFloat64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestFloat64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestFloat64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestFloat64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/int32_attribute_test.go b/datasource/schema/int32_attribute_test.go index 23ce97fd5..15404c9e8 100644 --- a/datasource/schema/int32_attribute_test.go +++ b/datasource/schema/int32_attribute_test.go @@ -56,8 +56,6 @@ func TestInt32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt32AttributeInt32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestInt32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestInt32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestInt32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestInt32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/int64_attribute_test.go b/datasource/schema/int64_attribute_test.go index b15eeb85f..ed0544c62 100644 --- a/datasource/schema/int64_attribute_test.go +++ b/datasource/schema/int64_attribute_test.go @@ -56,8 +56,6 @@ func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt64AttributeInt64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestInt64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestInt64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestInt64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/list_attribute_test.go b/datasource/schema/list_attribute_test.go index 1e7335ce3..c38c41e2f 100644 --- a/datasource/schema/list_attribute_test.go +++ b/datasource/schema/list_attribute_test.go @@ -59,8 +59,6 @@ func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestListAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestListAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestListAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestListAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestListAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -283,8 +271,6 @@ func TestListAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -317,8 +303,6 @@ func TestListAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +335,6 @@ func TestListAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -385,8 +367,6 @@ func TestListAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -413,8 +393,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -447,8 +425,6 @@ func TestListAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -536,8 +512,6 @@ func TestListAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/list_nested_attribute_test.go b/datasource/schema/list_nested_attribute_test.go index fd1cf6941..b7ea5be20 100644 --- a/datasource/schema/list_nested_attribute_test.go +++ b/datasource/schema/list_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestListNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestListNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestListNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestListNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestListNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestListNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -436,8 +422,6 @@ func TestListNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +460,6 @@ func TestListNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -516,8 +498,6 @@ func TestListNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -556,8 +536,6 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -584,8 +562,6 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -624,8 +600,6 @@ func TestListNestedAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -703,8 +677,6 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/list_nested_block_test.go b/datasource/schema/list_nested_block_test.go index 6dce467fa..dcf2f0d6b 100644 --- a/datasource/schema/list_nested_block_test.go +++ b/datasource/schema/list_nested_block_test.go @@ -86,8 +86,6 @@ func TestListNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestListNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestListNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestListNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestListNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestListNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestListNestedBlockListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestListNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestListNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/map_attribute_test.go b/datasource/schema/map_attribute_test.go index ab3d31167..af361a636 100644 --- a/datasource/schema/map_attribute_test.go +++ b/datasource/schema/map_attribute_test.go @@ -59,8 +59,6 @@ func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestMapAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestMapAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestMapAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestMapAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestMapAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -283,8 +271,6 @@ func TestMapAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -317,8 +303,6 @@ func TestMapAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +335,6 @@ func TestMapAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -385,8 +367,6 @@ func TestMapAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -413,8 +393,6 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -447,8 +425,6 @@ func TestMapAttributeMapValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -536,8 +512,6 @@ func TestMapAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/map_nested_attribute_test.go b/datasource/schema/map_nested_attribute_test.go index 671f55593..d691cda82 100644 --- a/datasource/schema/map_nested_attribute_test.go +++ b/datasource/schema/map_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestMapNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestMapNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestMapNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestMapNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestMapNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestMapNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -436,8 +422,6 @@ func TestMapNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +460,6 @@ func TestMapNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -516,8 +498,6 @@ func TestMapNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -556,8 +536,6 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -584,8 +562,6 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -624,8 +600,6 @@ func TestMapNestedAttributeMapNestedValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -703,8 +677,6 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/nested_attribute_object_test.go b/datasource/schema/nested_attribute_object_test.go index db5bc1277..74bf24506 100644 --- a/datasource/schema/nested_attribute_object_test.go +++ b/datasource/schema/nested_attribute_object_test.go @@ -80,8 +80,6 @@ func TestNestedAttributeObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestNestedAttributeObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +181,6 @@ func TestNestedAttributeObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -223,8 +217,6 @@ func TestNestedAttributeObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -265,8 +257,6 @@ func TestNestedAttributeObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/nested_block_object_test.go b/datasource/schema/nested_block_object_test.go index 2e0aa4738..550b8563f 100644 --- a/datasource/schema/nested_block_object_test.go +++ b/datasource/schema/nested_block_object_test.go @@ -98,8 +98,6 @@ func TestNestedBlockObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -163,8 +161,6 @@ func TestNestedBlockObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -203,8 +199,6 @@ func TestNestedBlockObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -259,8 +253,6 @@ func TestNestedBlockObjectGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -297,8 +289,6 @@ func TestNestedBlockObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +341,6 @@ func TestNestedBlockObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/number_attribute_test.go b/datasource/schema/number_attribute_test.go index 449dda7ce..c30b711eb 100644 --- a/datasource/schema/number_attribute_test.go +++ b/datasource/schema/number_attribute_test.go @@ -56,8 +56,6 @@ func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestNumberAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestNumberAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestNumberAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestNumberAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestNumberAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestNumberAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestNumberAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestNumberAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestNumberAttributeNumberValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/object_attribute_test.go b/datasource/schema/object_attribute_test.go index ab3c428ce..d5c45ee64 100644 --- a/datasource/schema/object_attribute_test.go +++ b/datasource/schema/object_attribute_test.go @@ -65,8 +65,6 @@ func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -113,8 +111,6 @@ func TestObjectAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -153,8 +149,6 @@ func TestObjectAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -187,8 +181,6 @@ func TestObjectAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -221,8 +213,6 @@ func TestObjectAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -255,8 +245,6 @@ func TestObjectAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -289,8 +277,6 @@ func TestObjectAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -323,8 +309,6 @@ func TestObjectAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -357,8 +341,6 @@ func TestObjectAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -391,8 +373,6 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -419,8 +399,6 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -453,8 +431,6 @@ func TestObjectAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -569,8 +545,6 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/schema_test.go b/datasource/schema/schema_test.go index 48dea22c4..a6eb8ee5c 100644 --- a/datasource/schema/schema_test.go +++ b/datasource/schema/schema_test.go @@ -100,8 +100,6 @@ func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,7 +267,6 @@ func TestSchemaAttributeAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,7 +387,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -448,8 +444,6 @@ func TestSchemaGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -504,8 +498,6 @@ func TestSchemaGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -542,8 +534,6 @@ func TestSchemaGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -580,8 +570,6 @@ func TestSchemaGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +606,6 @@ func TestSchemaGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -650,8 +636,6 @@ func TestSchemaGetVersion(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -698,8 +682,6 @@ func TestSchemaType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -839,8 +821,6 @@ func TestSchemaTypeAtPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -972,8 +952,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1029,8 +1007,6 @@ func TestSchemaValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1342,8 +1318,6 @@ func TestSchemaValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/set_attribute_test.go b/datasource/schema/set_attribute_test.go index 9a0760b9b..02fcb830c 100644 --- a/datasource/schema/set_attribute_test.go +++ b/datasource/schema/set_attribute_test.go @@ -59,8 +59,6 @@ func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestSetAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestSetAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestSetAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestSetAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestSetAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -283,8 +271,6 @@ func TestSetAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -317,8 +303,6 @@ func TestSetAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +335,6 @@ func TestSetAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -385,8 +367,6 @@ func TestSetAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -413,8 +393,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -447,8 +425,6 @@ func TestSetAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -536,8 +512,6 @@ func TestSetAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/set_nested_attribute_test.go b/datasource/schema/set_nested_attribute_test.go index 4b064ee7c..1a50cac03 100644 --- a/datasource/schema/set_nested_attribute_test.go +++ b/datasource/schema/set_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestSetNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestSetNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestSetNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestSetNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestSetNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestSetNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -436,8 +422,6 @@ func TestSetNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +460,6 @@ func TestSetNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -516,8 +498,6 @@ func TestSetNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -556,8 +536,6 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -584,8 +562,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -624,8 +600,6 @@ func TestSetNestedAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -703,8 +677,6 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/set_nested_block_test.go b/datasource/schema/set_nested_block_test.go index 40096f374..a868bad72 100644 --- a/datasource/schema/set_nested_block_test.go +++ b/datasource/schema/set_nested_block_test.go @@ -86,8 +86,6 @@ func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestSetNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestSetNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestSetNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestSetNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestSetNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestSetNestedBlockSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestSetNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/single_nested_attribute_test.go b/datasource/schema/single_nested_attribute_test.go index 2a62a8e42..0a7a0b66f 100644 --- a/datasource/schema/single_nested_attribute_test.go +++ b/datasource/schema/single_nested_attribute_test.go @@ -82,8 +82,6 @@ func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +171,6 @@ func TestSingleNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -211,8 +207,6 @@ func TestSingleNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +243,6 @@ func TestSingleNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +279,6 @@ func TestSingleNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -323,8 +313,6 @@ func TestSingleNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -365,8 +353,6 @@ func TestSingleNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -403,8 +389,6 @@ func TestSingleNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -441,8 +425,6 @@ func TestSingleNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -479,8 +461,6 @@ func TestSingleNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -517,8 +497,6 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -545,8 +523,6 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -583,8 +559,6 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/single_nested_block_test.go b/datasource/schema/single_nested_block_test.go index 041f98bfa..0208eb561 100644 --- a/datasource/schema/single_nested_block_test.go +++ b/datasource/schema/single_nested_block_test.go @@ -99,8 +99,6 @@ func TestSingleNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +149,6 @@ func TestSingleNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +249,6 @@ func TestSingleNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -291,8 +285,6 @@ func TestSingleNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -329,8 +321,6 @@ func TestSingleNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +369,6 @@ func TestSingleNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -417,8 +405,6 @@ func TestSingleNestedBlockObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -471,8 +457,6 @@ func TestSingleNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/datasource/schema/string_attribute_test.go b/datasource/schema/string_attribute_test.go index f86110feb..09ea194f2 100644 --- a/datasource/schema/string_attribute_test.go +++ b/datasource/schema/string_attribute_test.go @@ -56,8 +56,6 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestStringAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestStringAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestStringAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestStringAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestStringAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestStringAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestStringAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestStringAttributeStringValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/diag/diagnostics_test.go b/diag/diagnostics_test.go index 82def641e..cdc71ad7d 100644 --- a/diag/diagnostics_test.go +++ b/diag/diagnostics_test.go @@ -61,7 +61,6 @@ func TestDiagnosticsAddAttributeError(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,7 +122,6 @@ func TestDiagnosticsAddAttributeWarning(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,7 +179,6 @@ func TestDiagnosticsAddError(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -239,7 +236,6 @@ func TestDiagnosticsAddWarning(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -361,7 +357,6 @@ func TestDiagnosticsAppend(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -462,7 +457,6 @@ func TestDiagnosticsContains(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -588,8 +582,6 @@ func TestDiagnosticsEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -637,7 +629,6 @@ func TestDiagnosticsHasError(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -682,7 +673,6 @@ func TestDiagnosticsErrorsCount(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -727,7 +717,6 @@ func TestDiagnosticsWarningsCount(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -774,7 +763,6 @@ func TestDiagnosticsErrors(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -821,7 +809,6 @@ func TestDiagnosticsWarnings(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/diag/error_diagnostic_test.go b/diag/error_diagnostic_test.go index 2aaa44ecd..9746e6776 100644 --- a/diag/error_diagnostic_test.go +++ b/diag/error_diagnostic_test.go @@ -45,7 +45,6 @@ func TestErrorDiagnosticEqual(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/diag/warning_diagnostic_test.go b/diag/warning_diagnostic_test.go index 856346df0..177ed2082 100644 --- a/diag/warning_diagnostic_test.go +++ b/diag/warning_diagnostic_test.go @@ -45,7 +45,6 @@ func TestWarningDiagnosticEqual(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/bool_attribute_test.go b/ephemeral/schema/bool_attribute_test.go index 7eebac422..30cc54aa2 100644 --- a/ephemeral/schema/bool_attribute_test.go +++ b/ephemeral/schema/bool_attribute_test.go @@ -56,8 +56,6 @@ func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestBoolAttributeBoolValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestBoolAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestBoolAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestBoolAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestBoolAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestBoolAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestBoolAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestBoolAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestBoolAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/dynamic_attribute_test.go b/ephemeral/schema/dynamic_attribute_test.go index 99fbf6f4e..4d01e95a0 100644 --- a/ephemeral/schema/dynamic_attribute_test.go +++ b/ephemeral/schema/dynamic_attribute_test.go @@ -55,8 +55,6 @@ func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -103,8 +101,6 @@ func TestDynamicAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestDynamicAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -172,8 +166,6 @@ func TestDynamicAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -206,8 +198,6 @@ func TestDynamicAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -240,8 +230,6 @@ func TestDynamicAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestDynamicAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -308,8 +294,6 @@ func TestDynamicAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -342,8 +326,6 @@ func TestDynamicAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -376,8 +358,6 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -404,8 +384,6 @@ func TestDynamicAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -438,8 +416,6 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/float32_attribute_test.go b/ephemeral/schema/float32_attribute_test.go index f542972aa..1617d12af 100644 --- a/ephemeral/schema/float32_attribute_test.go +++ b/ephemeral/schema/float32_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat32AttributeFloat32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestFloat32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestFloat32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestFloat32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/float64_attribute_test.go b/ephemeral/schema/float64_attribute_test.go index db9e8db2b..4bfb2ebd0 100644 --- a/ephemeral/schema/float64_attribute_test.go +++ b/ephemeral/schema/float64_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat64AttributeFloat64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestFloat64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestFloat64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestFloat64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestFloat54AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/int32_attribute_test.go b/ephemeral/schema/int32_attribute_test.go index e118cb0db..b4d3cc6dc 100644 --- a/ephemeral/schema/int32_attribute_test.go +++ b/ephemeral/schema/int32_attribute_test.go @@ -56,8 +56,6 @@ func TestInt32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt32AttributeInt32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestInt32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestInt32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestInt32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestInt2AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/int64_attribute_test.go b/ephemeral/schema/int64_attribute_test.go index 5f07ff2ff..6b7c1849f 100644 --- a/ephemeral/schema/int64_attribute_test.go +++ b/ephemeral/schema/int64_attribute_test.go @@ -56,8 +56,6 @@ func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt64AttributeInt64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestInt64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestInt64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestInt64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +391,6 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/list_attribute_test.go b/ephemeral/schema/list_attribute_test.go index 4ab1985d8..cdbc9feb6 100644 --- a/ephemeral/schema/list_attribute_test.go +++ b/ephemeral/schema/list_attribute_test.go @@ -58,8 +58,6 @@ func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -106,8 +104,6 @@ func TestListAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +142,6 @@ func TestListAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +174,6 @@ func TestListAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +206,6 @@ func TestListAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +238,6 @@ func TestListAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -282,8 +270,6 @@ func TestListAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +302,6 @@ func TestListAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +334,6 @@ func TestListAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -384,8 +366,6 @@ func TestListAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +398,6 @@ func TestListAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -507,8 +485,6 @@ func TestListAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -536,8 +512,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/list_nested_attribute_test.go b/ephemeral/schema/list_nested_attribute_test.go index 7e1153e79..914af418c 100644 --- a/ephemeral/schema/list_nested_attribute_test.go +++ b/ephemeral/schema/list_nested_attribute_test.go @@ -86,8 +86,6 @@ func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestListNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -231,8 +227,6 @@ func TestListNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -271,8 +265,6 @@ func TestListNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -311,8 +303,6 @@ func TestListNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +339,6 @@ func TestListNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -395,8 +383,6 @@ func TestListNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -435,8 +421,6 @@ func TestListNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -475,8 +459,6 @@ func TestListNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -515,8 +497,6 @@ func TestListNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -555,8 +535,6 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -583,8 +561,6 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -623,8 +599,6 @@ func TestListNestedAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -702,8 +676,6 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/list_nested_block_test.go b/ephemeral/schema/list_nested_block_test.go index bb20c35de..763afd28c 100644 --- a/ephemeral/schema/list_nested_block_test.go +++ b/ephemeral/schema/list_nested_block_test.go @@ -86,8 +86,6 @@ func TestListNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestListNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestListNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestListNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestListNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestListNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestListNestedBlockListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestListNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestListNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/map_attribute_test.go b/ephemeral/schema/map_attribute_test.go index 7da8aa46f..f750b5f5d 100644 --- a/ephemeral/schema/map_attribute_test.go +++ b/ephemeral/schema/map_attribute_test.go @@ -58,8 +58,6 @@ func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -106,8 +104,6 @@ func TestMapAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +142,6 @@ func TestMapAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +174,6 @@ func TestMapAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +206,6 @@ func TestMapAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +238,6 @@ func TestMapAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -282,8 +270,6 @@ func TestMapAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +302,6 @@ func TestMapAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +334,6 @@ func TestMapAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -384,8 +366,6 @@ func TestMapAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -412,8 +392,6 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -446,8 +424,6 @@ func TestMapAttributeMapValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -535,8 +511,6 @@ func TestMapAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/map_nested_attribute_test.go b/ephemeral/schema/map_nested_attribute_test.go index ef21cf89b..37c2f1aa2 100644 --- a/ephemeral/schema/map_nested_attribute_test.go +++ b/ephemeral/schema/map_nested_attribute_test.go @@ -86,8 +86,6 @@ func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestMapNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -231,8 +227,6 @@ func TestMapNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -271,8 +265,6 @@ func TestMapNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -311,8 +303,6 @@ func TestMapNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +339,6 @@ func TestMapNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -395,8 +383,6 @@ func TestMapNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -435,8 +421,6 @@ func TestMapNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -475,8 +459,6 @@ func TestMapNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -515,8 +497,6 @@ func TestMapNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -555,8 +535,6 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -583,8 +561,6 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -623,8 +599,6 @@ func TestMapNestedAttributeMapNestedValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -702,8 +676,6 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/nested_attribute_object_test.go b/ephemeral/schema/nested_attribute_object_test.go index 65c76544b..f4827f212 100644 --- a/ephemeral/schema/nested_attribute_object_test.go +++ b/ephemeral/schema/nested_attribute_object_test.go @@ -80,8 +80,6 @@ func TestNestedAttributeObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestNestedAttributeObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +181,6 @@ func TestNestedAttributeObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -223,8 +217,6 @@ func TestNestedAttributeObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -265,8 +257,6 @@ func TestNestedAttributeObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/nested_block_object_test.go b/ephemeral/schema/nested_block_object_test.go index f484d10ca..7c2ebffc5 100644 --- a/ephemeral/schema/nested_block_object_test.go +++ b/ephemeral/schema/nested_block_object_test.go @@ -98,8 +98,6 @@ func TestNestedBlockObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -163,8 +161,6 @@ func TestNestedBlockObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -203,8 +199,6 @@ func TestNestedBlockObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -259,8 +253,6 @@ func TestNestedBlockObjectGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -297,8 +289,6 @@ func TestNestedBlockObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +341,6 @@ func TestNestedBlockObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/number_attribute_test.go b/ephemeral/schema/number_attribute_test.go index d3636d006..a15142afa 100644 --- a/ephemeral/schema/number_attribute_test.go +++ b/ephemeral/schema/number_attribute_test.go @@ -56,8 +56,6 @@ func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestNumberAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestNumberAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestNumberAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestNumberAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestNumberAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestNumberAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -309,8 +295,6 @@ func TestNumberAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,8 +327,6 @@ func TestNumberAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +359,6 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -439,8 +417,6 @@ func TestNumberAttributeNumberValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/object_attribute_test.go b/ephemeral/schema/object_attribute_test.go index 76f4aab38..f49e4afca 100644 --- a/ephemeral/schema/object_attribute_test.go +++ b/ephemeral/schema/object_attribute_test.go @@ -65,8 +65,6 @@ func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -113,8 +111,6 @@ func TestObjectAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -153,8 +149,6 @@ func TestObjectAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -187,8 +181,6 @@ func TestObjectAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -221,8 +213,6 @@ func TestObjectAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -255,8 +245,6 @@ func TestObjectAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -289,8 +277,6 @@ func TestObjectAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -323,8 +309,6 @@ func TestObjectAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -357,8 +341,6 @@ func TestObjectAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -391,8 +373,6 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -425,8 +405,6 @@ func TestObjectAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -453,8 +431,6 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -569,8 +545,6 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/schema_test.go b/ephemeral/schema/schema_test.go index 64cc4806b..e08d76a5d 100644 --- a/ephemeral/schema/schema_test.go +++ b/ephemeral/schema/schema_test.go @@ -100,8 +100,6 @@ func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,7 +267,6 @@ func TestSchemaAttributeAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,7 +387,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -448,8 +444,6 @@ func TestSchemaGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -504,8 +498,6 @@ func TestSchemaGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -542,8 +534,6 @@ func TestSchemaGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -580,8 +570,6 @@ func TestSchemaGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +606,6 @@ func TestSchemaGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -650,8 +636,6 @@ func TestSchemaGetVersion(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -698,8 +682,6 @@ func TestSchemaType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -839,8 +821,6 @@ func TestSchemaTypeAtPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -972,8 +952,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1029,8 +1007,6 @@ func TestSchemaValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1342,8 +1318,6 @@ func TestSchemaValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/set_attribute_test.go b/ephemeral/schema/set_attribute_test.go index a6211d666..d3c158fda 100644 --- a/ephemeral/schema/set_attribute_test.go +++ b/ephemeral/schema/set_attribute_test.go @@ -58,8 +58,6 @@ func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -106,8 +104,6 @@ func TestSetAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +142,6 @@ func TestSetAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +174,6 @@ func TestSetAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +206,6 @@ func TestSetAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +238,6 @@ func TestSetAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -282,8 +270,6 @@ func TestSetAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +302,6 @@ func TestSetAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +334,6 @@ func TestSetAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -384,8 +366,6 @@ func TestSetAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -412,8 +392,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -446,8 +424,6 @@ func TestSetAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -535,8 +511,6 @@ func TestSetAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/set_nested_attribute_test.go b/ephemeral/schema/set_nested_attribute_test.go index bcfa627a4..3abd59839 100644 --- a/ephemeral/schema/set_nested_attribute_test.go +++ b/ephemeral/schema/set_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestSetNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestSetNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestSetNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestSetNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestSetNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestSetNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -436,8 +422,6 @@ func TestSetNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +460,6 @@ func TestSetNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -516,8 +498,6 @@ func TestSetNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -556,8 +536,6 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -584,8 +562,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -624,8 +600,6 @@ func TestSetNestedAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -703,8 +677,6 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/set_nested_block_test.go b/ephemeral/schema/set_nested_block_test.go index 0862026f4..d403d4486 100644 --- a/ephemeral/schema/set_nested_block_test.go +++ b/ephemeral/schema/set_nested_block_test.go @@ -86,8 +86,6 @@ func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestSetNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestSetNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestSetNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestSetNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestSetNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestSetNestedBlockSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestSetNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/single_nested_attribute_test.go b/ephemeral/schema/single_nested_attribute_test.go index d3690828f..8f4e4e783 100644 --- a/ephemeral/schema/single_nested_attribute_test.go +++ b/ephemeral/schema/single_nested_attribute_test.go @@ -81,8 +81,6 @@ func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -172,8 +170,6 @@ func TestSingleNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -210,8 +206,6 @@ func TestSingleNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +242,6 @@ func TestSingleNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -286,8 +278,6 @@ func TestSingleNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -322,8 +312,6 @@ func TestSingleNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -364,8 +352,6 @@ func TestSingleNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -402,8 +388,6 @@ func TestSingleNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -440,8 +424,6 @@ func TestSingleNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -478,8 +460,6 @@ func TestSingleNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -516,8 +496,6 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -544,8 +522,6 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -582,8 +558,6 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/single_nested_block_test.go b/ephemeral/schema/single_nested_block_test.go index 994f94894..6c6a74639 100644 --- a/ephemeral/schema/single_nested_block_test.go +++ b/ephemeral/schema/single_nested_block_test.go @@ -98,8 +98,6 @@ func TestSingleNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -150,8 +148,6 @@ func TestSingleNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -252,8 +248,6 @@ func TestSingleNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -290,8 +284,6 @@ func TestSingleNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +320,6 @@ func TestSingleNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestSingleNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -416,8 +404,6 @@ func TestSingleNestedBlockObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -470,8 +456,6 @@ func TestSingleNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/ephemeral/schema/string_attribute_test.go b/ephemeral/schema/string_attribute_test.go index e0577b3cb..9f8532f34 100644 --- a/ephemeral/schema/string_attribute_test.go +++ b/ephemeral/schema/string_attribute_test.go @@ -55,8 +55,6 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -103,8 +101,6 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestStringAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -172,8 +166,6 @@ func TestStringAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -206,8 +198,6 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -240,8 +230,6 @@ func TestStringAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestStringAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -308,8 +294,6 @@ func TestStringAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -342,8 +326,6 @@ func TestStringAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -376,8 +358,6 @@ func TestStringAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -404,8 +384,6 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -438,8 +416,6 @@ func TestStringAttributeStringValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/arguments_data_test.go b/function/arguments_data_test.go index 7b8d2f83e..6c8e55fb9 100644 --- a/function/arguments_data_test.go +++ b/function/arguments_data_test.go @@ -68,8 +68,6 @@ func TestArgumentsDataEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -357,8 +355,6 @@ func TestArgumentsDataGet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +460,6 @@ func TestArgumentsDataGetArgument(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/bool_parameter_test.go b/function/bool_parameter_test.go index 6082fc279..2495bd19a 100644 --- a/function/bool_parameter_test.go +++ b/function/bool_parameter_test.go @@ -44,8 +44,6 @@ func TestBoolParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestBoolParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestBoolParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestBoolParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestBoolParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestBoolParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestBoolParameterBoolValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestBoolParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/bool_return_test.go b/function/bool_return_test.go index 5d27e7429..3894721a1 100644 --- a/function/bool_return_test.go +++ b/function/bool_return_test.go @@ -33,8 +33,6 @@ func TestBoolReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/definition_test.go b/function/definition_test.go index 87dcc27e6..d4625b685 100644 --- a/function/definition_test.go +++ b/function/definition_test.go @@ -293,8 +293,6 @@ func TestDefinitionValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/dynamic_parameter_test.go b/function/dynamic_parameter_test.go index d79889c83..94b91d0d0 100644 --- a/function/dynamic_parameter_test.go +++ b/function/dynamic_parameter_test.go @@ -44,8 +44,6 @@ func TestDynamicParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestDynamicParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestDynamicParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestDynamicParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestDynamicParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestDynamicParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestDynamicParameterDynamicValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestDynamicParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/dynamic_return_test.go b/function/dynamic_return_test.go index 1c1941050..67eaf918d 100644 --- a/function/dynamic_return_test.go +++ b/function/dynamic_return_test.go @@ -33,8 +33,6 @@ func TestDynamicReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/float32_parameter_test.go b/function/float32_parameter_test.go index 9292bf8c1..6dc40f894 100644 --- a/function/float32_parameter_test.go +++ b/function/float32_parameter_test.go @@ -44,8 +44,6 @@ func TestFloat32ParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestFloat32ParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestFloat32ParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestFloat32ParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestFloat32ParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestFloat32ParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestFloat32ParameterFloat32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestFloat32ParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/float32_return_test.go b/function/float32_return_test.go index 242041ebe..fe26963f2 100644 --- a/function/float32_return_test.go +++ b/function/float32_return_test.go @@ -34,8 +34,6 @@ func TestFloat32ReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/float64_parameter_test.go b/function/float64_parameter_test.go index 1e99b0904..60dc4495c 100644 --- a/function/float64_parameter_test.go +++ b/function/float64_parameter_test.go @@ -44,8 +44,6 @@ func TestFloat64ParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestFloat64ParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestFloat64ParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestFloat64ParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestFloat64ParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestFloat64ParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestFloat64ParameterFloat64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestFloat64ParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/float64_return_test.go b/function/float64_return_test.go index 8b48732c4..31a682920 100644 --- a/function/float64_return_test.go +++ b/function/float64_return_test.go @@ -33,8 +33,6 @@ func TestFloat64ReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/func_error_test.go b/function/func_error_test.go index 6232071e0..b95d89bb9 100644 --- a/function/func_error_test.go +++ b/function/func_error_test.go @@ -74,7 +74,6 @@ func TestFunctionError_Equal(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,7 +109,6 @@ func TestFunctionError_Error(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -219,7 +217,6 @@ func TestConcatFuncErrors(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -310,7 +307,6 @@ func TestFuncErrorFromDiags(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/int32_parameter_test.go b/function/int32_parameter_test.go index 29bb0e29d..880e8c1e0 100644 --- a/function/int32_parameter_test.go +++ b/function/int32_parameter_test.go @@ -44,8 +44,6 @@ func TestInt32ParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestInt32ParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestInt32ParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestInt32ParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestInt32ParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestInt32ParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestInt32ParameterInt32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestInt32ParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/int32_return_test.go b/function/int32_return_test.go index 555fe0c1a..ef13ca85f 100644 --- a/function/int32_return_test.go +++ b/function/int32_return_test.go @@ -34,8 +34,6 @@ func TestInt32ReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/int64_parameter_test.go b/function/int64_parameter_test.go index a0880de07..d4b9c59e1 100644 --- a/function/int64_parameter_test.go +++ b/function/int64_parameter_test.go @@ -44,8 +44,6 @@ func TestInt64ParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestInt64ParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestInt64ParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestInt64ParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestInt64ParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestInt64ParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestInt64ParameterInt64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestInt64ParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/int64_return_test.go b/function/int64_return_test.go index 7f15e3006..eae8b4bb5 100644 --- a/function/int64_return_test.go +++ b/function/int64_return_test.go @@ -33,8 +33,6 @@ func TestInt64ReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/list_parameter_test.go b/function/list_parameter_test.go index abca3b288..630053b20 100644 --- a/function/list_parameter_test.go +++ b/function/list_parameter_test.go @@ -45,8 +45,6 @@ func TestListParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -85,8 +83,6 @@ func TestListParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -125,8 +121,6 @@ func TestListParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -165,8 +159,6 @@ func TestListParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -199,8 +191,6 @@ func TestListParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -245,8 +235,6 @@ func TestListParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestListParameterListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +409,6 @@ func TestListParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/list_return_test.go b/function/list_return_test.go index 1465597d3..aaa1b97ab 100644 --- a/function/list_return_test.go +++ b/function/list_return_test.go @@ -49,8 +49,6 @@ func TestListReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestListReturnValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/map_parameter_test.go b/function/map_parameter_test.go index 003f03f0b..f58e30193 100644 --- a/function/map_parameter_test.go +++ b/function/map_parameter_test.go @@ -45,8 +45,6 @@ func TestMapParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -85,8 +83,6 @@ func TestMapParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -125,8 +121,6 @@ func TestMapParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -165,8 +159,6 @@ func TestMapParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -199,8 +191,6 @@ func TestMapParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -245,8 +235,6 @@ func TestMapParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestMapParameterMapValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +409,6 @@ func TestMapParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/map_return_test.go b/function/map_return_test.go index b919231c2..e0b8a9180 100644 --- a/function/map_return_test.go +++ b/function/map_return_test.go @@ -49,8 +49,6 @@ func TestMapReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestMapReturnValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/number_parameter_test.go b/function/number_parameter_test.go index d5fbc23f1..df6d09db5 100644 --- a/function/number_parameter_test.go +++ b/function/number_parameter_test.go @@ -44,8 +44,6 @@ func TestNumberParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestNumberParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestNumberParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestNumberParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestNumberParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestNumberParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestNumberParameterNumberValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestNumberParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/number_return_test.go b/function/number_return_test.go index 870548470..f7495b723 100644 --- a/function/number_return_test.go +++ b/function/number_return_test.go @@ -33,8 +33,6 @@ func TestNumberReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/object_parameter_test.go b/function/object_parameter_test.go index 79c86e234..2cb78d05d 100644 --- a/function/object_parameter_test.go +++ b/function/object_parameter_test.go @@ -45,8 +45,6 @@ func TestObjectParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -85,8 +83,6 @@ func TestObjectParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -125,8 +121,6 @@ func TestObjectParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -165,8 +159,6 @@ func TestObjectParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -199,8 +191,6 @@ func TestObjectParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +243,6 @@ func TestObjectParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -295,8 +283,6 @@ func TestObjectParameterObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -479,8 +465,6 @@ func TestObjectParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/object_return_test.go b/function/object_return_test.go index af1c78652..5e984c3b2 100644 --- a/function/object_return_test.go +++ b/function/object_return_test.go @@ -57,8 +57,6 @@ func TestObjectReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -165,8 +163,6 @@ func TestObjectReturnValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/result_data_test.go b/function/result_data_test.go index 7bd59010a..f32cbf560 100644 --- a/function/result_data_test.go +++ b/function/result_data_test.go @@ -54,8 +54,6 @@ func TestResultDataSet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/set_parameter_test.go b/function/set_parameter_test.go index 705640dbb..eeef290ab 100644 --- a/function/set_parameter_test.go +++ b/function/set_parameter_test.go @@ -45,8 +45,6 @@ func TestSetParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -85,8 +83,6 @@ func TestSetParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -125,8 +121,6 @@ func TestSetParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -165,8 +159,6 @@ func TestSetParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -199,8 +191,6 @@ func TestSetParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -245,8 +235,6 @@ func TestSetParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestSetParameterSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +409,6 @@ func TestSetParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/set_return_test.go b/function/set_return_test.go index ef973f3f6..9623a4cfd 100644 --- a/function/set_return_test.go +++ b/function/set_return_test.go @@ -49,8 +49,6 @@ func TestSetReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestSetReturnValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/string_parameter_test.go b/function/string_parameter_test.go index 73a8a18cb..3fe3b04b7 100644 --- a/function/string_parameter_test.go +++ b/function/string_parameter_test.go @@ -44,8 +44,6 @@ func TestStringParameterGetAllowNullValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -84,8 +82,6 @@ func TestStringParameterGetAllowUnknownValues(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -124,8 +120,6 @@ func TestStringParameterGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +158,6 @@ func TestStringParameterGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestStringParameterGetName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +222,6 @@ func TestStringParameterGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,8 +262,6 @@ func TestStringParameterStringValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestStringParameterValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/function/string_return_test.go b/function/string_return_test.go index 5e0db2260..08c867884 100644 --- a/function/string_return_test.go +++ b/function/string_return_test.go @@ -33,8 +33,6 @@ func TestStringReturnGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/applyresourcechange_test.go b/internal/fromproto5/applyresourcechange_test.go index 6aca21466..859181364 100644 --- a/internal/fromproto5/applyresourcechange_test.go +++ b/internal/fromproto5/applyresourcechange_test.go @@ -250,8 +250,6 @@ func TestApplyResourceChangeRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/arguments_data_test.go b/internal/fromproto5/arguments_data_test.go index cf5754adf..25d8c1bcb 100644 --- a/internal/fromproto5/arguments_data_test.go +++ b/internal/fromproto5/arguments_data_test.go @@ -689,8 +689,6 @@ func TestArgumentsData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3114,8 +3112,6 @@ func TestArgumentsData_ParameterValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/callfunction_test.go b/internal/fromproto5/callfunction_test.go index 3cde3f00a..214eebb3a 100644 --- a/internal/fromproto5/callfunction_test.go +++ b/internal/fromproto5/callfunction_test.go @@ -81,8 +81,6 @@ func TestCallFunctionRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/closeephemeralresource_test.go b/internal/fromproto5/closeephemeralresource_test.go index e9ba362ae..777d2c14b 100644 --- a/internal/fromproto5/closeephemeralresource_test.go +++ b/internal/fromproto5/closeephemeralresource_test.go @@ -82,8 +82,6 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/config_test.go b/internal/fromproto5/config_test.go index 60e5fc4b4..f252af7a3 100644 --- a/internal/fromproto5/config_test.go +++ b/internal/fromproto5/config_test.go @@ -103,8 +103,6 @@ func TestConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/configureprovider_test.go b/internal/fromproto5/configureprovider_test.go index f61f925a3..b653cd146 100644 --- a/internal/fromproto5/configureprovider_test.go +++ b/internal/fromproto5/configureprovider_test.go @@ -118,8 +118,6 @@ func TestConfigureProviderRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/dynamic_value_test.go b/internal/fromproto5/dynamic_value_test.go index a92d94445..a8aebd7b5 100644 --- a/internal/fromproto5/dynamic_value_test.go +++ b/internal/fromproto5/dynamic_value_test.go @@ -1498,8 +1498,6 @@ func TestDynamicValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/ephemeral_result_data_test.go b/internal/fromproto5/ephemeral_result_data_test.go index 40f2ebff5..0af6479bd 100644 --- a/internal/fromproto5/ephemeral_result_data_test.go +++ b/internal/fromproto5/ephemeral_result_data_test.go @@ -103,8 +103,6 @@ func TestEphemeralResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/getfunctions_test.go b/internal/fromproto5/getfunctions_test.go index 62d42aab8..9e26cbcef 100644 --- a/internal/fromproto5/getfunctions_test.go +++ b/internal/fromproto5/getfunctions_test.go @@ -31,8 +31,6 @@ func TestGetFunctionsRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/getmetadata_test.go b/internal/fromproto5/getmetadata_test.go index e92a5acef..adc450e1d 100644 --- a/internal/fromproto5/getmetadata_test.go +++ b/internal/fromproto5/getmetadata_test.go @@ -31,8 +31,6 @@ func TestGetMetadataRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/getproviderschema_test.go b/internal/fromproto5/getproviderschema_test.go index a9722d84d..754e68605 100644 --- a/internal/fromproto5/getproviderschema_test.go +++ b/internal/fromproto5/getproviderschema_test.go @@ -31,8 +31,6 @@ func TestGetProviderSchemaRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/importresourcestate_test.go b/internal/fromproto5/importresourcestate_test.go index ba58522af..39b5fdfa4 100644 --- a/internal/fromproto5/importresourcestate_test.go +++ b/internal/fromproto5/importresourcestate_test.go @@ -119,8 +119,6 @@ func TestImportResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/moveresourcestate_test.go b/internal/fromproto5/moveresourcestate_test.go index 3c8dd5181..2122edb75 100644 --- a/internal/fromproto5/moveresourcestate_test.go +++ b/internal/fromproto5/moveresourcestate_test.go @@ -165,8 +165,6 @@ func TestMoveResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/openephemeralresource_test.go b/internal/fromproto5/openephemeralresource_test.go index bd53e32d7..e84a5829c 100644 --- a/internal/fromproto5/openephemeralresource_test.go +++ b/internal/fromproto5/openephemeralresource_test.go @@ -127,8 +127,6 @@ func TestOpenEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/plan_test.go b/internal/fromproto5/plan_test.go index c447a86c9..686280fac 100644 --- a/internal/fromproto5/plan_test.go +++ b/internal/fromproto5/plan_test.go @@ -103,8 +103,6 @@ func TestPlan(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index 64dcad0f8..b223e0e59 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -262,8 +262,6 @@ func TestPlanResourceChangeRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/prepareproviderconfig_test.go b/internal/fromproto5/prepareproviderconfig_test.go index 3b10c7720..fc0f29a01 100644 --- a/internal/fromproto5/prepareproviderconfig_test.go +++ b/internal/fromproto5/prepareproviderconfig_test.go @@ -89,8 +89,6 @@ func TestValidateProviderConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/providermeta_test.go b/internal/fromproto5/providermeta_test.go index e6f1880cb..d7fcba82e 100644 --- a/internal/fromproto5/providermeta_test.go +++ b/internal/fromproto5/providermeta_test.go @@ -99,8 +99,6 @@ func TestProviderMeta(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/readdatasource_test.go b/internal/fromproto5/readdatasource_test.go index cbc054e2d..bcfc169ea 100644 --- a/internal/fromproto5/readdatasource_test.go +++ b/internal/fromproto5/readdatasource_test.go @@ -163,8 +163,6 @@ func TestReadDataSourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index fb19e9041..c58fc01cc 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -197,8 +197,6 @@ func TestReadResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/renewephemeralresource_test.go b/internal/fromproto5/renewephemeralresource_test.go index b61aed2ad..56b63d92a 100644 --- a/internal/fromproto5/renewephemeralresource_test.go +++ b/internal/fromproto5/renewephemeralresource_test.go @@ -82,8 +82,6 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/state_test.go b/internal/fromproto5/state_test.go index 4a374ff24..17a39f250 100644 --- a/internal/fromproto5/state_test.go +++ b/internal/fromproto5/state_test.go @@ -103,8 +103,6 @@ func TestState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/upgraderesourcestate_test.go b/internal/fromproto5/upgraderesourcestate_test.go index 6ecfe55d4..5cbd9aca8 100644 --- a/internal/fromproto5/upgraderesourcestate_test.go +++ b/internal/fromproto5/upgraderesourcestate_test.go @@ -86,8 +86,6 @@ func TestUpgradeResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/validatedatasourceconfig_test.go b/internal/fromproto5/validatedatasourceconfig_test.go index 7becf1f38..417d69945 100644 --- a/internal/fromproto5/validatedatasourceconfig_test.go +++ b/internal/fromproto5/validatedatasourceconfig_test.go @@ -91,8 +91,6 @@ func TestValidateDataSourceConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/validateephemeralresourceconfig_test.go b/internal/fromproto5/validateephemeralresourceconfig_test.go index bf4aec660..b1ec6662a 100644 --- a/internal/fromproto5/validateephemeralresourceconfig_test.go +++ b/internal/fromproto5/validateephemeralresourceconfig_test.go @@ -91,8 +91,6 @@ func TestValidateEphemeralResourceConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto5/validateresourcetypeconfig_test.go b/internal/fromproto5/validateresourcetypeconfig_test.go index 5c07807ed..dbc7dad1b 100644 --- a/internal/fromproto5/validateresourcetypeconfig_test.go +++ b/internal/fromproto5/validateresourcetypeconfig_test.go @@ -125,8 +125,6 @@ func TestValidateResourceTypeConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/applyresourcechange_test.go b/internal/fromproto6/applyresourcechange_test.go index fccbf619a..9f845412e 100644 --- a/internal/fromproto6/applyresourcechange_test.go +++ b/internal/fromproto6/applyresourcechange_test.go @@ -250,8 +250,6 @@ func TestApplyResourceChangeRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/arguments_data_test.go b/internal/fromproto6/arguments_data_test.go index 4a6f7f8b4..ad7a2115a 100644 --- a/internal/fromproto6/arguments_data_test.go +++ b/internal/fromproto6/arguments_data_test.go @@ -690,8 +690,6 @@ func TestArgumentsData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3115,8 +3113,6 @@ func TestArgumentsData_ParameterValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/callfunction_test.go b/internal/fromproto6/callfunction_test.go index e3bbd30fb..dd4c84540 100644 --- a/internal/fromproto6/callfunction_test.go +++ b/internal/fromproto6/callfunction_test.go @@ -81,8 +81,6 @@ func TestCallFunctionRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/closeephemeralresource_test.go b/internal/fromproto6/closeephemeralresource_test.go index dca9046c7..aab1a56a3 100644 --- a/internal/fromproto6/closeephemeralresource_test.go +++ b/internal/fromproto6/closeephemeralresource_test.go @@ -82,8 +82,6 @@ func TestCloseEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/config_test.go b/internal/fromproto6/config_test.go index d48be4755..d0504b6b8 100644 --- a/internal/fromproto6/config_test.go +++ b/internal/fromproto6/config_test.go @@ -103,8 +103,6 @@ func TestConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/configureprovider_test.go b/internal/fromproto6/configureprovider_test.go index 47a3d778a..f7215daae 100644 --- a/internal/fromproto6/configureprovider_test.go +++ b/internal/fromproto6/configureprovider_test.go @@ -118,8 +118,6 @@ func TestConfigureProviderRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/dynamic_value_test.go b/internal/fromproto6/dynamic_value_test.go index eba43543c..c41ac554c 100644 --- a/internal/fromproto6/dynamic_value_test.go +++ b/internal/fromproto6/dynamic_value_test.go @@ -1498,8 +1498,6 @@ func TestDynamicValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/ephemeral_result_data_test.go b/internal/fromproto6/ephemeral_result_data_test.go index 44396ee59..ac48f3f2b 100644 --- a/internal/fromproto6/ephemeral_result_data_test.go +++ b/internal/fromproto6/ephemeral_result_data_test.go @@ -103,8 +103,6 @@ func TestEphemeralResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/getfunctions_test.go b/internal/fromproto6/getfunctions_test.go index 9269a889b..3e62f1150 100644 --- a/internal/fromproto6/getfunctions_test.go +++ b/internal/fromproto6/getfunctions_test.go @@ -31,8 +31,6 @@ func TestGetFunctionsRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/getmetadata_test.go b/internal/fromproto6/getmetadata_test.go index 20944d614..5eed5f7fc 100644 --- a/internal/fromproto6/getmetadata_test.go +++ b/internal/fromproto6/getmetadata_test.go @@ -31,8 +31,6 @@ func TestGetMetadataRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/getproviderschema_test.go b/internal/fromproto6/getproviderschema_test.go index cd4c1bca3..020c96e10 100644 --- a/internal/fromproto6/getproviderschema_test.go +++ b/internal/fromproto6/getproviderschema_test.go @@ -31,8 +31,6 @@ func TestGetProviderSchemaRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/importresourcestate_test.go b/internal/fromproto6/importresourcestate_test.go index 6c72913f7..6b385bb1a 100644 --- a/internal/fromproto6/importresourcestate_test.go +++ b/internal/fromproto6/importresourcestate_test.go @@ -119,8 +119,6 @@ func TestImportResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/moveresourcestate_test.go b/internal/fromproto6/moveresourcestate_test.go index 3379a3b6e..881ab3a49 100644 --- a/internal/fromproto6/moveresourcestate_test.go +++ b/internal/fromproto6/moveresourcestate_test.go @@ -165,8 +165,6 @@ func TestMoveResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/openephemeralresource_test.go b/internal/fromproto6/openephemeralresource_test.go index beea040e4..a6535741e 100644 --- a/internal/fromproto6/openephemeralresource_test.go +++ b/internal/fromproto6/openephemeralresource_test.go @@ -127,8 +127,6 @@ func TestOpenEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/plan_test.go b/internal/fromproto6/plan_test.go index eb58c0caa..4b5ec495b 100644 --- a/internal/fromproto6/plan_test.go +++ b/internal/fromproto6/plan_test.go @@ -103,8 +103,6 @@ func TestPlan(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index 81901c514..af8ee025c 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -262,8 +262,6 @@ func TestPlanResourceChangeRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/providermeta_test.go b/internal/fromproto6/providermeta_test.go index 8b6501975..9e22f02c7 100644 --- a/internal/fromproto6/providermeta_test.go +++ b/internal/fromproto6/providermeta_test.go @@ -99,8 +99,6 @@ func TestProviderMeta(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/readdatasource_test.go b/internal/fromproto6/readdatasource_test.go index 08ef5124a..656d34e6a 100644 --- a/internal/fromproto6/readdatasource_test.go +++ b/internal/fromproto6/readdatasource_test.go @@ -163,8 +163,6 @@ func TestReadDataSourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index 03d6eea86..5ae9e1688 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -197,8 +197,6 @@ func TestReadResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/renewephemeralresource_test.go b/internal/fromproto6/renewephemeralresource_test.go index 02779157f..0460abf1b 100644 --- a/internal/fromproto6/renewephemeralresource_test.go +++ b/internal/fromproto6/renewephemeralresource_test.go @@ -82,8 +82,6 @@ func TestRenewEphemeralResourceRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/state_test.go b/internal/fromproto6/state_test.go index fe6382af4..ec81a9a48 100644 --- a/internal/fromproto6/state_test.go +++ b/internal/fromproto6/state_test.go @@ -103,8 +103,6 @@ func TestState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/upgraderesourcestate_test.go b/internal/fromproto6/upgraderesourcestate_test.go index f1b30efcd..a857a0eea 100644 --- a/internal/fromproto6/upgraderesourcestate_test.go +++ b/internal/fromproto6/upgraderesourcestate_test.go @@ -86,8 +86,6 @@ func TestUpgradeResourceStateRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/validatedatasourceconfig_test.go b/internal/fromproto6/validatedatasourceconfig_test.go index c6dd84466..9bf3404d7 100644 --- a/internal/fromproto6/validatedatasourceconfig_test.go +++ b/internal/fromproto6/validatedatasourceconfig_test.go @@ -91,8 +91,6 @@ func TestValidateDataSourceConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/validateephemeralresourceconfig_test.go b/internal/fromproto6/validateephemeralresourceconfig_test.go index 39d793335..093757252 100644 --- a/internal/fromproto6/validateephemeralresourceconfig_test.go +++ b/internal/fromproto6/validateephemeralresourceconfig_test.go @@ -91,8 +91,6 @@ func TestValidateEphemeralResourceConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/validateproviderconfig_test.go b/internal/fromproto6/validateproviderconfig_test.go index 96abd6dd9..2b8b872bc 100644 --- a/internal/fromproto6/validateproviderconfig_test.go +++ b/internal/fromproto6/validateproviderconfig_test.go @@ -89,8 +89,6 @@ func TestValidateProviderConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromproto6/validateresourceconfig_test.go b/internal/fromproto6/validateresourceconfig_test.go index f649a0d77..b28ea5267 100644 --- a/internal/fromproto6/validateresourceconfig_test.go +++ b/internal/fromproto6/validateresourceconfig_test.go @@ -125,8 +125,6 @@ func TestValidateResourceConfigRequest(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromtftypes/attribute_path_step_test.go b/internal/fromtftypes/attribute_path_step_test.go index 4e4a291fb..93086d738 100644 --- a/internal/fromtftypes/attribute_path_step_test.go +++ b/internal/fromtftypes/attribute_path_step_test.go @@ -57,8 +57,6 @@ func TestAttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromtftypes/attribute_path_test.go b/internal/fromtftypes/attribute_path_test.go index 8166c7be7..e650cfdef 100644 --- a/internal/fromtftypes/attribute_path_test.go +++ b/internal/fromtftypes/attribute_path_test.go @@ -182,8 +182,6 @@ func TestAttributePath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fromtftypes/value_test.go b/internal/fromtftypes/value_test.go index d633c8157..4775e1771 100644 --- a/internal/fromtftypes/value_test.go +++ b/internal/fromtftypes/value_test.go @@ -351,8 +351,6 @@ func TestValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschema/attribute_name_validation_test.go b/internal/fwschema/attribute_name_validation_test.go index accfb52b9..f07d7cfad 100644 --- a/internal/fwschema/attribute_name_validation_test.go +++ b/internal/fwschema/attribute_name_validation_test.go @@ -69,8 +69,6 @@ func TestIsReservedProviderAttributeName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -205,8 +203,6 @@ func TestIsReservedResourceAttributeName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +326,6 @@ func TestIsValidAttributeName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschema/schema_test.go b/internal/fwschema/schema_test.go index 89d2ee2a9..0e54405ee 100644 --- a/internal/fwschema/schema_test.go +++ b/internal/fwschema/schema_test.go @@ -170,8 +170,6 @@ func TestSchemaBlockPathExpressions(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -346,8 +344,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -521,8 +517,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_default_test.go b/internal/fwschemadata/data_default_test.go index d68208439..054021eb3 100644 --- a/internal/fwschemadata/data_default_test.go +++ b/internal/fwschemadata/data_default_test.go @@ -10389,8 +10389,6 @@ func TestDataDefault(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_get_at_path_test.go b/internal/fwschemadata/data_get_at_path_test.go index 4bb5b95b3..b1546f635 100644 --- a/internal/fwschemadata/data_get_at_path_test.go +++ b/internal/fwschemadata/data_get_at_path_test.go @@ -7338,7 +7338,6 @@ func TestDataGetAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_get_test.go b/internal/fwschemadata/data_get_test.go index 6528e985e..78d32a4dc 100644 --- a/internal/fwschemadata/data_get_test.go +++ b/internal/fwschemadata/data_get_test.go @@ -8189,7 +8189,6 @@ func TestDataGet(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_nullify_collection_blocks_test.go b/internal/fwschemadata/data_nullify_collection_blocks_test.go index 9eb2a4c9c..7a2de5f1b 100644 --- a/internal/fwschemadata/data_nullify_collection_blocks_test.go +++ b/internal/fwschemadata/data_nullify_collection_blocks_test.go @@ -950,8 +950,6 @@ func TestDataNullifyCollectionBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_path_exists_test.go b/internal/fwschemadata/data_path_exists_test.go index fd67033f6..b3f685b79 100644 --- a/internal/fwschemadata/data_path_exists_test.go +++ b/internal/fwschemadata/data_path_exists_test.go @@ -799,7 +799,6 @@ func TestDataPathExists(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_path_matches_test.go b/internal/fwschemadata/data_path_matches_test.go index abbe5be3a..b3a307528 100644 --- a/internal/fwschemadata/data_path_matches_test.go +++ b/internal/fwschemadata/data_path_matches_test.go @@ -1469,8 +1469,6 @@ func TestDataPathMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_reify_null_collection_blocks_test.go b/internal/fwschemadata/data_reify_null_collection_blocks_test.go index 1e7161a2f..50d0757a1 100644 --- a/internal/fwschemadata/data_reify_null_collection_blocks_test.go +++ b/internal/fwschemadata/data_reify_null_collection_blocks_test.go @@ -1194,8 +1194,6 @@ func TestDataReifyNullCollectionBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_set_at_path_test.go b/internal/fwschemadata/data_set_at_path_test.go index 13d7e878a..95a41fee9 100644 --- a/internal/fwschemadata/data_set_at_path_test.go +++ b/internal/fwschemadata/data_set_at_path_test.go @@ -3045,7 +3045,6 @@ func TestDataSetAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_set_test.go b/internal/fwschemadata/data_set_test.go index ce827faf9..995923187 100644 --- a/internal/fwschemadata/data_set_test.go +++ b/internal/fwschemadata/data_set_test.go @@ -279,7 +279,6 @@ func TestDataSet(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_valid_path_expression_test.go b/internal/fwschemadata/data_valid_path_expression_test.go index decef1271..13ef1489a 100644 --- a/internal/fwschemadata/data_valid_path_expression_test.go +++ b/internal/fwschemadata/data_valid_path_expression_test.go @@ -284,8 +284,6 @@ func TestDataValidPathExpression(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/data_value_test.go b/internal/fwschemadata/data_value_test.go index 10cdb9afb..a067e19c4 100644 --- a/internal/fwschemadata/data_value_test.go +++ b/internal/fwschemadata/data_value_test.go @@ -2362,7 +2362,6 @@ func TestDataValueAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/tftypes_value_test.go b/internal/fwschemadata/tftypes_value_test.go index 465745523..f9409f4d0 100644 --- a/internal/fwschemadata/tftypes_value_test.go +++ b/internal/fwschemadata/tftypes_value_test.go @@ -154,7 +154,6 @@ func TestCreateParentTerraformValue(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -493,7 +492,6 @@ func TestUpsertChildTerraformValue(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_bool_test.go b/internal/fwschemadata/value_semantic_equality_bool_test.go index 099e6dc68..1468951bf 100644 --- a/internal/fwschemadata/value_semantic_equality_bool_test.go +++ b/internal/fwschemadata/value_semantic_equality_bool_test.go @@ -108,8 +108,6 @@ func TestValueSemanticEqualityBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_dynamic_test.go b/internal/fwschemadata/value_semantic_equality_dynamic_test.go index 183b893c1..2cccd0ab6 100644 --- a/internal/fwschemadata/value_semantic_equality_dynamic_test.go +++ b/internal/fwschemadata/value_semantic_equality_dynamic_test.go @@ -184,8 +184,6 @@ func TestValueSemanticEqualityDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_float32_test.go b/internal/fwschemadata/value_semantic_equality_float32_test.go index 9af96709e..725650d6c 100644 --- a/internal/fwschemadata/value_semantic_equality_float32_test.go +++ b/internal/fwschemadata/value_semantic_equality_float32_test.go @@ -109,8 +109,6 @@ func TestValueSemanticEqualityFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_float64_test.go b/internal/fwschemadata/value_semantic_equality_float64_test.go index 009459118..821485077 100644 --- a/internal/fwschemadata/value_semantic_equality_float64_test.go +++ b/internal/fwschemadata/value_semantic_equality_float64_test.go @@ -108,8 +108,6 @@ func TestValueSemanticEqualityFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_int32_test.go b/internal/fwschemadata/value_semantic_equality_int32_test.go index 4c48b9ae0..ddc2d47a1 100644 --- a/internal/fwschemadata/value_semantic_equality_int32_test.go +++ b/internal/fwschemadata/value_semantic_equality_int32_test.go @@ -109,8 +109,6 @@ func TestValueSemanticEqualityInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_int64_test.go b/internal/fwschemadata/value_semantic_equality_int64_test.go index c8f35f581..f6f29cd4d 100644 --- a/internal/fwschemadata/value_semantic_equality_int64_test.go +++ b/internal/fwschemadata/value_semantic_equality_int64_test.go @@ -108,8 +108,6 @@ func TestValueSemanticEqualityInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_list_test.go b/internal/fwschemadata/value_semantic_equality_list_test.go index 4bb5ba3b6..01220af9e 100644 --- a/internal/fwschemadata/value_semantic_equality_list_test.go +++ b/internal/fwschemadata/value_semantic_equality_list_test.go @@ -638,8 +638,6 @@ func TestValueSemanticEqualityList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_map_test.go b/internal/fwschemadata/value_semantic_equality_map_test.go index f2c33edf0..01e163d2a 100644 --- a/internal/fwschemadata/value_semantic_equality_map_test.go +++ b/internal/fwschemadata/value_semantic_equality_map_test.go @@ -562,8 +562,6 @@ func TestValueSemanticEqualityMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_number_test.go b/internal/fwschemadata/value_semantic_equality_number_test.go index f912e09c4..060b959d1 100644 --- a/internal/fwschemadata/value_semantic_equality_number_test.go +++ b/internal/fwschemadata/value_semantic_equality_number_test.go @@ -109,8 +109,6 @@ func TestValueSemanticEqualityNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_object_test.go b/internal/fwschemadata/value_semantic_equality_object_test.go index c68f55638..fb1b1af84 100644 --- a/internal/fwschemadata/value_semantic_equality_object_test.go +++ b/internal/fwschemadata/value_semantic_equality_object_test.go @@ -658,8 +658,6 @@ func TestValueSemanticEqualityObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_set_test.go b/internal/fwschemadata/value_semantic_equality_set_test.go index d5fa25c85..0108eaddc 100644 --- a/internal/fwschemadata/value_semantic_equality_set_test.go +++ b/internal/fwschemadata/value_semantic_equality_set_test.go @@ -1006,8 +1006,6 @@ func TestValueSemanticEqualitySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_string_test.go b/internal/fwschemadata/value_semantic_equality_string_test.go index d705e9fbd..7e730cbe3 100644 --- a/internal/fwschemadata/value_semantic_equality_string_test.go +++ b/internal/fwschemadata/value_semantic_equality_string_test.go @@ -108,8 +108,6 @@ func TestValueSemanticEqualityString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwschemadata/value_semantic_equality_test.go b/internal/fwschemadata/value_semantic_equality_test.go index 81c85e335..64fe80962 100644 --- a/internal/fwschemadata/value_semantic_equality_test.go +++ b/internal/fwschemadata/value_semantic_equality_test.go @@ -1205,8 +1205,6 @@ func TestValueSemanticEquality(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/attribute_plan_modification_test.go b/internal/fwserver/attribute_plan_modification_test.go index f390311a9..a5c717964 100644 --- a/internal/fwserver/attribute_plan_modification_test.go +++ b/internal/fwserver/attribute_plan_modification_test.go @@ -3288,7 +3288,6 @@ func TestAttributeModifyPlan(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -3929,8 +3928,6 @@ func TestAttributePlanModifyBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -4565,8 +4562,6 @@ func TestAttributePlanModifyFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -5201,8 +5196,6 @@ func TestAttributePlanModifyFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -5837,8 +5830,6 @@ func TestAttributePlanModifyInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -6473,8 +6464,6 @@ func TestAttributePlanModifyInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -7127,8 +7116,6 @@ func TestAttributePlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -8108,8 +8095,6 @@ func TestAttributePlanModifyMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -8744,8 +8729,6 @@ func TestAttributePlanModifyNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -9943,8 +9926,6 @@ func TestAttributePlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -10597,8 +10578,6 @@ func TestAttributePlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -11233,8 +11212,6 @@ func TestAttributePlanModifyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -11869,8 +11846,6 @@ func TestAttributePlanModifyDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -13031,8 +13006,6 @@ func TestNestedAttributeObjectPlanModify(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/attribute_validation_test.go b/internal/fwserver/attribute_validation_test.go index 6f0fbc5ab..21f8349b5 100644 --- a/internal/fwserver/attribute_validation_test.go +++ b/internal/fwserver/attribute_validation_test.go @@ -1972,7 +1972,6 @@ func TestAttributeValidate(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -2211,8 +2210,6 @@ func TestAttributeValidateBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2441,8 +2438,6 @@ func TestAttributeValidateFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2671,8 +2666,6 @@ func TestAttributeValidateFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2901,8 +2894,6 @@ func TestAttributeValidateInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3131,8 +3122,6 @@ func TestAttributeValidateInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3376,8 +3365,6 @@ func TestAttributeValidateList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3642,8 +3629,6 @@ func TestAttributeValidateMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -3872,8 +3857,6 @@ func TestAttributeValidateNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -4148,8 +4131,6 @@ func TestAttributeValidateObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -4393,8 +4374,6 @@ func TestAttributeValidateSet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -4623,8 +4602,6 @@ func TestAttributeValidateString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -4853,8 +4830,6 @@ func TestAttributeValidateDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -5133,8 +5108,6 @@ func TestNestedAttributeObjectValidateObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/block_plan_modification_test.go b/internal/fwserver/block_plan_modification_test.go index 323ca8709..53f18579f 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -4024,7 +4024,6 @@ func TestBlockModifyPlan(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -4685,8 +4684,6 @@ func TestBlockPlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -5884,8 +5881,6 @@ func TestBlockPlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -6538,8 +6533,6 @@ func TestBlockPlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -7755,8 +7748,6 @@ func TestNestedBlockObjectPlanModify(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/block_validation_test.go b/internal/fwserver/block_validation_test.go index 40726f22f..2c6f3981d 100644 --- a/internal/fwserver/block_validation_test.go +++ b/internal/fwserver/block_validation_test.go @@ -1180,7 +1180,6 @@ func TestBlockValidate(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -1555,8 +1554,6 @@ func TestBlockValidateList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1849,8 +1846,6 @@ func TestBlockValidateObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2218,8 +2213,6 @@ func TestBlockValidateSet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2585,8 +2578,6 @@ func TestNestedBlockObjectValidateObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/schema_plan_modification_test.go b/internal/fwserver/schema_plan_modification_test.go index 3e00223ec..a3bc0ab5f 100644 --- a/internal/fwserver/schema_plan_modification_test.go +++ b/internal/fwserver/schema_plan_modification_test.go @@ -2425,7 +2425,6 @@ func TestSchemaModifyPlan(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/schema_semantic_equality_test.go b/internal/fwserver/schema_semantic_equality_test.go index b9ab45d92..65018998f 100644 --- a/internal/fwserver/schema_semantic_equality_test.go +++ b/internal/fwserver/schema_semantic_equality_test.go @@ -2300,8 +2300,6 @@ func TestSchemaSemanticEquality(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/schema_validation_test.go b/internal/fwserver/schema_validation_test.go index f896c3e08..81b0d4d37 100644 --- a/internal/fwserver/schema_validation_test.go +++ b/internal/fwserver/schema_validation_test.go @@ -172,7 +172,6 @@ func TestSchemaValidate(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_applyresourcechange_test.go b/internal/fwserver/server_applyresourcechange_test.go index 41ab43c77..ea4452244 100644 --- a/internal/fwserver/server_applyresourcechange_test.go +++ b/internal/fwserver/server_applyresourcechange_test.go @@ -1478,8 +1478,6 @@ func TestServerApplyResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_callfunction_test.go b/internal/fwserver/server_callfunction_test.go index dfb3ee1cb..e867c9069 100644 --- a/internal/fwserver/server_callfunction_test.go +++ b/internal/fwserver/server_callfunction_test.go @@ -380,8 +380,6 @@ func TestServerCallFunction(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_closeephemeralresource_test.go b/internal/fwserver/server_closeephemeralresource_test.go index 4bad0e620..3ffc19c86 100644 --- a/internal/fwserver/server_closeephemeralresource_test.go +++ b/internal/fwserver/server_closeephemeralresource_test.go @@ -185,8 +185,6 @@ func TestServerCloseEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_configureprovider_test.go b/internal/fwserver/server_configureprovider_test.go index f8dd1e9e0..96d5ac63a 100644 --- a/internal/fwserver/server_configureprovider_test.go +++ b/internal/fwserver/server_configureprovider_test.go @@ -230,8 +230,6 @@ func TestServerConfigureProvider(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_createresource_test.go b/internal/fwserver/server_createresource_test.go index 988a268c9..86bedcb8f 100644 --- a/internal/fwserver/server_createresource_test.go +++ b/internal/fwserver/server_createresource_test.go @@ -605,8 +605,6 @@ func TestServerCreateResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_deleteresource_test.go b/internal/fwserver/server_deleteresource_test.go index 95024d9a1..2042e2764 100644 --- a/internal/fwserver/server_deleteresource_test.go +++ b/internal/fwserver/server_deleteresource_test.go @@ -420,8 +420,6 @@ func TestServerDeleteResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_getfunctions_test.go b/internal/fwserver/server_getfunctions_test.go index 1df383710..1713183c6 100644 --- a/internal/fwserver/server_getfunctions_test.go +++ b/internal/fwserver/server_getfunctions_test.go @@ -199,8 +199,6 @@ func TestServerGetFunctions(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_getmetadata_test.go b/internal/fwserver/server_getmetadata_test.go index 532765e1a..8729f6dbd 100644 --- a/internal/fwserver/server_getmetadata_test.go +++ b/internal/fwserver/server_getmetadata_test.go @@ -647,8 +647,6 @@ func TestServerGetMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_getproviderschema_test.go b/internal/fwserver/server_getproviderschema_test.go index 43a0c9a4e..e5e17bc89 100644 --- a/internal/fwserver/server_getproviderschema_test.go +++ b/internal/fwserver/server_getproviderschema_test.go @@ -1223,8 +1223,6 @@ func TestServerGetProviderSchema(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index d605ef3d9..0f8481eea 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -486,8 +486,6 @@ func TestServerImportResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_moveresourcestate_test.go b/internal/fwserver/server_moveresourcestate_test.go index 931b84a76..8e2445ba9 100644 --- a/internal/fwserver/server_moveresourcestate_test.go +++ b/internal/fwserver/server_moveresourcestate_test.go @@ -815,8 +815,6 @@ func TestServerMoveResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_openephemeralresource_test.go b/internal/fwserver/server_openephemeralresource_test.go index ec2649430..9f0365a92 100644 --- a/internal/fwserver/server_openephemeralresource_test.go +++ b/internal/fwserver/server_openephemeralresource_test.go @@ -381,8 +381,6 @@ func TestServerOpenEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 4e22b36c6..783fb2e51 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -705,7 +705,6 @@ func TestNormaliseRequiresReplace(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -6643,8 +6642,6 @@ func TestServerPlanResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -16258,8 +16255,6 @@ func TestServerPlanResourceChange_AttributeRoundtrip(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index d2f91bd37..45334d43e 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -453,8 +453,6 @@ func TestServerReadDataSource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index f6711551a..9f3aa7065 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -665,8 +665,6 @@ func TestServerReadResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_renewephemeralresource_test.go b/internal/fwserver/server_renewephemeralresource_test.go index 368a5aa87..8cc6e01a6 100644 --- a/internal/fwserver/server_renewephemeralresource_test.go +++ b/internal/fwserver/server_renewephemeralresource_test.go @@ -262,8 +262,6 @@ func TestServerRenewEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_updateresource_test.go b/internal/fwserver/server_updateresource_test.go index 5c0bb5c7d..4396e93f1 100644 --- a/internal/fwserver/server_updateresource_test.go +++ b/internal/fwserver/server_updateresource_test.go @@ -916,8 +916,6 @@ func TestServerUpdateResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_upgraderesourcestate_test.go b/internal/fwserver/server_upgraderesourcestate_test.go index 1e733874f..00ee46092 100644 --- a/internal/fwserver/server_upgraderesourcestate_test.go +++ b/internal/fwserver/server_upgraderesourcestate_test.go @@ -913,8 +913,6 @@ func TestServerUpgradeResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_validatedatasourceconfig_test.go b/internal/fwserver/server_validatedatasourceconfig_test.go index 6ff93062a..3b669dfcb 100644 --- a/internal/fwserver/server_validatedatasourceconfig_test.go +++ b/internal/fwserver/server_validatedatasourceconfig_test.go @@ -292,8 +292,6 @@ func TestServerValidateDataSourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_validateephemeralresourceconfig_test.go b/internal/fwserver/server_validateephemeralresourceconfig_test.go index 24cb08e70..0497cd7d1 100644 --- a/internal/fwserver/server_validateephemeralresourceconfig_test.go +++ b/internal/fwserver/server_validateephemeralresourceconfig_test.go @@ -292,8 +292,6 @@ func TestServerValidateEphemeralResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_validateproviderconfig_test.go b/internal/fwserver/server_validateproviderconfig_test.go index 49574e60c..b2d71483d 100644 --- a/internal/fwserver/server_validateproviderconfig_test.go +++ b/internal/fwserver/server_validateproviderconfig_test.go @@ -298,8 +298,6 @@ func TestServerValidateProviderConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_validateresourceconfig_test.go b/internal/fwserver/server_validateresourceconfig_test.go index 489d6a234..59369e9df 100644 --- a/internal/fwserver/server_validateresourceconfig_test.go +++ b/internal/fwserver/server_validateresourceconfig_test.go @@ -384,8 +384,6 @@ func TestServerValidateResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwtype/missing_underlying_type_validation_test.go b/internal/fwtype/missing_underlying_type_validation_test.go index dea491b11..e8b5d3adb 100644 --- a/internal/fwtype/missing_underlying_type_validation_test.go +++ b/internal/fwtype/missing_underlying_type_validation_test.go @@ -2812,8 +2812,6 @@ func TestContainsMissingUnderlyingType(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/fwtype/static_collection_validation_test.go b/internal/fwtype/static_collection_validation_test.go index e7ea5e722..8000bc91a 100644 --- a/internal/fwtype/static_collection_validation_test.go +++ b/internal/fwtype/static_collection_validation_test.go @@ -933,7 +933,6 @@ func TestTypeContainsCollectionWithDynamic(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/privatestate/data_test.go b/internal/privatestate/data_test.go index ae40c2f8f..d1bce0f32 100644 --- a/internal/privatestate/data_test.go +++ b/internal/privatestate/data_test.go @@ -200,8 +200,6 @@ func TestData_Bytes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -431,8 +429,6 @@ func TestNewData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -538,8 +534,6 @@ func TestNewProviderData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -631,8 +625,6 @@ func TestProviderDataEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -693,8 +685,6 @@ func TestProviderData_GetKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -938,8 +928,6 @@ func TestProviderData_SetKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -977,8 +965,6 @@ func TestValidateProviderDataKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_applyresourcechange_test.go b/internal/proto5server/server_applyresourcechange_test.go index 92e0a1b24..54484786c 100644 --- a/internal/proto5server/server_applyresourcechange_test.go +++ b/internal/proto5server/server_applyresourcechange_test.go @@ -1416,8 +1416,6 @@ func TestServerApplyResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_callfunction_test.go b/internal/proto5server/server_callfunction_test.go index 17f119267..632e18f5a 100644 --- a/internal/proto5server/server_callfunction_test.go +++ b/internal/proto5server/server_callfunction_test.go @@ -235,8 +235,6 @@ func TestServerCallFunction(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_closeephemeralresource_test.go b/internal/proto5server/server_closeephemeralresource_test.go index 46d987d1d..6f575786f 100644 --- a/internal/proto5server/server_closeephemeralresource_test.go +++ b/internal/proto5server/server_closeephemeralresource_test.go @@ -112,8 +112,6 @@ func TestServerCloseEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_configureprovider_test.go b/internal/proto5server/server_configureprovider_test.go index f538af6b9..06d4df619 100644 --- a/internal/proto5server/server_configureprovider_test.go +++ b/internal/proto5server/server_configureprovider_test.go @@ -153,8 +153,6 @@ func TestServerConfigureProvider(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_getfunctions_test.go b/internal/proto5server/server_getfunctions_test.go index 07060e83f..ad8e4a04e 100644 --- a/internal/proto5server/server_getfunctions_test.go +++ b/internal/proto5server/server_getfunctions_test.go @@ -160,8 +160,6 @@ func TestServerGetFunctions(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_getmetadata_test.go b/internal/proto5server/server_getmetadata_test.go index 39d2985bd..9f8baf1fc 100644 --- a/internal/proto5server/server_getmetadata_test.go +++ b/internal/proto5server/server_getmetadata_test.go @@ -554,8 +554,6 @@ func TestServerGetMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_getproviderschema_test.go b/internal/proto5server/server_getproviderschema_test.go index 41a632391..590d87a62 100644 --- a/internal/proto5server/server_getproviderschema_test.go +++ b/internal/proto5server/server_getproviderschema_test.go @@ -865,8 +865,6 @@ func TestServerGetProviderSchema(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_importresourcestate_test.go b/internal/proto5server/server_importresourcestate_test.go index 8b6b3578e..268853c7d 100644 --- a/internal/proto5server/server_importresourcestate_test.go +++ b/internal/proto5server/server_importresourcestate_test.go @@ -233,8 +233,6 @@ func TestServerImportResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_moveresourcestate_test.go b/internal/proto5server/server_moveresourcestate_test.go index b558b573c..5b5e53c97 100644 --- a/internal/proto5server/server_moveresourcestate_test.go +++ b/internal/proto5server/server_moveresourcestate_test.go @@ -708,8 +708,6 @@ func TestServerMoveResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_openephemeralresource_test.go b/internal/proto5server/server_openephemeralresource_test.go index a4e7d2113..85e8e911e 100644 --- a/internal/proto5server/server_openephemeralresource_test.go +++ b/internal/proto5server/server_openephemeralresource_test.go @@ -249,8 +249,6 @@ func TestServerOpenEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_planresourcechange_test.go b/internal/proto5server/server_planresourcechange_test.go index ad16683fb..fc452f6af 100644 --- a/internal/proto5server/server_planresourcechange_test.go +++ b/internal/proto5server/server_planresourcechange_test.go @@ -1039,8 +1039,6 @@ func TestServerPlanResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_prepareproviderconfig_test.go b/internal/proto5server/server_prepareproviderconfig_test.go index 65ae74a28..945171a80 100644 --- a/internal/proto5server/server_prepareproviderconfig_test.go +++ b/internal/proto5server/server_prepareproviderconfig_test.go @@ -124,8 +124,6 @@ func TestServerPrepareProviderConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_readdatasource_test.go b/internal/proto5server/server_readdatasource_test.go index 538786bad..49bbe21af 100644 --- a/internal/proto5server/server_readdatasource_test.go +++ b/internal/proto5server/server_readdatasource_test.go @@ -286,8 +286,6 @@ func TestServerReadDataSource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_readresource_test.go b/internal/proto5server/server_readresource_test.go index 12a2604f3..8fbba4647 100644 --- a/internal/proto5server/server_readresource_test.go +++ b/internal/proto5server/server_readresource_test.go @@ -402,8 +402,6 @@ func TestServerReadResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_renewephemeralresource_test.go b/internal/proto5server/server_renewephemeralresource_test.go index 73a63f43b..651a7fc61 100644 --- a/internal/proto5server/server_renewephemeralresource_test.go +++ b/internal/proto5server/server_renewephemeralresource_test.go @@ -146,8 +146,6 @@ func TestServerRenewEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_upgraderesourcestate_test.go b/internal/proto5server/server_upgraderesourcestate_test.go index 727091014..6d391f831 100644 --- a/internal/proto5server/server_upgraderesourcestate_test.go +++ b/internal/proto5server/server_upgraderesourcestate_test.go @@ -267,8 +267,6 @@ func TestServerUpgradeResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_validatedatasourceconfig_test.go b/internal/proto5server/server_validatedatasourceconfig_test.go index 2166ec7ca..0eb8f5ad7 100644 --- a/internal/proto5server/server_validatedatasourceconfig_test.go +++ b/internal/proto5server/server_validatedatasourceconfig_test.go @@ -151,8 +151,6 @@ func TestServerValidateDataSourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_validateephemeralresourceconfig_test.go b/internal/proto5server/server_validateephemeralresourceconfig_test.go index 6505bbf53..7912087e4 100644 --- a/internal/proto5server/server_validateephemeralresourceconfig_test.go +++ b/internal/proto5server/server_validateephemeralresourceconfig_test.go @@ -149,8 +149,6 @@ func TestServerValidateEphemeralResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto5server/server_validateresourcetypeconfig_test.go b/internal/proto5server/server_validateresourcetypeconfig_test.go index 2799a82c1..0a647cfc2 100644 --- a/internal/proto5server/server_validateresourcetypeconfig_test.go +++ b/internal/proto5server/server_validateresourcetypeconfig_test.go @@ -149,8 +149,6 @@ func TestServerValidateResourceTypeConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_applyresourcechange_test.go b/internal/proto6server/server_applyresourcechange_test.go index 463371cb7..66c12933d 100644 --- a/internal/proto6server/server_applyresourcechange_test.go +++ b/internal/proto6server/server_applyresourcechange_test.go @@ -1416,8 +1416,6 @@ func TestServerApplyResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_callfunction_test.go b/internal/proto6server/server_callfunction_test.go index e4710ff26..15cfe6170 100644 --- a/internal/proto6server/server_callfunction_test.go +++ b/internal/proto6server/server_callfunction_test.go @@ -234,8 +234,6 @@ func TestServerCallFunction(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_closeephemeralresource_test.go b/internal/proto6server/server_closeephemeralresource_test.go index f1c732bcc..795343de1 100644 --- a/internal/proto6server/server_closeephemeralresource_test.go +++ b/internal/proto6server/server_closeephemeralresource_test.go @@ -112,8 +112,6 @@ func TestServerCloseEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_configureprovider_test.go b/internal/proto6server/server_configureprovider_test.go index 13ae83371..a3369846f 100644 --- a/internal/proto6server/server_configureprovider_test.go +++ b/internal/proto6server/server_configureprovider_test.go @@ -153,8 +153,6 @@ func TestServerConfigureProvider(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_getfunctions_test.go b/internal/proto6server/server_getfunctions_test.go index f503f7ef3..b15fc925b 100644 --- a/internal/proto6server/server_getfunctions_test.go +++ b/internal/proto6server/server_getfunctions_test.go @@ -160,8 +160,6 @@ func TestServerGetFunctions(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_getmetadata_test.go b/internal/proto6server/server_getmetadata_test.go index 5a2395f7e..b32933a65 100644 --- a/internal/proto6server/server_getmetadata_test.go +++ b/internal/proto6server/server_getmetadata_test.go @@ -554,8 +554,6 @@ func TestServerGetMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_getproviderschema_test.go b/internal/proto6server/server_getproviderschema_test.go index 24337c998..0b60159de 100644 --- a/internal/proto6server/server_getproviderschema_test.go +++ b/internal/proto6server/server_getproviderschema_test.go @@ -865,8 +865,6 @@ func TestServerGetProviderSchema(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_importresourcestate_test.go b/internal/proto6server/server_importresourcestate_test.go index 024756df2..8c67646f0 100644 --- a/internal/proto6server/server_importresourcestate_test.go +++ b/internal/proto6server/server_importresourcestate_test.go @@ -233,8 +233,6 @@ func TestServerImportResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_moveresourcestate_test.go b/internal/proto6server/server_moveresourcestate_test.go index 832bcc89d..22cf82154 100644 --- a/internal/proto6server/server_moveresourcestate_test.go +++ b/internal/proto6server/server_moveresourcestate_test.go @@ -708,8 +708,6 @@ func TestServerMoveResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_openephemeralresource_test.go b/internal/proto6server/server_openephemeralresource_test.go index 18167e499..718dd4d3b 100644 --- a/internal/proto6server/server_openephemeralresource_test.go +++ b/internal/proto6server/server_openephemeralresource_test.go @@ -249,8 +249,6 @@ func TestServerOpenEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_planresourcechange_test.go b/internal/proto6server/server_planresourcechange_test.go index 77bd54226..35b39c2e0 100644 --- a/internal/proto6server/server_planresourcechange_test.go +++ b/internal/proto6server/server_planresourcechange_test.go @@ -1038,8 +1038,6 @@ func TestServerPlanResourceChange(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_readdatasource_test.go b/internal/proto6server/server_readdatasource_test.go index 98a9d20cc..9808a58eb 100644 --- a/internal/proto6server/server_readdatasource_test.go +++ b/internal/proto6server/server_readdatasource_test.go @@ -286,8 +286,6 @@ func TestServerReadDataSource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_readresource_test.go b/internal/proto6server/server_readresource_test.go index 140b4be7b..7e2180008 100644 --- a/internal/proto6server/server_readresource_test.go +++ b/internal/proto6server/server_readresource_test.go @@ -402,8 +402,6 @@ func TestServerReadResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_renewephemeralresource_test.go b/internal/proto6server/server_renewephemeralresource_test.go index 2dfe3905b..1ed95d505 100644 --- a/internal/proto6server/server_renewephemeralresource_test.go +++ b/internal/proto6server/server_renewephemeralresource_test.go @@ -146,8 +146,6 @@ func TestServerRenewEphemeralResource(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_upgraderesourcestate_test.go b/internal/proto6server/server_upgraderesourcestate_test.go index f56758075..bcc4ac145 100644 --- a/internal/proto6server/server_upgraderesourcestate_test.go +++ b/internal/proto6server/server_upgraderesourcestate_test.go @@ -267,8 +267,6 @@ func TestServerUpgradeResourceState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_validatedataresourceconfig_test.go b/internal/proto6server/server_validatedataresourceconfig_test.go index 9da96df87..163191dee 100644 --- a/internal/proto6server/server_validatedataresourceconfig_test.go +++ b/internal/proto6server/server_validatedataresourceconfig_test.go @@ -151,8 +151,6 @@ func TestServerValidateDataResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_validateephemeralresourceconfig_test.go b/internal/proto6server/server_validateephemeralresourceconfig_test.go index 9e1932143..b6ca07e44 100644 --- a/internal/proto6server/server_validateephemeralresourceconfig_test.go +++ b/internal/proto6server/server_validateephemeralresourceconfig_test.go @@ -149,8 +149,6 @@ func TestServerValidateEphemeralResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_validateproviderconfig_test.go b/internal/proto6server/server_validateproviderconfig_test.go index 5d7cc7df6..216261867 100644 --- a/internal/proto6server/server_validateproviderconfig_test.go +++ b/internal/proto6server/server_validateproviderconfig_test.go @@ -124,8 +124,6 @@ func TestServerValidateProviderConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/proto6server/server_validateresourceconfig_test.go b/internal/proto6server/server_validateresourceconfig_test.go index 36ea3f91c..e094dd05a 100644 --- a/internal/proto6server/server_validateresourceconfig_test.go +++ b/internal/proto6server/server_validateresourceconfig_test.go @@ -150,8 +150,6 @@ func TestServerValidateResourceConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/build_value_test.go b/internal/reflect/build_value_test.go index 44d8b8f01..c943e6627 100644 --- a/internal/reflect/build_value_test.go +++ b/internal/reflect/build_value_test.go @@ -51,7 +51,6 @@ func TestBuildValue(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/helpers_test.go b/internal/reflect/helpers_test.go index ce6f35763..fcd9ee9be 100644 --- a/internal/reflect/helpers_test.go +++ b/internal/reflect/helpers_test.go @@ -237,7 +237,6 @@ func TestGetStructTags(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() @@ -336,7 +335,6 @@ func TestCommaSeparatedString(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() got := commaSeparatedString(test.input) @@ -360,7 +358,6 @@ func TestIsValidFieldName(t *testing.T) { "a_b": true, } for in, expected := range tests { - in, expected := in, expected t.Run(fmt.Sprintf("input=%q", in), func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/interfaces_test.go b/internal/reflect/interfaces_test.go index ae1022c59..c56f1ce96 100644 --- a/internal/reflect/interfaces_test.go +++ b/internal/reflect/interfaces_test.go @@ -260,7 +260,6 @@ func TestNewUnknownable(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -348,7 +347,6 @@ func TestFromUnknownable(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -400,7 +398,6 @@ func TestNewNullable(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -488,7 +485,6 @@ func TestFromNullable(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -550,7 +546,6 @@ func TestNewAttributeValue(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -864,7 +859,6 @@ func TestFromAttributeValue(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -919,7 +913,6 @@ func TestNewValueConverter(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -996,7 +989,6 @@ func TestFromValueCreator(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/into_test.go b/internal/reflect/into_test.go index 15e2b1048..151a8b14a 100644 --- a/internal/reflect/into_test.go +++ b/internal/reflect/into_test.go @@ -211,8 +211,6 @@ func TestInto_Slices(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/map_test.go b/internal/reflect/map_test.go index 4d57b451f..fc680841b 100644 --- a/internal/reflect/map_test.go +++ b/internal/reflect/map_test.go @@ -244,7 +244,6 @@ func TestFromMap(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/number_test.go b/internal/reflect/number_test.go index a7b8b49e2..97b6b3587 100644 --- a/internal/reflect/number_test.go +++ b/internal/reflect/number_test.go @@ -831,7 +831,6 @@ func TestFromInt(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() actualVal, diags := refl.FromInt(context.Background(), tc.typ, tc.val, path.Empty()) @@ -907,7 +906,6 @@ func TestFromUint(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() actualVal, diags := refl.FromUint(context.Background(), tc.typ, tc.val, path.Empty()) @@ -988,7 +986,6 @@ func TestFromFloat(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() actualVal, diags := refl.FromFloat(context.Background(), tc.typ, tc.val, path.Empty()) @@ -1069,7 +1066,6 @@ func TestFromBigFloat(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() actualVal, diags := refl.FromBigFloat(context.Background(), tc.typ, tc.val, path.Empty()) @@ -1145,7 +1141,6 @@ func TestFromBigInt(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() actualVal, diags := refl.FromBigInt(context.Background(), tc.typ, tc.val, path.Empty()) diff --git a/internal/reflect/outof_test.go b/internal/reflect/outof_test.go index ab618f7d0..0a24e5e7d 100644 --- a/internal/reflect/outof_test.go +++ b/internal/reflect/outof_test.go @@ -167,8 +167,6 @@ func TestFromValue_go_types(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/pointer_test.go b/internal/reflect/pointer_test.go index fdc1ddd71..e254c0fcd 100644 --- a/internal/reflect/pointer_test.go +++ b/internal/reflect/pointer_test.go @@ -160,7 +160,6 @@ func TestFromPointer(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/primitive_test.go b/internal/reflect/primitive_test.go index 729b42749..58074e2b1 100644 --- a/internal/reflect/primitive_test.go +++ b/internal/reflect/primitive_test.go @@ -136,7 +136,6 @@ func TestFromString(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -213,7 +212,6 @@ func TestFromBool(t *testing.T) { } for name, tc := range cases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/slice_test.go b/internal/reflect/slice_test.go index 41a8daa09..05e5fc2f4 100644 --- a/internal/reflect/slice_test.go +++ b/internal/reflect/slice_test.go @@ -388,7 +388,6 @@ func TestFromSlice(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/reflect/struct_test.go b/internal/reflect/struct_test.go index a79df6e24..fb382a180 100644 --- a/internal/reflect/struct_test.go +++ b/internal/reflect/struct_test.go @@ -395,8 +395,6 @@ func TestNewStruct_errors(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -2122,8 +2120,6 @@ func TestFromStruct_errors(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/applyresourcechange_test.go b/internal/toproto5/applyresourcechange_test.go index 64c84f7cc..85f8c99dd 100644 --- a/internal/toproto5/applyresourcechange_test.go +++ b/internal/toproto5/applyresourcechange_test.go @@ -158,8 +158,6 @@ func TestApplyResourceChangeResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/block_test.go b/internal/toproto5/block_test.go index 4618d862c..303474e56 100644 --- a/internal/toproto5/block_test.go +++ b/internal/toproto5/block_test.go @@ -551,7 +551,6 @@ func TestBlock(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/callfunction_test.go b/internal/toproto5/callfunction_test.go index 91c26725b..7c7f49a36 100644 --- a/internal/toproto5/callfunction_test.go +++ b/internal/toproto5/callfunction_test.go @@ -53,8 +53,6 @@ func TestCallFunctionResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/closeephemeralresource_test.go b/internal/toproto5/closeephemeralresource_test.go index f10ab23b1..12b2e1171 100644 --- a/internal/toproto5/closeephemeralresource_test.go +++ b/internal/toproto5/closeephemeralresource_test.go @@ -54,8 +54,6 @@ func TestCloseEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/config_test.go b/internal/toproto5/config_test.go index 5cff0d303..2574bcf6e 100644 --- a/internal/toproto5/config_test.go +++ b/internal/toproto5/config_test.go @@ -90,8 +90,6 @@ func TestConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/configureprovider_test.go b/internal/toproto5/configureprovider_test.go index 5ecdf930f..2d7a20d68 100644 --- a/internal/toproto5/configureprovider_test.go +++ b/internal/toproto5/configureprovider_test.go @@ -54,8 +54,6 @@ func TestConfigureProviderResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/datasourcemetadata_test.go b/internal/toproto5/datasourcemetadata_test.go index 882de7469..10324596e 100644 --- a/internal/toproto5/datasourcemetadata_test.go +++ b/internal/toproto5/datasourcemetadata_test.go @@ -31,8 +31,6 @@ func TestDataSourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/diagnostics_test.go b/internal/toproto5/diagnostics_test.go index 7e888cb19..f10c2c641 100644 --- a/internal/toproto5/diagnostics_test.go +++ b/internal/toproto5/diagnostics_test.go @@ -38,8 +38,6 @@ func TestDiagnosticSeverity(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -116,7 +114,6 @@ func TestDiagnostics(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/dynamic_value_test.go b/internal/toproto5/dynamic_value_test.go index 613a377f2..ee9dd1f69 100644 --- a/internal/toproto5/dynamic_value_test.go +++ b/internal/toproto5/dynamic_value_test.go @@ -1230,8 +1230,6 @@ func TestDynamicValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/ephemeral_result_data_test.go b/internal/toproto5/ephemeral_result_data_test.go index bcb082b21..4a303f016 100644 --- a/internal/toproto5/ephemeral_result_data_test.go +++ b/internal/toproto5/ephemeral_result_data_test.go @@ -90,8 +90,6 @@ func TestEphemeralResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/ephemeralresourcemetadata_test.go b/internal/toproto5/ephemeralresourcemetadata_test.go index 2e3e13731..81f6566d7 100644 --- a/internal/toproto5/ephemeralresourcemetadata_test.go +++ b/internal/toproto5/ephemeralresourcemetadata_test.go @@ -31,8 +31,6 @@ func TestEphemeralResourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/function_test.go b/internal/toproto5/function_test.go index 8f941c805..f29d0a9bb 100644 --- a/internal/toproto5/function_test.go +++ b/internal/toproto5/function_test.go @@ -215,8 +215,6 @@ func TestFunction(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +245,6 @@ func TestFunctionMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -465,8 +461,6 @@ func TestFunctionParameter(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -499,8 +493,6 @@ func TestFunctionReturn(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -539,8 +531,6 @@ func TestFunctionResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/getfunctions_test.go b/internal/toproto5/getfunctions_test.go index f90ed5a7b..844c03a38 100644 --- a/internal/toproto5/getfunctions_test.go +++ b/internal/toproto5/getfunctions_test.go @@ -221,8 +221,6 @@ func TestGetFunctionsResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/getmetadata_test.go b/internal/toproto5/getmetadata_test.go index f328d387e..a2d0c2bdc 100644 --- a/internal/toproto5/getmetadata_test.go +++ b/internal/toproto5/getmetadata_test.go @@ -173,8 +173,6 @@ func TestGetMetadataResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/getproviderschema_test.go b/internal/toproto5/getproviderschema_test.go index 104814f5d..ca293fa96 100644 --- a/internal/toproto5/getproviderschema_test.go +++ b/internal/toproto5/getproviderschema_test.go @@ -5010,8 +5010,6 @@ func TestGetProviderSchemaResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/importedresource_test.go b/internal/toproto5/importedresource_test.go index a1e9e4146..58a1e860b 100644 --- a/internal/toproto5/importedresource_test.go +++ b/internal/toproto5/importedresource_test.go @@ -199,8 +199,6 @@ func TestImportResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/moveresourcestate_test.go b/internal/toproto5/moveresourcestate_test.go index 76712ee64..36075c0bf 100644 --- a/internal/toproto5/moveresourcestate_test.go +++ b/internal/toproto5/moveresourcestate_test.go @@ -155,8 +155,6 @@ func TestMoveResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/openephemeralresource_test.go b/internal/toproto5/openephemeralresource_test.go index 582a1f90e..fe6cf04c4 100644 --- a/internal/toproto5/openephemeralresource_test.go +++ b/internal/toproto5/openephemeralresource_test.go @@ -199,8 +199,6 @@ func TestOpenEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index d4ed9e674..05924b5b4 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -200,8 +200,6 @@ func TestPlanResourceChangeResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/prepareproviderconfig_test.go b/internal/toproto5/prepareproviderconfig_test.go index 46f5ea451..67668e5e4 100644 --- a/internal/toproto5/prepareproviderconfig_test.go +++ b/internal/toproto5/prepareproviderconfig_test.go @@ -134,8 +134,6 @@ func TestPrepareProviderConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/readdatasource_test.go b/internal/toproto5/readdatasource_test.go index bc39fd51a..8801eefe9 100644 --- a/internal/toproto5/readdatasource_test.go +++ b/internal/toproto5/readdatasource_test.go @@ -152,8 +152,6 @@ func TestReadDataSourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index 582e7090a..2e9c549d3 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -187,8 +187,6 @@ func TestReadResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/renewephemeralresource_test.go b/internal/toproto5/renewephemeralresource_test.go index 975e297c8..a1c55bf03 100644 --- a/internal/toproto5/renewephemeralresource_test.go +++ b/internal/toproto5/renewephemeralresource_test.go @@ -102,8 +102,6 @@ func TestRenewEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/resourcemetadata_test.go b/internal/toproto5/resourcemetadata_test.go index d8572f227..a8a982977 100644 --- a/internal/toproto5/resourcemetadata_test.go +++ b/internal/toproto5/resourcemetadata_test.go @@ -31,8 +31,6 @@ func TestResourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/schema_attribute_test.go b/internal/toproto5/schema_attribute_test.go index 9b49ca736..cc52ebd92 100644 --- a/internal/toproto5/schema_attribute_test.go +++ b/internal/toproto5/schema_attribute_test.go @@ -369,7 +369,6 @@ func TestSchemaAttribute(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/schema_test.go b/internal/toproto5/schema_test.go index 30a0e82f1..587f0424c 100644 --- a/internal/toproto5/schema_test.go +++ b/internal/toproto5/schema_test.go @@ -523,7 +523,6 @@ func TestSchema(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/server_capabilities_test.go b/internal/toproto5/server_capabilities_test.go index 95eac4af6..bf78fd7d4 100644 --- a/internal/toproto5/server_capabilities_test.go +++ b/internal/toproto5/server_capabilities_test.go @@ -51,8 +51,6 @@ func TestServerCapabilities(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/state_test.go b/internal/toproto5/state_test.go index b9b514308..355ca35ce 100644 --- a/internal/toproto5/state_test.go +++ b/internal/toproto5/state_test.go @@ -90,8 +90,6 @@ func TestState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/upgraderesourcestate_test.go b/internal/toproto5/upgraderesourcestate_test.go index 42ff28515..d29b2f395 100644 --- a/internal/toproto5/upgraderesourcestate_test.go +++ b/internal/toproto5/upgraderesourcestate_test.go @@ -134,8 +134,6 @@ func TestUpgradeResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/validatedatasourceconfig_test.go b/internal/toproto5/validatedatasourceconfig_test.go index fe9001f84..7e0514a6f 100644 --- a/internal/toproto5/validatedatasourceconfig_test.go +++ b/internal/toproto5/validatedatasourceconfig_test.go @@ -54,8 +54,6 @@ func TestValidateDataSourceConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/validateephemeralresourceconfig_test.go b/internal/toproto5/validateephemeralresourceconfig_test.go index 0a2a939f3..a3c4c7dd6 100644 --- a/internal/toproto5/validateephemeralresourceconfig_test.go +++ b/internal/toproto5/validateephemeralresourceconfig_test.go @@ -54,8 +54,6 @@ func TestValidateEphemeralResourceConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto5/validateresourcetypeconfig_test.go b/internal/toproto5/validateresourcetypeconfig_test.go index 820a7f59a..8596f3f40 100644 --- a/internal/toproto5/validateresourcetypeconfig_test.go +++ b/internal/toproto5/validateresourcetypeconfig_test.go @@ -54,8 +54,6 @@ func TestValidateResourceTypeConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/applyresourcechange_test.go b/internal/toproto6/applyresourcechange_test.go index 9a4d1f886..5084841d1 100644 --- a/internal/toproto6/applyresourcechange_test.go +++ b/internal/toproto6/applyresourcechange_test.go @@ -157,8 +157,6 @@ func TestApplyResourceChangeResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/block_test.go b/internal/toproto6/block_test.go index 37369b503..e1c5784d3 100644 --- a/internal/toproto6/block_test.go +++ b/internal/toproto6/block_test.go @@ -551,7 +551,6 @@ func TestBlock(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/callfunction_test.go b/internal/toproto6/callfunction_test.go index 2f8df94d0..b7757a2eb 100644 --- a/internal/toproto6/callfunction_test.go +++ b/internal/toproto6/callfunction_test.go @@ -53,8 +53,6 @@ func TestCallFunctionResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/closeephemeralresource_test.go b/internal/toproto6/closeephemeralresource_test.go index cf9830dd0..05bd2cd1b 100644 --- a/internal/toproto6/closeephemeralresource_test.go +++ b/internal/toproto6/closeephemeralresource_test.go @@ -54,8 +54,6 @@ func TestCloseEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/config_test.go b/internal/toproto6/config_test.go index e8543200a..4cc350877 100644 --- a/internal/toproto6/config_test.go +++ b/internal/toproto6/config_test.go @@ -90,8 +90,6 @@ func TestConfig(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/configureprovider_test.go b/internal/toproto6/configureprovider_test.go index f8f999dbd..13b28959a 100644 --- a/internal/toproto6/configureprovider_test.go +++ b/internal/toproto6/configureprovider_test.go @@ -54,8 +54,6 @@ func TestConfigureProviderResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/datasourcemetadata_test.go b/internal/toproto6/datasourcemetadata_test.go index af81c1f80..583a8e7a1 100644 --- a/internal/toproto6/datasourcemetadata_test.go +++ b/internal/toproto6/datasourcemetadata_test.go @@ -31,8 +31,6 @@ func TestDataSourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/diagnostics_test.go b/internal/toproto6/diagnostics_test.go index d473131b0..7e3ec0313 100644 --- a/internal/toproto6/diagnostics_test.go +++ b/internal/toproto6/diagnostics_test.go @@ -38,8 +38,6 @@ func TestDiagnosticSeverity(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -116,7 +114,6 @@ func TestDiagnostics(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/dynamic_value_test.go b/internal/toproto6/dynamic_value_test.go index efe2b8ee2..766f6b022 100644 --- a/internal/toproto6/dynamic_value_test.go +++ b/internal/toproto6/dynamic_value_test.go @@ -1230,8 +1230,6 @@ func TestDynamicValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/ephemeral_result_data_test.go b/internal/toproto6/ephemeral_result_data_test.go index 18cb2aced..14705f5fe 100644 --- a/internal/toproto6/ephemeral_result_data_test.go +++ b/internal/toproto6/ephemeral_result_data_test.go @@ -90,8 +90,6 @@ func TestEphemeralResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/ephemeralresourcemetadata_test.go b/internal/toproto6/ephemeralresourcemetadata_test.go index c62b90797..d14a22605 100644 --- a/internal/toproto6/ephemeralresourcemetadata_test.go +++ b/internal/toproto6/ephemeralresourcemetadata_test.go @@ -31,8 +31,6 @@ func TestEphemeralResourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/function_test.go b/internal/toproto6/function_test.go index f831cbe6a..793ab5202 100644 --- a/internal/toproto6/function_test.go +++ b/internal/toproto6/function_test.go @@ -215,8 +215,6 @@ func TestFunction(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +245,6 @@ func TestFunctionMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -456,8 +452,6 @@ func TestFunctionParameter(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -490,8 +484,6 @@ func TestFunctionReturn(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -530,8 +522,6 @@ func TestFunctionResultData(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/getfunctions_test.go b/internal/toproto6/getfunctions_test.go index 8de74f894..c3ebbe79a 100644 --- a/internal/toproto6/getfunctions_test.go +++ b/internal/toproto6/getfunctions_test.go @@ -221,8 +221,6 @@ func TestGetFunctionsResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/getmetadata_test.go b/internal/toproto6/getmetadata_test.go index 40a6b05e7..fe02ebf1c 100644 --- a/internal/toproto6/getmetadata_test.go +++ b/internal/toproto6/getmetadata_test.go @@ -173,8 +173,6 @@ func TestGetMetadataResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/getproviderschema_test.go b/internal/toproto6/getproviderschema_test.go index 161ae67a3..1b3d1da19 100644 --- a/internal/toproto6/getproviderschema_test.go +++ b/internal/toproto6/getproviderschema_test.go @@ -5136,8 +5136,6 @@ func TestGetProviderSchemaResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/importedresource_test.go b/internal/toproto6/importedresource_test.go index ce89d34f9..248d0d437 100644 --- a/internal/toproto6/importedresource_test.go +++ b/internal/toproto6/importedresource_test.go @@ -199,8 +199,6 @@ func TestImportResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/moveresourcestate_test.go b/internal/toproto6/moveresourcestate_test.go index 8a40223ee..d5281c970 100644 --- a/internal/toproto6/moveresourcestate_test.go +++ b/internal/toproto6/moveresourcestate_test.go @@ -155,8 +155,6 @@ func TestMoveResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/openephemeralresource_test.go b/internal/toproto6/openephemeralresource_test.go index 734294018..4343c3e26 100644 --- a/internal/toproto6/openephemeralresource_test.go +++ b/internal/toproto6/openephemeralresource_test.go @@ -199,8 +199,6 @@ func TestOpenEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index ad8376e16..376ec1f9b 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -200,8 +200,6 @@ func TestPlanResourceChangeResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/readdatasource_test.go b/internal/toproto6/readdatasource_test.go index 593452609..062e0e0ba 100644 --- a/internal/toproto6/readdatasource_test.go +++ b/internal/toproto6/readdatasource_test.go @@ -152,8 +152,6 @@ func TestReadDataSourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index 46d928c31..18842699e 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -187,8 +187,6 @@ func TestReadResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/renewephemeralresource_test.go b/internal/toproto6/renewephemeralresource_test.go index 54c3b4f64..1491f3bc4 100644 --- a/internal/toproto6/renewephemeralresource_test.go +++ b/internal/toproto6/renewephemeralresource_test.go @@ -102,8 +102,6 @@ func TestRenewEphemeralResourceResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/resourcemetadata_test.go b/internal/toproto6/resourcemetadata_test.go index 17f9463e1..d4e561e72 100644 --- a/internal/toproto6/resourcemetadata_test.go +++ b/internal/toproto6/resourcemetadata_test.go @@ -31,8 +31,6 @@ func TestResourceMetadata(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/schema_attribute_test.go b/internal/toproto6/schema_attribute_test.go index 595694bb9..1eb83c0c0 100644 --- a/internal/toproto6/schema_attribute_test.go +++ b/internal/toproto6/schema_attribute_test.go @@ -437,7 +437,6 @@ func TestSchemaAttribute(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/schema_test.go b/internal/toproto6/schema_test.go index e3888b639..50d203c61 100644 --- a/internal/toproto6/schema_test.go +++ b/internal/toproto6/schema_test.go @@ -627,7 +627,6 @@ func TestSchema(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/server_capabilities_test.go b/internal/toproto6/server_capabilities_test.go index a9e6955fe..557db6c32 100644 --- a/internal/toproto6/server_capabilities_test.go +++ b/internal/toproto6/server_capabilities_test.go @@ -51,8 +51,6 @@ func TestServerCapabilities(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/state_test.go b/internal/toproto6/state_test.go index 70f36ce08..0316e3524 100644 --- a/internal/toproto6/state_test.go +++ b/internal/toproto6/state_test.go @@ -90,8 +90,6 @@ func TestState(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/upgraderesourcestate_test.go b/internal/toproto6/upgraderesourcestate_test.go index 70928db44..d31589d55 100644 --- a/internal/toproto6/upgraderesourcestate_test.go +++ b/internal/toproto6/upgraderesourcestate_test.go @@ -134,8 +134,6 @@ func TestUpgradeResourceStateResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/validatedatasourceconfig_test.go b/internal/toproto6/validatedatasourceconfig_test.go index 879b6982d..460807eb6 100644 --- a/internal/toproto6/validatedatasourceconfig_test.go +++ b/internal/toproto6/validatedatasourceconfig_test.go @@ -54,8 +54,6 @@ func TestValidateDataSourceConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/validateephemeralresourceconfig_test.go b/internal/toproto6/validateephemeralresourceconfig_test.go index a088f2b88..3b41fdf1d 100644 --- a/internal/toproto6/validateephemeralresourceconfig_test.go +++ b/internal/toproto6/validateephemeralresourceconfig_test.go @@ -54,8 +54,6 @@ func TestValidateEphemeralResourceConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/validateproviderconfig_test.go b/internal/toproto6/validateproviderconfig_test.go index 8f8c2ba90..ffe9adb93 100644 --- a/internal/toproto6/validateproviderconfig_test.go +++ b/internal/toproto6/validateproviderconfig_test.go @@ -134,8 +134,6 @@ func TestValidateProviderConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/toproto6/validateresourceconfig_test.go b/internal/toproto6/validateresourceconfig_test.go index 783487a97..c673ad911 100644 --- a/internal/toproto6/validateresourceconfig_test.go +++ b/internal/toproto6/validateresourceconfig_test.go @@ -54,8 +54,6 @@ func TestValidateResourceConfigResponse(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/totftypes/attribute_path_step_test.go b/internal/totftypes/attribute_path_step_test.go index f5070953b..796ec9b94 100644 --- a/internal/totftypes/attribute_path_step_test.go +++ b/internal/totftypes/attribute_path_step_test.go @@ -48,8 +48,6 @@ func TestAttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/totftypes/attribute_path_test.go b/internal/totftypes/attribute_path_test.go index eb618ca34..f0b957c1d 100644 --- a/internal/totftypes/attribute_path_test.go +++ b/internal/totftypes/attribute_path_test.go @@ -52,8 +52,6 @@ func TestAttributePath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/internal/totftypes/attribute_paths_test.go b/internal/totftypes/attribute_paths_test.go index b8daf3c06..d23901425 100644 --- a/internal/totftypes/attribute_paths_test.go +++ b/internal/totftypes/attribute_paths_test.go @@ -87,8 +87,6 @@ func TestAttributePaths(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_attribute_name_exact_test.go b/path/expression_step_attribute_name_exact_test.go index 46ec2f278..379f75e5b 100644 --- a/path/expression_step_attribute_name_exact_test.go +++ b/path/expression_step_attribute_name_exact_test.go @@ -47,8 +47,6 @@ func TestExpressionStepAttributeNameExactEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestExpressionStepAttributeNameExactMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -125,8 +121,6 @@ func TestExpressionStepAttributeNameExactString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_int_any_test.go b/path/expression_step_element_key_int_any_test.go index 3308ad4b9..a8752d6ea 100644 --- a/path/expression_step_element_key_int_any_test.go +++ b/path/expression_step_element_key_int_any_test.go @@ -47,8 +47,6 @@ func TestExpressionStepElementKeyIntAnyEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -92,8 +90,6 @@ func TestExpressionStepElementKeyIntAnyMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -120,8 +116,6 @@ func TestExpressionStepElementKeyIntAnyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_int_exact_test.go b/path/expression_step_element_key_int_exact_test.go index 7ae70ce20..f9c24c740 100644 --- a/path/expression_step_element_key_int_exact_test.go +++ b/path/expression_step_element_key_int_exact_test.go @@ -52,8 +52,6 @@ func TestExpressionStepElementKeyIntExactEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -102,8 +100,6 @@ func TestExpressionStepElementKeyIntExactMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -130,8 +126,6 @@ func TestExpressionStepElementKeyIntExactString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_string_any_test.go b/path/expression_step_element_key_string_any_test.go index 7af02ae70..c7fa43f1f 100644 --- a/path/expression_step_element_key_string_any_test.go +++ b/path/expression_step_element_key_string_any_test.go @@ -47,8 +47,6 @@ func TestExpressionStepElementKeyStringAnyEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -92,8 +90,6 @@ func TestExpressionStepElementKeyStringAnyMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -120,8 +116,6 @@ func TestExpressionStepElementKeyStringAnyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_string_exact_test.go b/path/expression_step_element_key_string_exact_test.go index 16a935045..ec2da5920 100644 --- a/path/expression_step_element_key_string_exact_test.go +++ b/path/expression_step_element_key_string_exact_test.go @@ -47,8 +47,6 @@ func TestExpressionStepElementKeyStringExactEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestExpressionStepElementKeyStringExactMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -129,8 +125,6 @@ func TestExpressionStepElementKeyStringExactString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_value_any_test.go b/path/expression_step_element_key_value_any_test.go index 78ee79e56..ae4ef52fe 100644 --- a/path/expression_step_element_key_value_any_test.go +++ b/path/expression_step_element_key_value_any_test.go @@ -47,8 +47,6 @@ func TestExpressionStepElementKeyValueAnyEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -92,8 +90,6 @@ func TestExpressionStepElementKeyValueAnyMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -120,8 +116,6 @@ func TestExpressionStepElementKeyValueAnyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_element_key_value_exact_test.go b/path/expression_step_element_key_value_exact_test.go index 136965c4f..c12fa0bd6 100644 --- a/path/expression_step_element_key_value_exact_test.go +++ b/path/expression_step_element_key_value_exact_test.go @@ -48,8 +48,6 @@ func TestExpressionStepElementKeyValueExactEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -98,8 +96,6 @@ func TestExpressionStepElementKeyValueExactMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -179,8 +175,6 @@ func TestExpressionStepElementKeyValueExactString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_step_parent_test.go b/path/expression_step_parent_test.go index 044f55ee2..496a98e09 100644 --- a/path/expression_step_parent_test.go +++ b/path/expression_step_parent_test.go @@ -47,8 +47,6 @@ func TestExpressionStepParentEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -92,8 +90,6 @@ func TestExpressionStepParentMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -120,8 +116,6 @@ func TestExpressionStepParentString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_steps_test.go b/path/expression_steps_test.go index 0e5fa3e97..02cb01d80 100644 --- a/path/expression_steps_test.go +++ b/path/expression_steps_test.go @@ -54,8 +54,6 @@ func TestExpressionStepsAppend(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestExpressionStepsCopy(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +296,6 @@ func TestExpressionStepsEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -364,8 +358,6 @@ func TestExpressionStepsLastStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -652,8 +644,6 @@ func TestExpressionStepsMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1068,8 +1058,6 @@ func TestExpressionStepsMatchesParent(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1132,8 +1120,6 @@ func TestExpressionStepsNextStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1253,8 +1239,6 @@ func TestExpressionStepsResolve(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1394,8 +1378,6 @@ func TestExpressionStepsString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expression_test.go b/path/expression_test.go index 982714a65..70664f5a1 100644 --- a/path/expression_test.go +++ b/path/expression_test.go @@ -30,8 +30,6 @@ func TestExpressionAtAnyListIndex(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -62,8 +60,6 @@ func TestExpressionAtAnyMapKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -94,8 +90,6 @@ func TestExpressionAtAnySetValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -129,8 +123,6 @@ func TestExpressionAtListIndex(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +156,6 @@ func TestExpressionAtMapKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -199,8 +189,6 @@ func TestExpressionAtName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -231,8 +219,6 @@ func TestExpressionAtParent(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -266,8 +252,6 @@ func TestExpressionAtSetValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -298,8 +282,6 @@ func TestExpressionCopy(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -348,8 +330,6 @@ func TestExpressionEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -493,8 +473,6 @@ func TestExpressionMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -698,8 +676,6 @@ func TestExpressionMatchesParent(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -743,8 +719,6 @@ func TestExpressionMerge(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -829,8 +803,6 @@ func TestExpressionMergeExpressions(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -871,8 +843,6 @@ func TestExpressionResolve(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -922,8 +892,6 @@ func TestExpressionSteps(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1011,8 +979,6 @@ func TestExpressionString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/expressions_test.go b/path/expressions_test.go index 6aca104f4..92b3060f2 100644 --- a/path/expressions_test.go +++ b/path/expressions_test.go @@ -83,8 +83,6 @@ func TestExpressionsAppend(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -210,8 +208,6 @@ func TestExpressionsContains(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -347,8 +343,6 @@ func TestExpressionsMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +399,6 @@ func TestExpressionsString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_step_attribute_name_test.go b/path/path_step_attribute_name_test.go index 5e5a0c215..780c7ee85 100644 --- a/path/path_step_attribute_name_test.go +++ b/path/path_step_attribute_name_test.go @@ -47,8 +47,6 @@ func TestPathStepAttributeNameEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -75,8 +73,6 @@ func TestPathStepAttributeNameExpressionStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -103,8 +99,6 @@ func TestPathStepAttributeNameString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_step_element_key_int_test.go b/path/path_step_element_key_int_test.go index 1599cd263..ec2601714 100644 --- a/path/path_step_element_key_int_test.go +++ b/path/path_step_element_key_int_test.go @@ -47,8 +47,6 @@ func TestPathStepElementKeyIntEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -75,8 +73,6 @@ func TestPathStepElementKeyIntExpressionStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -103,8 +99,6 @@ func TestPathStepElementKeyIntString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_step_element_key_string_test.go b/path/path_step_element_key_string_test.go index 222394e5a..4a80a038e 100644 --- a/path/path_step_element_key_string_test.go +++ b/path/path_step_element_key_string_test.go @@ -47,8 +47,6 @@ func TestPathStepElementKeyStringEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -75,8 +73,6 @@ func TestPathStepElementKeyStringExpressionStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +103,6 @@ func TestPathStepElementKeyStringString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_step_element_key_value_test.go b/path/path_step_element_key_value_test.go index 4039df817..d7e8af659 100644 --- a/path/path_step_element_key_value_test.go +++ b/path/path_step_element_key_value_test.go @@ -53,8 +53,6 @@ func TestPathStepElementKeyValueEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -81,8 +79,6 @@ func TestPathStepElementKeyValueExpressionStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -162,8 +158,6 @@ func TestPathStepElementKeyValueString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_steps_test.go b/path/path_steps_test.go index 3a46e7fb9..2e3f1e604 100644 --- a/path/path_steps_test.go +++ b/path/path_steps_test.go @@ -54,8 +54,6 @@ func TestPathStepsAppend(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestPathStepsCopy(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +296,6 @@ func TestPathStepsEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +344,6 @@ func TestPathStepsExpressionSteps(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -414,8 +406,6 @@ func TestPathStepsLastStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -482,8 +472,6 @@ func TestPathStepsNextStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -627,8 +615,6 @@ func TestPathStepsString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/path_test.go b/path/path_test.go index 7742e4335..80155d06a 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -38,8 +38,6 @@ func TestPathAtListIndex(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -78,8 +76,6 @@ func TestPathAtTupleIndex(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -118,8 +114,6 @@ func TestPathAtMapKey(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -158,8 +152,6 @@ func TestPathAtName(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -198,8 +190,6 @@ func TestPathAtSetValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestPathCopy(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -289,8 +277,6 @@ func TestPathEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -321,8 +307,6 @@ func TestPathExpression(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -357,8 +341,6 @@ func TestPathParentPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -398,8 +380,6 @@ func TestPathSteps(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -491,8 +471,6 @@ func TestPathString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/path/paths_test.go b/path/paths_test.go index 78a9d3561..a7ff2a422 100644 --- a/path/paths_test.go +++ b/path/paths_test.go @@ -83,8 +83,6 @@ func TestPathsAppend(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -203,8 +201,6 @@ func TestPathsContains(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -261,8 +257,6 @@ func TestPathsString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/bool_attribute_test.go b/provider/metaschema/bool_attribute_test.go index 50b3e06bb..f679fc1a5 100644 --- a/provider/metaschema/bool_attribute_test.go +++ b/provider/metaschema/bool_attribute_test.go @@ -55,8 +55,6 @@ func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestBoolAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -132,8 +128,6 @@ func TestBoolAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -166,8 +160,6 @@ func TestBoolAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -200,8 +192,6 @@ func TestBoolAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestBoolAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +250,6 @@ func TestBoolAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +282,6 @@ func TestBoolAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +314,6 @@ func TestBoolAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +340,6 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +366,6 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/float64_attribute_test.go b/provider/metaschema/float64_attribute_test.go index f6f5ebd7f..8a73c9225 100644 --- a/provider/metaschema/float64_attribute_test.go +++ b/provider/metaschema/float64_attribute_test.go @@ -55,8 +55,6 @@ func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -132,8 +128,6 @@ func TestFloat64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -166,8 +160,6 @@ func TestFloat64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -200,8 +192,6 @@ func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestFloat64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +250,6 @@ func TestFloat64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +282,6 @@ func TestFloat64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +314,6 @@ func TestFloat64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +340,6 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +366,6 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/int64_attribute_test.go b/provider/metaschema/int64_attribute_test.go index 2aa4d585e..69439d021 100644 --- a/provider/metaschema/int64_attribute_test.go +++ b/provider/metaschema/int64_attribute_test.go @@ -55,8 +55,6 @@ func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestInt64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -132,8 +128,6 @@ func TestInt64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -166,8 +160,6 @@ func TestInt64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -200,8 +192,6 @@ func TestInt64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestInt64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +250,6 @@ func TestInt64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +282,6 @@ func TestInt64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +314,6 @@ func TestInt64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +340,6 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +366,6 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/list_attribute_test.go b/provider/metaschema/list_attribute_test.go index 424c1d602..93f213ce0 100644 --- a/provider/metaschema/list_attribute_test.go +++ b/provider/metaschema/list_attribute_test.go @@ -58,8 +58,6 @@ func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -100,8 +98,6 @@ func TestListAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +136,6 @@ func TestListAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -174,8 +168,6 @@ func TestListAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -208,8 +200,6 @@ func TestListAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -242,8 +232,6 @@ func TestListAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -270,8 +258,6 @@ func TestListAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -304,8 +290,6 @@ func TestListAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -338,8 +322,6 @@ func TestListAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -366,8 +348,6 @@ func TestListAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -394,8 +374,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -461,8 +439,6 @@ func TestListAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/list_nested_attribute_test.go b/provider/metaschema/list_nested_attribute_test.go index c2aeae811..1c3576a30 100644 --- a/provider/metaschema/list_nested_attribute_test.go +++ b/provider/metaschema/list_nested_attribute_test.go @@ -83,8 +83,6 @@ func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -131,8 +129,6 @@ func TestListNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -222,8 +218,6 @@ func TestListNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +256,6 @@ func TestListNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -302,8 +294,6 @@ func TestListNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +330,6 @@ func TestListNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +374,6 @@ func TestListNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -420,8 +406,6 @@ func TestListNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -460,8 +444,6 @@ func TestListNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -500,8 +482,6 @@ func TestListNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -534,8 +514,6 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -562,8 +540,6 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/map_attribute_test.go b/provider/metaschema/map_attribute_test.go index 52ee19318..7d137ef1d 100644 --- a/provider/metaschema/map_attribute_test.go +++ b/provider/metaschema/map_attribute_test.go @@ -58,8 +58,6 @@ func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -100,8 +98,6 @@ func TestMapAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +136,6 @@ func TestMapAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -174,8 +168,6 @@ func TestMapAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -208,8 +200,6 @@ func TestMapAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -242,8 +232,6 @@ func TestMapAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -270,8 +258,6 @@ func TestMapAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -304,8 +290,6 @@ func TestMapAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -338,8 +322,6 @@ func TestMapAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -366,8 +348,6 @@ func TestMapAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -394,8 +374,6 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -461,8 +439,6 @@ func TestMapAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/map_nested_attribute_test.go b/provider/metaschema/map_nested_attribute_test.go index 88eb68b26..99c5ae558 100644 --- a/provider/metaschema/map_nested_attribute_test.go +++ b/provider/metaschema/map_nested_attribute_test.go @@ -83,8 +83,6 @@ func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -131,8 +129,6 @@ func TestMapNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -222,8 +218,6 @@ func TestMapNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +256,6 @@ func TestMapNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -302,8 +294,6 @@ func TestMapNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +330,6 @@ func TestMapNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +374,6 @@ func TestMapNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -420,8 +406,6 @@ func TestMapNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -460,8 +444,6 @@ func TestMapNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -500,8 +482,6 @@ func TestMapNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -534,8 +514,6 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -562,8 +540,6 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/nested_attribute_object_test.go b/provider/metaschema/nested_attribute_object_test.go index c9ca5a210..3ea0c0ac1 100644 --- a/provider/metaschema/nested_attribute_object_test.go +++ b/provider/metaschema/nested_attribute_object_test.go @@ -79,8 +79,6 @@ func TestNestedAttributeObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -144,8 +142,6 @@ func TestNestedAttributeObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -184,8 +180,6 @@ func TestNestedAttributeObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -226,8 +220,6 @@ func TestNestedAttributeObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/number_attribute_test.go b/provider/metaschema/number_attribute_test.go index 588dd4e0b..676db8e1b 100644 --- a/provider/metaschema/number_attribute_test.go +++ b/provider/metaschema/number_attribute_test.go @@ -55,8 +55,6 @@ func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestNumberAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -132,8 +128,6 @@ func TestNumberAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -166,8 +160,6 @@ func TestNumberAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -200,8 +192,6 @@ func TestNumberAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestNumberAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +250,6 @@ func TestNumberAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +282,6 @@ func TestNumberAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +314,6 @@ func TestNumberAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +340,6 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +366,6 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/object_attribute_test.go b/provider/metaschema/object_attribute_test.go index f439faf0a..6b3bb4479 100644 --- a/provider/metaschema/object_attribute_test.go +++ b/provider/metaschema/object_attribute_test.go @@ -64,8 +64,6 @@ func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -106,8 +104,6 @@ func TestObjectAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +142,6 @@ func TestObjectAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +174,6 @@ func TestObjectAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +206,6 @@ func TestObjectAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +238,6 @@ func TestObjectAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -276,8 +264,6 @@ func TestObjectAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -310,8 +296,6 @@ func TestObjectAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -344,8 +328,6 @@ func TestObjectAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -372,8 +354,6 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -400,8 +380,6 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -469,8 +447,6 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/schema_test.go b/provider/metaschema/schema_test.go index f4b64875f..7460f8016 100644 --- a/provider/metaschema/schema_test.go +++ b/provider/metaschema/schema_test.go @@ -82,8 +82,6 @@ func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -223,7 +221,6 @@ func TestSchemaAttributeAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -325,7 +322,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -383,8 +379,6 @@ func TestSchemaGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -411,8 +405,6 @@ func TestSchemaGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -443,8 +435,6 @@ func TestSchemaGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -475,8 +465,6 @@ func TestSchemaGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -507,8 +495,6 @@ func TestSchemaGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -539,8 +525,6 @@ func TestSchemaGetVersion(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -575,8 +559,6 @@ func TestSchemaType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -683,8 +665,6 @@ func TestSchemaTypeAtPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -783,8 +763,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -840,8 +818,6 @@ func TestSchemaValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -945,8 +921,6 @@ func TestSchemaValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/set_attribute_test.go b/provider/metaschema/set_attribute_test.go index 620209b6f..975b3b719 100644 --- a/provider/metaschema/set_attribute_test.go +++ b/provider/metaschema/set_attribute_test.go @@ -58,8 +58,6 @@ func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -100,8 +98,6 @@ func TestSetAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +136,6 @@ func TestSetAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -174,8 +168,6 @@ func TestSetAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -208,8 +200,6 @@ func TestSetAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -242,8 +232,6 @@ func TestSetAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -270,8 +258,6 @@ func TestSetAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -304,8 +290,6 @@ func TestSetAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -338,8 +322,6 @@ func TestSetAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -366,8 +348,6 @@ func TestSetAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -394,8 +374,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -461,8 +439,6 @@ func TestSetAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/set_nested_attribute_test.go b/provider/metaschema/set_nested_attribute_test.go index d0d86508c..225a936f9 100644 --- a/provider/metaschema/set_nested_attribute_test.go +++ b/provider/metaschema/set_nested_attribute_test.go @@ -83,8 +83,6 @@ func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -131,8 +129,6 @@ func TestSetNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -222,8 +218,6 @@ func TestSetNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +256,6 @@ func TestSetNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -302,8 +294,6 @@ func TestSetNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +330,6 @@ func TestSetNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +374,6 @@ func TestSetNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -420,8 +406,6 @@ func TestSetNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -460,8 +444,6 @@ func TestSetNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -500,8 +482,6 @@ func TestSetNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -534,8 +514,6 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -562,8 +540,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/single_nested_attribute_test.go b/provider/metaschema/single_nested_attribute_test.go index f04d55634..d590cbba8 100644 --- a/provider/metaschema/single_nested_attribute_test.go +++ b/provider/metaschema/single_nested_attribute_test.go @@ -81,8 +81,6 @@ func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -172,8 +170,6 @@ func TestSingleNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -204,8 +200,6 @@ func TestSingleNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -242,8 +236,6 @@ func TestSingleNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -280,8 +272,6 @@ func TestSingleNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +306,6 @@ func TestSingleNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +346,6 @@ func TestSingleNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,8 +376,6 @@ func TestSingleNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -428,8 +412,6 @@ func TestSingleNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -466,8 +448,6 @@ func TestSingleNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +478,6 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -526,8 +504,6 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/metaschema/string_attribute_test.go b/provider/metaschema/string_attribute_test.go index 00f34d5b2..c360462a1 100644 --- a/provider/metaschema/string_attribute_test.go +++ b/provider/metaschema/string_attribute_test.go @@ -55,8 +55,6 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,8 +95,6 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -132,8 +128,6 @@ func TestStringAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -166,8 +160,6 @@ func TestStringAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -200,8 +192,6 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -234,8 +224,6 @@ func TestStringAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -262,8 +250,6 @@ func TestStringAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +282,6 @@ func TestStringAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +314,6 @@ func TestStringAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -358,8 +340,6 @@ func TestStringAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +366,6 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/bool_attribute_test.go b/provider/schema/bool_attribute_test.go index 50e4af6ed..3e134f214 100644 --- a/provider/schema/bool_attribute_test.go +++ b/provider/schema/bool_attribute_test.go @@ -56,8 +56,6 @@ func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestBoolAttributeBoolValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestBoolAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestBoolAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestBoolAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestBoolAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestBoolAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestBoolAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestBoolAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestBoolAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/dynamic_attribute_test.go b/provider/schema/dynamic_attribute_test.go index 0779fe460..66c7d855e 100644 --- a/provider/schema/dynamic_attribute_test.go +++ b/provider/schema/dynamic_attribute_test.go @@ -56,8 +56,6 @@ func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestDynamicAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestDynamicAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestDynamicAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestDynamicAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestDynamicAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,8 +257,6 @@ func TestDynamicAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestDynamicAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestDynamicAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -399,8 +379,6 @@ func TestDynamicAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/float32_attribute_test.go b/provider/schema/float32_attribute_test.go index 107216df6..70d13db04 100644 --- a/provider/schema/float32_attribute_test.go +++ b/provider/schema/float32_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat32AttributeFloat32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestFloat32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestFloat32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestFloat32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/float64_attribute_test.go b/provider/schema/float64_attribute_test.go index b08486708..8ff796cdb 100644 --- a/provider/schema/float64_attribute_test.go +++ b/provider/schema/float64_attribute_test.go @@ -56,8 +56,6 @@ func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestFloat64AttributeFloat64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +134,6 @@ func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestFloat64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestFloat64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestFloat64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestFloat64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestFloat64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestFloat64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/int32_attribute_test.go b/provider/schema/int32_attribute_test.go index a612fb42f..b9fa878b7 100644 --- a/provider/schema/int32_attribute_test.go +++ b/provider/schema/int32_attribute_test.go @@ -56,8 +56,6 @@ func TestInt32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt32AttributeInt32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestInt32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestInt32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestInt32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestInt32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/int64_attribute_test.go b/provider/schema/int64_attribute_test.go index 42efcaed4..c5ebec88a 100644 --- a/provider/schema/int64_attribute_test.go +++ b/provider/schema/int64_attribute_test.go @@ -56,8 +56,6 @@ func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestInt64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestInt64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestInt64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestInt64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestInt64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -275,8 +263,6 @@ func TestInt64AttributeInt64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestInt64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestInt64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestInt64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -405,8 +385,6 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/list_attribute_test.go b/provider/schema/list_attribute_test.go index 2a529ae13..d0ab91c58 100644 --- a/provider/schema/list_attribute_test.go +++ b/provider/schema/list_attribute_test.go @@ -59,8 +59,6 @@ func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestListAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestListAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestListAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestListAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestListAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -277,8 +265,6 @@ func TestListAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -311,8 +297,6 @@ func TestListAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -345,8 +329,6 @@ func TestListAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +361,6 @@ func TestListAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -407,8 +387,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -441,8 +419,6 @@ func TestListAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -530,8 +506,6 @@ func TestListAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/list_nested_attribute_test.go b/provider/schema/list_nested_attribute_test.go index 33d6f1723..1f3715c73 100644 --- a/provider/schema/list_nested_attribute_test.go +++ b/provider/schema/list_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestListNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestListNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestListNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestListNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestListNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestListNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +416,6 @@ func TestListNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -470,8 +454,6 @@ func TestListNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -510,8 +492,6 @@ func TestListNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -550,8 +530,6 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -578,8 +556,6 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +594,6 @@ func TestListNestedAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -697,8 +671,6 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/list_nested_block_test.go b/provider/schema/list_nested_block_test.go index f7042840e..a5891a4f5 100644 --- a/provider/schema/list_nested_block_test.go +++ b/provider/schema/list_nested_block_test.go @@ -86,8 +86,6 @@ func TestListNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestListNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestListNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestListNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestListNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestListNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestListNestedBlockListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestListNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestListNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/map_attribute_test.go b/provider/schema/map_attribute_test.go index 8c5548e6d..f2e1e6a25 100644 --- a/provider/schema/map_attribute_test.go +++ b/provider/schema/map_attribute_test.go @@ -59,8 +59,6 @@ func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestMapAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestMapAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestMapAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestMapAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestMapAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -277,8 +265,6 @@ func TestMapAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -311,8 +297,6 @@ func TestMapAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -345,8 +329,6 @@ func TestMapAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +361,6 @@ func TestMapAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -407,8 +387,6 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -441,8 +419,6 @@ func TestMapAttributeMapValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -530,8 +506,6 @@ func TestMapAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/map_nested_attribute_test.go b/provider/schema/map_nested_attribute_test.go index 129321f22..bfbdb86c9 100644 --- a/provider/schema/map_nested_attribute_test.go +++ b/provider/schema/map_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestMapNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestMapNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestMapNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestMapNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestMapNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestMapNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +416,6 @@ func TestMapNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -470,8 +454,6 @@ func TestMapNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -510,8 +492,6 @@ func TestMapNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -550,8 +530,6 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -578,8 +556,6 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +594,6 @@ func TestMapNestedAttributeMapNestedValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -697,8 +671,6 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/nested_attribute_object_test.go b/provider/schema/nested_attribute_object_test.go index 968d071d0..6a14e6344 100644 --- a/provider/schema/nested_attribute_object_test.go +++ b/provider/schema/nested_attribute_object_test.go @@ -80,8 +80,6 @@ func TestNestedAttributeObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestNestedAttributeObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +181,6 @@ func TestNestedAttributeObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -223,8 +217,6 @@ func TestNestedAttributeObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -265,8 +257,6 @@ func TestNestedAttributeObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/nested_block_object_test.go b/provider/schema/nested_block_object_test.go index 5fa2a9c7e..3dd2beaf3 100644 --- a/provider/schema/nested_block_object_test.go +++ b/provider/schema/nested_block_object_test.go @@ -98,8 +98,6 @@ func TestNestedBlockObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -163,8 +161,6 @@ func TestNestedBlockObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -203,8 +199,6 @@ func TestNestedBlockObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -259,8 +253,6 @@ func TestNestedBlockObjectGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -297,8 +289,6 @@ func TestNestedBlockObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +341,6 @@ func TestNestedBlockObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/number_attribute_test.go b/provider/schema/number_attribute_test.go index 306e975e5..80dba9dd1 100644 --- a/provider/schema/number_attribute_test.go +++ b/provider/schema/number_attribute_test.go @@ -56,8 +56,6 @@ func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestNumberAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestNumberAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestNumberAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestNumberAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestNumberAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,8 +257,6 @@ func TestNumberAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestNumberAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestNumberAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -399,8 +379,6 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestNumberAttributeNumberValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/object_attribute_test.go b/provider/schema/object_attribute_test.go index 089d03f71..a53bc6bf5 100644 --- a/provider/schema/object_attribute_test.go +++ b/provider/schema/object_attribute_test.go @@ -65,8 +65,6 @@ func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -113,8 +111,6 @@ func TestObjectAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -153,8 +149,6 @@ func TestObjectAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -187,8 +181,6 @@ func TestObjectAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -221,8 +213,6 @@ func TestObjectAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -255,8 +245,6 @@ func TestObjectAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -283,8 +271,6 @@ func TestObjectAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -317,8 +303,6 @@ func TestObjectAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -351,8 +335,6 @@ func TestObjectAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -385,8 +367,6 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -413,8 +393,6 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -447,8 +425,6 @@ func TestObjectAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -563,8 +539,6 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/schema_test.go b/provider/schema/schema_test.go index 5b84aac8c..9007eabdc 100644 --- a/provider/schema/schema_test.go +++ b/provider/schema/schema_test.go @@ -100,8 +100,6 @@ func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,7 +267,6 @@ func TestSchemaAttributeAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,7 +387,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -448,8 +444,6 @@ func TestSchemaGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -504,8 +498,6 @@ func TestSchemaGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -542,8 +534,6 @@ func TestSchemaGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -580,8 +570,6 @@ func TestSchemaGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +606,6 @@ func TestSchemaGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -650,8 +636,6 @@ func TestSchemaGetVersion(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -698,8 +682,6 @@ func TestSchemaType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -839,8 +821,6 @@ func TestSchemaTypeAtPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -972,8 +952,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1029,8 +1007,6 @@ func TestSchemaValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1342,8 +1318,6 @@ func TestSchemaValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/set_attribute_test.go b/provider/schema/set_attribute_test.go index 42aec99d1..bfa58193b 100644 --- a/provider/schema/set_attribute_test.go +++ b/provider/schema/set_attribute_test.go @@ -59,8 +59,6 @@ func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -107,8 +105,6 @@ func TestSetAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -147,8 +143,6 @@ func TestSetAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -181,8 +175,6 @@ func TestSetAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -215,8 +207,6 @@ func TestSetAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +239,6 @@ func TestSetAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -277,8 +265,6 @@ func TestSetAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -311,8 +297,6 @@ func TestSetAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -345,8 +329,6 @@ func TestSetAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +361,6 @@ func TestSetAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -407,8 +387,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -441,8 +419,6 @@ func TestSetAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -530,8 +506,6 @@ func TestSetAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/set_nested_attribute_test.go b/provider/schema/set_nested_attribute_test.go index 942cd2a25..3471f42e3 100644 --- a/provider/schema/set_nested_attribute_test.go +++ b/provider/schema/set_nested_attribute_test.go @@ -87,8 +87,6 @@ func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestSetNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -232,8 +228,6 @@ func TestSetNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +266,6 @@ func TestSetNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -312,8 +304,6 @@ func TestSetNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +340,6 @@ func TestSetNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +384,6 @@ func TestSetNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +416,6 @@ func TestSetNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -470,8 +454,6 @@ func TestSetNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -510,8 +492,6 @@ func TestSetNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -550,8 +530,6 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -578,8 +556,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +594,6 @@ func TestSetNestedAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -697,8 +671,6 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/set_nested_block_test.go b/provider/schema/set_nested_block_test.go index d8a680009..14bd372de 100644 --- a/provider/schema/set_nested_block_test.go +++ b/provider/schema/set_nested_block_test.go @@ -86,8 +86,6 @@ func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -140,8 +138,6 @@ func TestSetNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +256,6 @@ func TestSetNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -300,8 +294,6 @@ func TestSetNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +332,6 @@ func TestSetNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -378,8 +368,6 @@ func TestSetNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestSetNestedBlockSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -476,8 +462,6 @@ func TestSetNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -554,8 +538,6 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/single_nested_attribute_test.go b/provider/schema/single_nested_attribute_test.go index 6d40629a2..cbb2f3aa6 100644 --- a/provider/schema/single_nested_attribute_test.go +++ b/provider/schema/single_nested_attribute_test.go @@ -82,8 +82,6 @@ func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +171,6 @@ func TestSingleNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -211,8 +207,6 @@ func TestSingleNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,8 +243,6 @@ func TestSingleNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +279,6 @@ func TestSingleNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -323,8 +313,6 @@ func TestSingleNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -365,8 +353,6 @@ func TestSingleNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -397,8 +383,6 @@ func TestSingleNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -435,8 +419,6 @@ func TestSingleNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -473,8 +455,6 @@ func TestSingleNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -511,8 +491,6 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -539,8 +517,6 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -577,8 +553,6 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/single_nested_block_test.go b/provider/schema/single_nested_block_test.go index 802f41be6..d4a7e75cd 100644 --- a/provider/schema/single_nested_block_test.go +++ b/provider/schema/single_nested_block_test.go @@ -99,8 +99,6 @@ func TestSingleNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +149,6 @@ func TestSingleNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +249,6 @@ func TestSingleNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -291,8 +285,6 @@ func TestSingleNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -329,8 +321,6 @@ func TestSingleNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +369,6 @@ func TestSingleNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -417,8 +405,6 @@ func TestSingleNestedBlockObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -471,8 +457,6 @@ func TestSingleNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/provider/schema/string_attribute_test.go b/provider/schema/string_attribute_test.go index 58b0cafeb..35e9f03a8 100644 --- a/provider/schema/string_attribute_test.go +++ b/provider/schema/string_attribute_test.go @@ -56,8 +56,6 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -104,8 +102,6 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -139,8 +135,6 @@ func TestStringAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -173,8 +167,6 @@ func TestStringAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -207,8 +199,6 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -241,8 +231,6 @@ func TestStringAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,8 +257,6 @@ func TestStringAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +289,6 @@ func TestStringAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -337,8 +321,6 @@ func TestStringAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -371,8 +353,6 @@ func TestStringAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -399,8 +379,6 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -433,8 +411,6 @@ func TestStringAttributeStringValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/providerserver/serve_opts_test.go b/providerserver/serve_opts_test.go index 5b7634819..5df7e98f1 100644 --- a/providerserver/serve_opts_test.go +++ b/providerserver/serve_opts_test.go @@ -60,8 +60,6 @@ func TestServeOptsValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -111,8 +109,6 @@ func TestServeOptsValidateAddress(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/bool_attribute_test.go b/resource/schema/bool_attribute_test.go index e2d77dd42..9f2d2e863 100644 --- a/resource/schema/bool_attribute_test.go +++ b/resource/schema/bool_attribute_test.go @@ -62,8 +62,6 @@ func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestBoolAttributeBoolDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -157,8 +153,6 @@ func TestBoolAttributeBoolPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -191,8 +185,6 @@ func TestBoolAttributeBoolValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -225,8 +217,6 @@ func TestBoolAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +250,6 @@ func TestBoolAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +282,6 @@ func TestBoolAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestBoolAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -362,8 +346,6 @@ func TestBoolAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +378,6 @@ func TestBoolAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +410,6 @@ func TestBoolAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestBoolAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestBoolAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestBoolAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/booldefault/static_value_test.go b/resource/schema/booldefault/static_value_test.go index a346d57d8..8d79eb88d 100644 --- a/resource/schema/booldefault/static_value_test.go +++ b/resource/schema/booldefault/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticBoolDefaultBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/boolplanmodifier/requires_replace_if_configured_test.go b/resource/schema/boolplanmodifier/requires_replace_if_configured_test.go index b4559f5a6..13bfae26b 100644 --- a/resource/schema/boolplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/boolplanmodifier/requires_replace_if_configured_test.go @@ -151,8 +151,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/boolplanmodifier/requires_replace_if_test.go b/resource/schema/boolplanmodifier/requires_replace_if_test.go index 73a941856..4d88e36ef 100644 --- a/resource/schema/boolplanmodifier/requires_replace_if_test.go +++ b/resource/schema/boolplanmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/boolplanmodifier/requires_replace_test.go b/resource/schema/boolplanmodifier/requires_replace_test.go index bb92ca4d0..d73c2b2da 100644 --- a/resource/schema/boolplanmodifier/requires_replace_test.go +++ b/resource/schema/boolplanmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/boolplanmodifier/use_state_for_unknown_test.go b/resource/schema/boolplanmodifier/use_state_for_unknown_test.go index cad1f649e..c385c8c75 100644 --- a/resource/schema/boolplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/boolplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamic_attribute_test.go b/resource/schema/dynamic_attribute_test.go index 93c5010f3..c1340d493 100644 --- a/resource/schema/dynamic_attribute_test.go +++ b/resource/schema/dynamic_attribute_test.go @@ -62,8 +62,6 @@ func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestDynamicAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +141,6 @@ func TestDynamicAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -179,8 +173,6 @@ func TestDynamicAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -213,8 +205,6 @@ func TestDynamicAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +237,6 @@ func TestDynamicAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -281,8 +269,6 @@ func TestDynamicAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -315,8 +301,6 @@ func TestDynamicAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +333,6 @@ func TestDynamicAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -383,8 +365,6 @@ func TestDynamicAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -417,8 +397,6 @@ func TestDynamicAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestDynamicAttributeDynamicDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestDynamicAttributeDynamicPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestDynamicAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamicdefault/static_value_test.go b/resource/schema/dynamicdefault/static_value_test.go index d96670b51..825e4d646 100644 --- a/resource/schema/dynamicdefault/static_value_test.go +++ b/resource/schema/dynamicdefault/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticValueDefaultDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamicplanmodifier/requires_replace_if_configured_test.go b/resource/schema/dynamicplanmodifier/requires_replace_if_configured_test.go index fc017e69e..8f26f5b69 100644 --- a/resource/schema/dynamicplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/dynamicplanmodifier/requires_replace_if_configured_test.go @@ -164,8 +164,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamicplanmodifier/requires_replace_if_test.go b/resource/schema/dynamicplanmodifier/requires_replace_if_test.go index c482bcab8..a878405f2 100644 --- a/resource/schema/dynamicplanmodifier/requires_replace_if_test.go +++ b/resource/schema/dynamicplanmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamicplanmodifier/requires_replace_test.go b/resource/schema/dynamicplanmodifier/requires_replace_test.go index 2b1d76fb4..6fad7bac8 100644 --- a/resource/schema/dynamicplanmodifier/requires_replace_test.go +++ b/resource/schema/dynamicplanmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/dynamicplanmodifier/use_state_for_unknown_test.go b/resource/schema/dynamicplanmodifier/use_state_for_unknown_test.go index 13fbcd264..58e346bf1 100644 --- a/resource/schema/dynamicplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/dynamicplanmodifier/use_state_for_unknown_test.go @@ -140,8 +140,6 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32_attribute_test.go b/resource/schema/float32_attribute_test.go index 3c90d0081..4671b082f 100644 --- a/resource/schema/float32_attribute_test.go +++ b/resource/schema/float32_attribute_test.go @@ -62,8 +62,6 @@ func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestFloat32AttributeFloat32DefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -157,8 +153,6 @@ func TestFloat32AttributeFloat32PlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -191,8 +185,6 @@ func TestFloat32AttributeFloat32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -225,8 +217,6 @@ func TestFloat32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +250,6 @@ func TestFloat32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +282,6 @@ func TestFloat32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestFloat32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -362,8 +346,6 @@ func TestFloat32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +378,6 @@ func TestFloat32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +410,6 @@ func TestFloat32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestFloat32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestFloat32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestFloat32AttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32default/static_value_test.go b/resource/schema/float32default/static_value_test.go index 543677b05..9e61e4309 100644 --- a/resource/schema/float32default/static_value_test.go +++ b/resource/schema/float32default/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticFloat32DefaultFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32planmodifier/requires_replace_if_configured_test.go b/resource/schema/float32planmodifier/requires_replace_if_configured_test.go index 2d13f3d97..ef437a77c 100644 --- a/resource/schema/float32planmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/float32planmodifier/requires_replace_if_configured_test.go @@ -151,8 +151,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32planmodifier/requires_replace_if_test.go b/resource/schema/float32planmodifier/requires_replace_if_test.go index f8185a66f..cc8c07e04 100644 --- a/resource/schema/float32planmodifier/requires_replace_if_test.go +++ b/resource/schema/float32planmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32planmodifier/requires_replace_test.go b/resource/schema/float32planmodifier/requires_replace_test.go index 32f35e86c..02252e4ee 100644 --- a/resource/schema/float32planmodifier/requires_replace_test.go +++ b/resource/schema/float32planmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float32planmodifier/use_state_for_unknown_test.go b/resource/schema/float32planmodifier/use_state_for_unknown_test.go index cb8c514fa..2151333aa 100644 --- a/resource/schema/float32planmodifier/use_state_for_unknown_test.go +++ b/resource/schema/float32planmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64_attribute_test.go b/resource/schema/float64_attribute_test.go index 939080080..0a42d4770 100644 --- a/resource/schema/float64_attribute_test.go +++ b/resource/schema/float64_attribute_test.go @@ -62,8 +62,6 @@ func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -123,8 +121,6 @@ func TestFloat64AttributeFloat64DefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -157,8 +153,6 @@ func TestFloat64AttributeFloat64PlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -191,8 +185,6 @@ func TestFloat64AttributeFloat64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -225,8 +217,6 @@ func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +250,6 @@ func TestFloat64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +282,6 @@ func TestFloat64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -362,8 +346,6 @@ func TestFloat64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +378,6 @@ func TestFloat64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +410,6 @@ func TestFloat64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestFloat64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestFloat64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestFloat64AttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64default/static_value_test.go b/resource/schema/float64default/static_value_test.go index 03feb33c1..923a0fe6d 100644 --- a/resource/schema/float64default/static_value_test.go +++ b/resource/schema/float64default/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticFloat64DefaultFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64planmodifier/requires_replace_if_configured_test.go b/resource/schema/float64planmodifier/requires_replace_if_configured_test.go index 2823b6482..79b567a26 100644 --- a/resource/schema/float64planmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/float64planmodifier/requires_replace_if_configured_test.go @@ -151,8 +151,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64planmodifier/requires_replace_if_test.go b/resource/schema/float64planmodifier/requires_replace_if_test.go index eec7cbdb2..15fec9b42 100644 --- a/resource/schema/float64planmodifier/requires_replace_if_test.go +++ b/resource/schema/float64planmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64planmodifier/requires_replace_test.go b/resource/schema/float64planmodifier/requires_replace_test.go index 5899e327e..172cf5ca4 100644 --- a/resource/schema/float64planmodifier/requires_replace_test.go +++ b/resource/schema/float64planmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/float64planmodifier/use_state_for_unknown_test.go b/resource/schema/float64planmodifier/use_state_for_unknown_test.go index d8d9168c5..75f439847 100644 --- a/resource/schema/float64planmodifier/use_state_for_unknown_test.go +++ b/resource/schema/float64planmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32_attribute_test.go b/resource/schema/int32_attribute_test.go index 48eb3d2f2..a151c9406 100644 --- a/resource/schema/int32_attribute_test.go +++ b/resource/schema/int32_attribute_test.go @@ -62,8 +62,6 @@ func TestInt32AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestInt32AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +141,6 @@ func TestInt32AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -179,8 +173,6 @@ func TestInt32AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -213,8 +205,6 @@ func TestInt32AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +237,6 @@ func TestInt32AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +282,6 @@ func TestInt32AttributeInt32DefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestInt32AttributeInt32PlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -362,8 +346,6 @@ func TestInt32AttributeInt32Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +378,6 @@ func TestInt32AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +410,6 @@ func TestInt32AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestInt32AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestInt32AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestInt32AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestInt32AttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32default/static_value_test.go b/resource/schema/int32default/static_value_test.go index 6b34cd9d7..2fed56939 100644 --- a/resource/schema/int32default/static_value_test.go +++ b/resource/schema/int32default/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticInt32DefaultInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32planmodifier/requires_replace_if_configured_test.go b/resource/schema/int32planmodifier/requires_replace_if_configured_test.go index 4f6873025..6357e014b 100644 --- a/resource/schema/int32planmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/int32planmodifier/requires_replace_if_configured_test.go @@ -152,8 +152,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32planmodifier/requires_replace_if_test.go b/resource/schema/int32planmodifier/requires_replace_if_test.go index 950c94f47..6bc3cc165 100644 --- a/resource/schema/int32planmodifier/requires_replace_if_test.go +++ b/resource/schema/int32planmodifier/requires_replace_if_test.go @@ -163,8 +163,6 @@ func TestRequiresReplaceIfModifierPlanModifyInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32planmodifier/requires_replace_test.go b/resource/schema/int32planmodifier/requires_replace_test.go index 71881c8a6..8f1ea86e9 100644 --- a/resource/schema/int32planmodifier/requires_replace_test.go +++ b/resource/schema/int32planmodifier/requires_replace_test.go @@ -135,8 +135,6 @@ func TestRequiresReplaceModifierPlanModifyInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int32planmodifier/use_state_for_unknown_test.go b/resource/schema/int32planmodifier/use_state_for_unknown_test.go index 6cb27e802..45bdc2b10 100644 --- a/resource/schema/int32planmodifier/use_state_for_unknown_test.go +++ b/resource/schema/int32planmodifier/use_state_for_unknown_test.go @@ -122,8 +122,6 @@ func TestUseStateForUnknownModifierPlanModifyInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64_attribute_test.go b/resource/schema/int64_attribute_test.go index 032f523c9..1ce11e722 100644 --- a/resource/schema/int64_attribute_test.go +++ b/resource/schema/int64_attribute_test.go @@ -62,8 +62,6 @@ func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestInt64AttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +141,6 @@ func TestInt64AttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -179,8 +173,6 @@ func TestInt64AttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -213,8 +205,6 @@ func TestInt64AttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +237,6 @@ func TestInt64AttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +282,6 @@ func TestInt64AttributeInt64DefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -328,8 +314,6 @@ func TestInt64AttributeInt64PlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -362,8 +346,6 @@ func TestInt64AttributeInt64Validators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -396,8 +378,6 @@ func TestInt64AttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -430,8 +410,6 @@ func TestInt64AttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestInt64AttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestInt64AttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestInt64AttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64default/static_value_test.go b/resource/schema/int64default/static_value_test.go index b6dbce3f1..13404c8fc 100644 --- a/resource/schema/int64default/static_value_test.go +++ b/resource/schema/int64default/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticInt64DefaultInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64planmodifier/requires_replace_if_configured_test.go b/resource/schema/int64planmodifier/requires_replace_if_configured_test.go index 23bbdb2ed..915e1b302 100644 --- a/resource/schema/int64planmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/int64planmodifier/requires_replace_if_configured_test.go @@ -151,8 +151,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64planmodifier/requires_replace_if_test.go b/resource/schema/int64planmodifier/requires_replace_if_test.go index 9c9a05451..f19b794b0 100644 --- a/resource/schema/int64planmodifier/requires_replace_if_test.go +++ b/resource/schema/int64planmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64planmodifier/requires_replace_test.go b/resource/schema/int64planmodifier/requires_replace_test.go index dcb819ead..92298f8a8 100644 --- a/resource/schema/int64planmodifier/requires_replace_test.go +++ b/resource/schema/int64planmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/int64planmodifier/use_state_for_unknown_test.go b/resource/schema/int64planmodifier/use_state_for_unknown_test.go index bd7e9279a..c588e5e67 100644 --- a/resource/schema/int64planmodifier/use_state_for_unknown_test.go +++ b/resource/schema/int64planmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/list_attribute_test.go b/resource/schema/list_attribute_test.go index 07db0c333..1be15bbb7 100644 --- a/resource/schema/list_attribute_test.go +++ b/resource/schema/list_attribute_test.go @@ -63,8 +63,6 @@ func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -111,8 +109,6 @@ func TestListAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +147,6 @@ func TestListAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +179,6 @@ func TestListAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -219,8 +211,6 @@ func TestListAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +243,6 @@ func TestListAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestListAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -321,8 +307,6 @@ func TestListAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -355,8 +339,6 @@ func TestListAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -389,8 +371,6 @@ func TestListAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +403,6 @@ func TestListAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -484,8 +462,6 @@ func TestListAttributeListDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -518,8 +494,6 @@ func TestListAttributeListPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -552,8 +526,6 @@ func TestListAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -748,8 +720,6 @@ func TestListAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/list_nested_attribute_test.go b/resource/schema/list_nested_attribute_test.go index 290fd0dc7..258d7cf44 100644 --- a/resource/schema/list_nested_attribute_test.go +++ b/resource/schema/list_nested_attribute_test.go @@ -91,8 +91,6 @@ func TestListNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestListNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -236,8 +232,6 @@ func TestListNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -276,8 +270,6 @@ func TestListNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +308,6 @@ func TestListNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -354,8 +344,6 @@ func TestListNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -400,8 +388,6 @@ func TestListNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -440,8 +426,6 @@ func TestListNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -480,8 +464,6 @@ func TestListNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -520,8 +502,6 @@ func TestListNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -560,8 +540,6 @@ func TestListNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -594,8 +572,6 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -655,8 +631,6 @@ func TestListNestedAttributeListDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -695,8 +669,6 @@ func TestListNestedAttributeListPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -735,8 +707,6 @@ func TestListNestedAttributeListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1034,8 +1004,6 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/list_nested_block_test.go b/resource/schema/list_nested_block_test.go index c58551a04..e500287c1 100644 --- a/resource/schema/list_nested_block_test.go +++ b/resource/schema/list_nested_block_test.go @@ -87,8 +87,6 @@ func TestListNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestListNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -261,8 +257,6 @@ func TestListNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -301,8 +295,6 @@ func TestListNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -341,8 +333,6 @@ func TestListNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +369,6 @@ func TestListNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -419,8 +407,6 @@ func TestListNestedBlockListPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -459,8 +445,6 @@ func TestListNestedBlockListValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -517,8 +501,6 @@ func TestListNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -569,8 +551,6 @@ func TestListNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/listdefault/static_value_test.go b/resource/schema/listdefault/static_value_test.go index 0ca83bcb3..0d0c8e50a 100644 --- a/resource/schema/listdefault/static_value_test.go +++ b/resource/schema/listdefault/static_value_test.go @@ -41,8 +41,6 @@ func TestStaticValueDefaultList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/listplanmodifier/requires_replace_if_configured_test.go b/resource/schema/listplanmodifier/requires_replace_if_configured_test.go index 55bb3d0a8..a7552a119 100644 --- a/resource/schema/listplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/listplanmodifier/requires_replace_if_configured_test.go @@ -154,8 +154,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/listplanmodifier/requires_replace_if_test.go b/resource/schema/listplanmodifier/requires_replace_if_test.go index 40887f128..2f6aa2be9 100644 --- a/resource/schema/listplanmodifier/requires_replace_if_test.go +++ b/resource/schema/listplanmodifier/requires_replace_if_test.go @@ -165,8 +165,6 @@ func TestRequiresReplaceIfModifierPlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/listplanmodifier/requires_replace_test.go b/resource/schema/listplanmodifier/requires_replace_test.go index 32ac458f7..6961bec81 100644 --- a/resource/schema/listplanmodifier/requires_replace_test.go +++ b/resource/schema/listplanmodifier/requires_replace_test.go @@ -137,8 +137,6 @@ func TestRequiresReplaceModifierPlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/listplanmodifier/use_state_for_unknown_test.go b/resource/schema/listplanmodifier/use_state_for_unknown_test.go index 6a38f8221..e41b07cac 100644 --- a/resource/schema/listplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/listplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyList(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/map_attribute_test.go b/resource/schema/map_attribute_test.go index 9e48ea787..b62293350 100644 --- a/resource/schema/map_attribute_test.go +++ b/resource/schema/map_attribute_test.go @@ -63,8 +63,6 @@ func TestMapAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -111,8 +109,6 @@ func TestMapAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +147,6 @@ func TestMapAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +179,6 @@ func TestMapAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -219,8 +211,6 @@ func TestMapAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +243,6 @@ func TestMapAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestMapAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -321,8 +307,6 @@ func TestMapAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -355,8 +339,6 @@ func TestMapAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -389,8 +371,6 @@ func TestMapAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +403,6 @@ func TestMapAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -484,8 +462,6 @@ func TestMapAttributeMapDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -518,8 +494,6 @@ func TestMapAttributeMapPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -551,8 +525,6 @@ func TestMapAttributeMapValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -747,8 +719,6 @@ func TestMapAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/map_nested_attribute_test.go b/resource/schema/map_nested_attribute_test.go index 4c7a72489..6f360d85b 100644 --- a/resource/schema/map_nested_attribute_test.go +++ b/resource/schema/map_nested_attribute_test.go @@ -91,8 +91,6 @@ func TestMapNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestMapNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -236,8 +232,6 @@ func TestMapNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -276,8 +270,6 @@ func TestMapNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +308,6 @@ func TestMapNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -354,8 +344,6 @@ func TestMapNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -400,8 +388,6 @@ func TestMapNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -440,8 +426,6 @@ func TestMapNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -480,8 +464,6 @@ func TestMapNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -520,8 +502,6 @@ func TestMapNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -560,8 +540,6 @@ func TestMapNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -594,8 +572,6 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -655,8 +631,6 @@ func TestMapNestedAttributeMapNestedDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -695,8 +669,6 @@ func TestMapNestedAttributeMapNestedPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -735,8 +707,6 @@ func TestMapNestedAttributeMapNestedValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1034,8 +1004,6 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/mapdefault/static_value_test.go b/resource/schema/mapdefault/static_value_test.go index 9b606d7c2..04d1a9325 100644 --- a/resource/schema/mapdefault/static_value_test.go +++ b/resource/schema/mapdefault/static_value_test.go @@ -41,8 +41,6 @@ func TestStaticValueDefaultMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/mapplanmodifier/requires_replace_if_configured_test.go b/resource/schema/mapplanmodifier/requires_replace_if_configured_test.go index b9b7bfd76..9eecb0eb9 100644 --- a/resource/schema/mapplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/mapplanmodifier/requires_replace_if_configured_test.go @@ -154,8 +154,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/mapplanmodifier/requires_replace_if_test.go b/resource/schema/mapplanmodifier/requires_replace_if_test.go index cfe362858..5edd5ceac 100644 --- a/resource/schema/mapplanmodifier/requires_replace_if_test.go +++ b/resource/schema/mapplanmodifier/requires_replace_if_test.go @@ -165,8 +165,6 @@ func TestRequiresReplaceIfModifierPlanModifyMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/mapplanmodifier/requires_replace_test.go b/resource/schema/mapplanmodifier/requires_replace_test.go index bb8b08b69..8d1f6f7eb 100644 --- a/resource/schema/mapplanmodifier/requires_replace_test.go +++ b/resource/schema/mapplanmodifier/requires_replace_test.go @@ -137,8 +137,6 @@ func TestRequiresReplaceModifierPlanModifyMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/mapplanmodifier/use_state_for_unknown_test.go b/resource/schema/mapplanmodifier/use_state_for_unknown_test.go index 3b4bf72eb..1ea2aa940 100644 --- a/resource/schema/mapplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/mapplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyMap(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/nested_attribute_object_test.go b/resource/schema/nested_attribute_object_test.go index 7b325bbb8..f86c1952e 100644 --- a/resource/schema/nested_attribute_object_test.go +++ b/resource/schema/nested_attribute_object_test.go @@ -81,8 +81,6 @@ func TestNestedAttributeObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +144,6 @@ func TestNestedAttributeObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -186,8 +182,6 @@ func TestNestedAttributeObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -224,8 +218,6 @@ func TestNestedAttributeObjectObjectPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -261,8 +253,6 @@ func TestNestedAttributeObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -303,8 +293,6 @@ func TestNestedAttributeObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/nested_block_object_test.go b/resource/schema/nested_block_object_test.go index 95ff4613a..81e64c4e0 100644 --- a/resource/schema/nested_block_object_test.go +++ b/resource/schema/nested_block_object_test.go @@ -99,8 +99,6 @@ func TestNestedBlockObjectApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -164,8 +162,6 @@ func TestNestedBlockObjectEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -204,8 +200,6 @@ func TestNestedBlockObjectGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +254,6 @@ func TestNestedBlockObjectGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -298,8 +290,6 @@ func TestNestedBlockObjectObjectPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -336,8 +326,6 @@ func TestNestedBlockObjectObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,8 +378,6 @@ func TestNestedBlockObjectType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/number_attribute_test.go b/resource/schema/number_attribute_test.go index e9ff78cc8..537bd1bfc 100644 --- a/resource/schema/number_attribute_test.go +++ b/resource/schema/number_attribute_test.go @@ -63,8 +63,6 @@ func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -111,8 +109,6 @@ func TestNumberAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +142,6 @@ func TestNumberAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +174,6 @@ func TestNumberAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +206,6 @@ func TestNumberAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +238,6 @@ func TestNumberAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -282,8 +270,6 @@ func TestNumberAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +302,6 @@ func TestNumberAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -350,8 +334,6 @@ func TestNumberAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -384,8 +366,6 @@ func TestNumberAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +398,6 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -469,8 +447,6 @@ func TestNumberAttributeNumberDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -503,8 +479,6 @@ func TestNumberAttributeNumberPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -537,8 +511,6 @@ func TestNumberAttributeNumberValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -601,8 +573,6 @@ func TestNumberAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/numberdefault/static_value_test.go b/resource/schema/numberdefault/static_value_test.go index 48fa4e36a..a7c4ab79d 100644 --- a/resource/schema/numberdefault/static_value_test.go +++ b/resource/schema/numberdefault/static_value_test.go @@ -31,8 +31,6 @@ func TestStaticBigFloatDefaultNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/numberplanmodifier/requires_replace_if_configured_test.go b/resource/schema/numberplanmodifier/requires_replace_if_configured_test.go index f9280324b..88594020c 100644 --- a/resource/schema/numberplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/numberplanmodifier/requires_replace_if_configured_test.go @@ -152,8 +152,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/numberplanmodifier/requires_replace_if_test.go b/resource/schema/numberplanmodifier/requires_replace_if_test.go index ad12a05cd..67272731e 100644 --- a/resource/schema/numberplanmodifier/requires_replace_if_test.go +++ b/resource/schema/numberplanmodifier/requires_replace_if_test.go @@ -163,8 +163,6 @@ func TestRequiresReplaceIfModifierPlanModifyNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/numberplanmodifier/requires_replace_test.go b/resource/schema/numberplanmodifier/requires_replace_test.go index e88831f06..b902a544f 100644 --- a/resource/schema/numberplanmodifier/requires_replace_test.go +++ b/resource/schema/numberplanmodifier/requires_replace_test.go @@ -135,8 +135,6 @@ func TestRequiresReplaceModifierPlanModifyNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/numberplanmodifier/use_state_for_unknown_test.go b/resource/schema/numberplanmodifier/use_state_for_unknown_test.go index 982f17854..b910a4335 100644 --- a/resource/schema/numberplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/numberplanmodifier/use_state_for_unknown_test.go @@ -122,8 +122,6 @@ func TestUseStateForUnknownModifierPlanModifyNumber(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/object_attribute_test.go b/resource/schema/object_attribute_test.go index 8f1b6cc3c..908c48395 100644 --- a/resource/schema/object_attribute_test.go +++ b/resource/schema/object_attribute_test.go @@ -69,8 +69,6 @@ func TestObjectAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -117,8 +115,6 @@ func TestObjectAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -157,8 +153,6 @@ func TestObjectAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -191,8 +185,6 @@ func TestObjectAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -225,8 +217,6 @@ func TestObjectAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -259,8 +249,6 @@ func TestObjectAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -293,8 +281,6 @@ func TestObjectAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -327,8 +313,6 @@ func TestObjectAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -361,8 +345,6 @@ func TestObjectAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -395,8 +377,6 @@ func TestObjectAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -429,8 +409,6 @@ func TestObjectAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -494,8 +472,6 @@ func TestObjectAttributeObjectDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -528,8 +504,6 @@ func TestObjectAttributeObjectPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -562,8 +536,6 @@ func TestObjectAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -801,8 +773,6 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/objectdefault/static_value_test.go b/resource/schema/objectdefault/static_value_test.go index c6f14f8e6..5aae03793 100644 --- a/resource/schema/objectdefault/static_value_test.go +++ b/resource/schema/objectdefault/static_value_test.go @@ -45,8 +45,6 @@ func TestStaticValueDefaultObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/objectplanmodifier/requires_replace_if_configured_test.go b/resource/schema/objectplanmodifier/requires_replace_if_configured_test.go index abc413c0d..df267a1f4 100644 --- a/resource/schema/objectplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/objectplanmodifier/requires_replace_if_configured_test.go @@ -154,8 +154,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/objectplanmodifier/requires_replace_if_test.go b/resource/schema/objectplanmodifier/requires_replace_if_test.go index 5723167a5..15417ca49 100644 --- a/resource/schema/objectplanmodifier/requires_replace_if_test.go +++ b/resource/schema/objectplanmodifier/requires_replace_if_test.go @@ -165,8 +165,6 @@ func TestRequiresReplaceIfModifierPlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/objectplanmodifier/requires_replace_test.go b/resource/schema/objectplanmodifier/requires_replace_test.go index af62eb615..4dd051d4a 100644 --- a/resource/schema/objectplanmodifier/requires_replace_test.go +++ b/resource/schema/objectplanmodifier/requires_replace_test.go @@ -137,8 +137,6 @@ func TestRequiresReplaceModifierPlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/objectplanmodifier/use_state_for_unknown_test.go b/resource/schema/objectplanmodifier/use_state_for_unknown_test.go index f285194a0..f90747529 100644 --- a/resource/schema/objectplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/objectplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/schema_test.go b/resource/schema/schema_test.go index 7df9790ac..a48fed812 100644 --- a/resource/schema/schema_test.go +++ b/resource/schema/schema_test.go @@ -100,8 +100,6 @@ func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -269,7 +267,6 @@ func TestSchemaAttributeAtPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -390,7 +387,6 @@ func TestSchemaAttributeAtTerraformPath(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -448,8 +444,6 @@ func TestSchemaGetAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -504,8 +498,6 @@ func TestSchemaGetBlocks(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -542,8 +534,6 @@ func TestSchemaGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -580,8 +570,6 @@ func TestSchemaGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -618,8 +606,6 @@ func TestSchemaGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -659,8 +645,6 @@ func TestSchemaGetVersion(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -707,8 +691,6 @@ func TestSchemaType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -848,8 +830,6 @@ func TestSchemaTypeAtPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -981,8 +961,6 @@ func TestSchemaTypeAtTerraformPath(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1038,8 +1016,6 @@ func TestSchemaValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1351,8 +1327,6 @@ func TestSchemaValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/set_attribute_test.go b/resource/schema/set_attribute_test.go index 9f7c6d0ac..ebc7f291b 100644 --- a/resource/schema/set_attribute_test.go +++ b/resource/schema/set_attribute_test.go @@ -63,8 +63,6 @@ func TestSetAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -111,8 +109,6 @@ func TestSetAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +147,6 @@ func TestSetAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +179,6 @@ func TestSetAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -219,8 +211,6 @@ func TestSetAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -253,8 +243,6 @@ func TestSetAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -287,8 +275,6 @@ func TestSetAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -321,8 +307,6 @@ func TestSetAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -355,8 +339,6 @@ func TestSetAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -389,8 +371,6 @@ func TestSetAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -423,8 +403,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -484,8 +462,6 @@ func TestSetAttributeSetDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -518,8 +494,6 @@ func TestSetAttributeSetPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -552,8 +526,6 @@ func TestSetAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -737,8 +709,6 @@ func TestSetAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/set_nested_attribute_test.go b/resource/schema/set_nested_attribute_test.go index 8ea3cb58c..0e24d3f72 100644 --- a/resource/schema/set_nested_attribute_test.go +++ b/resource/schema/set_nested_attribute_test.go @@ -91,8 +91,6 @@ func TestSetNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +143,6 @@ func TestSetNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -236,8 +232,6 @@ func TestSetNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -276,8 +270,6 @@ func TestSetNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +308,6 @@ func TestSetNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -354,8 +344,6 @@ func TestSetNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -400,8 +388,6 @@ func TestSetNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -440,8 +426,6 @@ func TestSetNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -480,8 +464,6 @@ func TestSetNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -520,8 +502,6 @@ func TestSetNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -560,8 +540,6 @@ func TestSetNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -594,8 +572,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -655,8 +631,6 @@ func TestSetNestedAttributeSetDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -695,8 +669,6 @@ func TestSetNestedAttributeSetPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -735,8 +707,6 @@ func TestSetNestedAttributeSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1034,8 +1004,6 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/set_nested_block_test.go b/resource/schema/set_nested_block_test.go index 45d054366..4aee18302 100644 --- a/resource/schema/set_nested_block_test.go +++ b/resource/schema/set_nested_block_test.go @@ -87,8 +87,6 @@ func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -141,8 +139,6 @@ func TestSetNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -261,8 +257,6 @@ func TestSetNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -301,8 +295,6 @@ func TestSetNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -341,8 +333,6 @@ func TestSetNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -379,8 +369,6 @@ func TestSetNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -419,8 +407,6 @@ func TestSetNestedBlockSetPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -459,8 +445,6 @@ func TestSetNestedBlockSetValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -517,8 +501,6 @@ func TestSetNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -569,8 +551,6 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/setdefault/static_value_test.go b/resource/schema/setdefault/static_value_test.go index 6778995b5..418bda46c 100644 --- a/resource/schema/setdefault/static_value_test.go +++ b/resource/schema/setdefault/static_value_test.go @@ -41,8 +41,6 @@ func TestStaticValueDefaultSet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/setplanmodifier/requires_replace_if_configured_test.go b/resource/schema/setplanmodifier/requires_replace_if_configured_test.go index 7a8f06f19..350f37aa4 100644 --- a/resource/schema/setplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/setplanmodifier/requires_replace_if_configured_test.go @@ -154,8 +154,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/setplanmodifier/requires_replace_if_test.go b/resource/schema/setplanmodifier/requires_replace_if_test.go index 24a0c3684..b5c057f8d 100644 --- a/resource/schema/setplanmodifier/requires_replace_if_test.go +++ b/resource/schema/setplanmodifier/requires_replace_if_test.go @@ -165,8 +165,6 @@ func TestRequiresReplaceIfModifierPlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/setplanmodifier/requires_replace_test.go b/resource/schema/setplanmodifier/requires_replace_test.go index 731b9a195..36a8a9671 100644 --- a/resource/schema/setplanmodifier/requires_replace_test.go +++ b/resource/schema/setplanmodifier/requires_replace_test.go @@ -137,8 +137,6 @@ func TestRequiresReplaceModifierPlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/setplanmodifier/use_state_for_unknown_test.go b/resource/schema/setplanmodifier/use_state_for_unknown_test.go index 47a324dac..c55bc2034 100644 --- a/resource/schema/setplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/setplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifySet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/single_nested_attribute_test.go b/resource/schema/single_nested_attribute_test.go index f1b666566..b03725b76 100644 --- a/resource/schema/single_nested_attribute_test.go +++ b/resource/schema/single_nested_attribute_test.go @@ -89,8 +89,6 @@ func TestSingleNestedAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -180,8 +178,6 @@ func TestSingleNestedAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -218,8 +214,6 @@ func TestSingleNestedAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -256,8 +250,6 @@ func TestSingleNestedAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,8 +286,6 @@ func TestSingleNestedAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +320,6 @@ func TestSingleNestedAttributeGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -372,8 +360,6 @@ func TestSingleNestedAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -410,8 +396,6 @@ func TestSingleNestedAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -448,8 +432,6 @@ func TestSingleNestedAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -486,8 +468,6 @@ func TestSingleNestedAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -524,8 +504,6 @@ func TestSingleNestedAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -558,8 +536,6 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -623,8 +599,6 @@ func TestSingleNestedAttributeObjectDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -661,8 +635,6 @@ func TestSingleNestedAttributeObjectPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -699,8 +671,6 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -936,8 +906,6 @@ func TestSingleNestedAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/single_nested_block_test.go b/resource/schema/single_nested_block_test.go index 8216ca180..5c2c7a0c5 100644 --- a/resource/schema/single_nested_block_test.go +++ b/resource/schema/single_nested_block_test.go @@ -100,8 +100,6 @@ func TestSingleNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -152,8 +150,6 @@ func TestSingleNestedBlockGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -254,8 +250,6 @@ func TestSingleNestedBlockEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -292,8 +286,6 @@ func TestSingleNestedBlockGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -330,8 +322,6 @@ func TestSingleNestedBlockGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -380,8 +370,6 @@ func TestSingleNestedBlockGetNestedObject(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -418,8 +406,6 @@ func TestSingleNestedBlockObjectPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -456,8 +442,6 @@ func TestSingleNestedBlockObjectValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -510,8 +494,6 @@ func TestSingleNestedBlockType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/string_attribute_test.go b/resource/schema/string_attribute_test.go index 20f5ccf36..6bf70eda4 100644 --- a/resource/schema/string_attribute_test.go +++ b/resource/schema/string_attribute_test.go @@ -62,8 +62,6 @@ func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -110,8 +108,6 @@ func TestStringAttributeGetDeprecationMessage(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -145,8 +141,6 @@ func TestStringAttributeEqual(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -179,8 +173,6 @@ func TestStringAttributeGetDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -213,8 +205,6 @@ func TestStringAttributeGetMarkdownDescription(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -247,8 +237,6 @@ func TestStringAttributeGetType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -281,8 +269,6 @@ func TestStringAttributeIsComputed(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -315,8 +301,6 @@ func TestStringAttributeIsOptional(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +333,6 @@ func TestStringAttributeIsRequired(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -383,8 +365,6 @@ func TestStringAttributeIsSensitive(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -417,8 +397,6 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -464,8 +442,6 @@ func TestStringAttributeStringDefaultValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -498,8 +474,6 @@ func TestStringAttributeStringPlanModifiers(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,8 +506,6 @@ func TestStringAttributeStringValidators(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,8 +568,6 @@ func TestStringAttributeValidateImplementation(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/stringdefault/static_value_test.go b/resource/schema/stringdefault/static_value_test.go index 96b0a786e..6fb04b7fc 100644 --- a/resource/schema/stringdefault/static_value_test.go +++ b/resource/schema/stringdefault/static_value_test.go @@ -30,8 +30,6 @@ func TestStaticStringDefaultString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/stringplanmodifier/requires_replace_if_configured_test.go b/resource/schema/stringplanmodifier/requires_replace_if_configured_test.go index 2a2bb1988..e9c2dee01 100644 --- a/resource/schema/stringplanmodifier/requires_replace_if_configured_test.go +++ b/resource/schema/stringplanmodifier/requires_replace_if_configured_test.go @@ -151,8 +151,6 @@ func TestRequiresReplaceIfConfiguredModifierPlanModifyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/stringplanmodifier/requires_replace_if_test.go b/resource/schema/stringplanmodifier/requires_replace_if_test.go index 997949b5d..5473f6d75 100644 --- a/resource/schema/stringplanmodifier/requires_replace_if_test.go +++ b/resource/schema/stringplanmodifier/requires_replace_if_test.go @@ -162,8 +162,6 @@ func TestRequiresReplaceIfModifierPlanModifyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/stringplanmodifier/requires_replace_test.go b/resource/schema/stringplanmodifier/requires_replace_test.go index 7e3fe5659..1ec274e28 100644 --- a/resource/schema/stringplanmodifier/requires_replace_test.go +++ b/resource/schema/stringplanmodifier/requires_replace_test.go @@ -134,8 +134,6 @@ func TestRequiresReplaceModifierPlanModifyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/resource/schema/stringplanmodifier/use_state_for_unknown_test.go b/resource/schema/stringplanmodifier/use_state_for_unknown_test.go index 0bdd1f513..6a686469c 100644 --- a/resource/schema/stringplanmodifier/use_state_for_unknown_test.go +++ b/resource/schema/stringplanmodifier/use_state_for_unknown_test.go @@ -121,8 +121,6 @@ func TestUseStateForUnknownModifierPlanModifyString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/config_test.go b/tfsdk/config_test.go index 10955c5b5..313e98232 100644 --- a/tfsdk/config_test.go +++ b/tfsdk/config_test.go @@ -105,8 +105,6 @@ func TestConfigGet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -183,7 +181,6 @@ func TestConfigGetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +269,6 @@ func TestConfigPathMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/convert_test.go b/tfsdk/convert_test.go index ee3a6b27b..4bc4efb2b 100644 --- a/tfsdk/convert_test.go +++ b/tfsdk/convert_test.go @@ -52,7 +52,6 @@ func TestConvert(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/ephemeral_result_data_test.go b/tfsdk/ephemeral_result_data_test.go index d1cbc5a1a..4e2cbb4da 100644 --- a/tfsdk/ephemeral_result_data_test.go +++ b/tfsdk/ephemeral_result_data_test.go @@ -105,8 +105,6 @@ func TestEphemeralResultDataGet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -183,7 +181,6 @@ func TestEphemeralResultDataGetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +269,6 @@ func TestEphemeralResultDataPathMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -364,7 +359,6 @@ func TestEphemeralResultDataSet(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -466,7 +460,6 @@ func TestEphemeralResultDataSetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/plan_test.go b/tfsdk/plan_test.go index d256d33dd..a430b3bf7 100644 --- a/tfsdk/plan_test.go +++ b/tfsdk/plan_test.go @@ -105,8 +105,6 @@ func TestPlanGet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -183,7 +181,6 @@ func TestPlanGetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +269,6 @@ func TestPlanPathMatches(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -364,7 +359,6 @@ func TestPlanSet(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -466,7 +460,6 @@ func TestPlanSetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/state_test.go b/tfsdk/state_test.go index ced1e21f8..2d9e30ea0 100644 --- a/tfsdk/state_test.go +++ b/tfsdk/state_test.go @@ -105,8 +105,6 @@ func TestStateGet(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -183,7 +181,6 @@ func TestStateGetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -274,7 +271,6 @@ func TestStateSet(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -376,7 +372,6 @@ func TestStateSetAttribute(t *testing.T) { } for name, tc := range testCases { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/value_as_test.go b/tfsdk/value_as_test.go index a5e0cf80c..d7eba4184 100644 --- a/tfsdk/value_as_test.go +++ b/tfsdk/value_as_test.go @@ -377,7 +377,6 @@ func TestValueAs(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/tfsdk/value_from_test.go b/tfsdk/value_from_test.go index 5fe219e30..27bff23f0 100644 --- a/tfsdk/value_from_test.go +++ b/tfsdk/value_from_test.go @@ -199,7 +199,6 @@ func TestValueFrom(t *testing.T) { } for name, tc := range tests { - name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/bool_type_test.go b/types/basetypes/bool_type_test.go index 8d55d8309..2280dcfa3 100644 --- a/types/basetypes/bool_type_test.go +++ b/types/basetypes/bool_type_test.go @@ -42,7 +42,6 @@ func TestBoolTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/bool_value_test.go b/types/basetypes/bool_value_test.go index 001006891..b106a15f8 100644 --- a/types/basetypes/bool_value_test.go +++ b/types/basetypes/bool_value_test.go @@ -38,7 +38,6 @@ func TestBoolValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -156,7 +155,6 @@ func TestBoolValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -190,8 +188,6 @@ func TestBoolValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -226,8 +222,6 @@ func TestBoolValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -267,7 +261,6 @@ func TestBoolValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -305,8 +298,6 @@ func TestBoolValueValueBool(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -345,8 +336,6 @@ func TestBoolValueValueBoolPointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -377,8 +366,6 @@ func TestNewBoolPointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/dynamic_type_test.go b/types/basetypes/dynamic_type_test.go index 279a7d3d3..65fe89e34 100644 --- a/types/basetypes/dynamic_type_test.go +++ b/types/basetypes/dynamic_type_test.go @@ -38,7 +38,6 @@ func TestDynamicTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -402,7 +401,6 @@ func TestDynamicTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/dynamic_value_test.go b/types/basetypes/dynamic_value_test.go index ba8512d17..df0cb039d 100644 --- a/types/basetypes/dynamic_value_test.go +++ b/types/basetypes/dynamic_value_test.go @@ -43,8 +43,6 @@ func TestNewDynamicValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -158,7 +156,6 @@ func TestDynamicValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -385,7 +382,6 @@ func TestDynamicValueEqual(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -467,7 +463,6 @@ func TestDynamicValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -622,7 +617,6 @@ func TestDynamicValueIsUnderlyingValueNull(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -777,7 +771,6 @@ func TestDynamicValueIsUnderlyingValueUnknown(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/float32_type_test.go b/types/basetypes/float32_type_test.go index a409b35c5..49ff4960d 100644 --- a/types/basetypes/float32_type_test.go +++ b/types/basetypes/float32_type_test.go @@ -101,7 +101,6 @@ func TestFloat32TypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/float32_value_test.go b/types/basetypes/float32_value_test.go index 73781613f..9d6cc7fa5 100644 --- a/types/basetypes/float32_value_test.go +++ b/types/basetypes/float32_value_test.go @@ -60,7 +60,6 @@ func TestFloat32ValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -214,7 +213,6 @@ func TestFloat32ValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -248,8 +246,6 @@ func TestFloat32ValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -284,8 +280,6 @@ func TestFloat32ValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -352,7 +346,6 @@ func TestFloat32ValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -386,8 +379,6 @@ func TestFloat32ValueValueFloat32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -422,8 +413,6 @@ func TestFloat32ValueValueFloat32Pointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -454,8 +443,6 @@ func TestNewFloat32PointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -527,7 +514,6 @@ func TestFloat32ValueFloat32SemanticEquals(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/float64_type_test.go b/types/basetypes/float64_type_test.go index 4f1035254..2e86204d9 100644 --- a/types/basetypes/float64_type_test.go +++ b/types/basetypes/float64_type_test.go @@ -92,8 +92,6 @@ func TestFloat64TypeValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -189,7 +187,6 @@ func TestFloat64TypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/float64_value_test.go b/types/basetypes/float64_value_test.go index ca9dca6b9..37b5b6c55 100644 --- a/types/basetypes/float64_value_test.go +++ b/types/basetypes/float64_value_test.go @@ -72,7 +72,6 @@ func TestFloat64ValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -226,7 +225,6 @@ func TestFloat64ValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -260,8 +258,6 @@ func TestFloat64ValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -296,8 +292,6 @@ func TestFloat64ValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -357,7 +351,6 @@ func TestFloat64ValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -391,8 +384,6 @@ func TestFloat64ValueValueFloat64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -427,8 +418,6 @@ func TestFloat64ValueValueFloat64Pointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -459,8 +448,6 @@ func TestNewFloat64PointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -532,7 +519,6 @@ func TestFloat64ValueFloat64SemanticEquals(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/int32_type_test.go b/types/basetypes/int32_type_test.go index af5c8bdd0..f47ddd803 100644 --- a/types/basetypes/int32_type_test.go +++ b/types/basetypes/int32_type_test.go @@ -60,7 +60,6 @@ func TestInt32TypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/int32_value_test.go b/types/basetypes/int32_value_test.go index 15f505cac..89a366a64 100644 --- a/types/basetypes/int32_value_test.go +++ b/types/basetypes/int32_value_test.go @@ -37,7 +37,6 @@ func TestInt32ValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -115,7 +114,6 @@ func TestInt32ValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -149,8 +147,6 @@ func TestInt32ValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -185,8 +181,6 @@ func TestInt32ValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -238,7 +232,6 @@ func TestInt32ValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -272,8 +265,6 @@ func TestInt32ValueValueInt32(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -308,8 +299,6 @@ func TestInt32ValueValueInt32Pointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -340,8 +329,6 @@ func TestNewInt32PointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/int64_type_test.go b/types/basetypes/int64_type_test.go index f99866052..6eb115bc9 100644 --- a/types/basetypes/int64_type_test.go +++ b/types/basetypes/int64_type_test.go @@ -38,7 +38,6 @@ func TestInt64TypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/int64_value_test.go b/types/basetypes/int64_value_test.go index 8afbcc18a..ba2de237c 100644 --- a/types/basetypes/int64_value_test.go +++ b/types/basetypes/int64_value_test.go @@ -36,7 +36,6 @@ func TestInt64ValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -114,7 +113,6 @@ func TestInt64ValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -148,8 +146,6 @@ func TestInt64ValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -184,8 +180,6 @@ func TestInt64ValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -237,7 +231,6 @@ func TestInt64ValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -271,8 +264,6 @@ func TestInt64ValueValueInt64(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -307,8 +298,6 @@ func TestInt64ValueValueInt64Pointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -339,8 +328,6 @@ func TestNewInt64PointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/list_type_test.go b/types/basetypes/list_type_test.go index 6939a8f4b..9ecd65b82 100644 --- a/types/basetypes/list_type_test.go +++ b/types/basetypes/list_type_test.go @@ -30,8 +30,6 @@ func TestListTypeElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -96,7 +94,6 @@ func TestListTypeTerraformType(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -229,7 +226,6 @@ func TestListTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -299,7 +295,6 @@ func TestListTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -329,8 +324,6 @@ func TestListTypeString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/list_value_test.go b/types/basetypes/list_value_test.go index e63eb8088..2532d4bd4 100644 --- a/types/basetypes/list_value_test.go +++ b/types/basetypes/list_value_test.go @@ -66,8 +66,6 @@ func TestNewListValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -184,8 +182,6 @@ func TestNewListValueFrom(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -343,7 +339,6 @@ func TestListValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -395,8 +390,6 @@ func TestListValueElements(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -442,8 +435,6 @@ func TestListValueElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -635,7 +626,6 @@ func TestListValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -669,8 +659,6 @@ func TestListValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -705,8 +693,6 @@ func TestListValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -776,7 +762,6 @@ func TestListValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -845,7 +830,6 @@ func TestListValueType(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -899,8 +883,6 @@ func TestListTypeValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/map_type_test.go b/types/basetypes/map_type_test.go index f3bc1bd9f..d13edb259 100644 --- a/types/basetypes/map_type_test.go +++ b/types/basetypes/map_type_test.go @@ -31,8 +31,6 @@ func TestMapTypeElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -97,7 +95,6 @@ func TestMapTypeTerraformType(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -186,7 +183,6 @@ func TestMapTypeValueFromTerraform(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -276,7 +272,6 @@ func TestMapTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -306,8 +301,6 @@ func TestMapTypeString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/map_value_test.go b/types/basetypes/map_value_test.go index 0debecd9f..293fff102 100644 --- a/types/basetypes/map_value_test.go +++ b/types/basetypes/map_value_test.go @@ -66,8 +66,6 @@ func TestNewMapValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -184,8 +182,6 @@ func TestNewMapValueFrom(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -346,7 +342,6 @@ func TestMapValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -398,8 +393,6 @@ func TestMapValueElements(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -445,8 +438,6 @@ func TestMapValueElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -638,7 +629,6 @@ func TestMapValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -672,8 +662,6 @@ func TestMapValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -708,8 +696,6 @@ func TestMapValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -791,7 +777,6 @@ func TestMapValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -860,7 +845,6 @@ func TestMapValueType(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -914,8 +898,6 @@ func TestMapTypeValidate(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/number_type_test.go b/types/basetypes/number_type_test.go index 287e76035..153e8f9c2 100644 --- a/types/basetypes/number_type_test.go +++ b/types/basetypes/number_type_test.go @@ -39,7 +39,6 @@ func TestNumberTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/number_value_test.go b/types/basetypes/number_value_test.go index 27a25ac7d..08ca0c2bc 100644 --- a/types/basetypes/number_value_test.go +++ b/types/basetypes/number_value_test.go @@ -44,7 +44,6 @@ func TestNumberValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -167,7 +166,6 @@ func TestNumberValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -201,8 +199,6 @@ func TestNumberValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -237,8 +233,6 @@ func TestNumberValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -294,7 +288,6 @@ func TestNumberValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -332,8 +325,6 @@ func TestNumberValueValueBigFloat(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/object_type_test.go b/types/basetypes/object_type_test.go index 59d1ea838..0e1407578 100644 --- a/types/basetypes/object_type_test.go +++ b/types/basetypes/object_type_test.go @@ -220,7 +220,6 @@ func TestObjectTypeValueFromTerraform(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -398,7 +397,6 @@ func TestObjectTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -432,8 +430,6 @@ func TestObjectTypeString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/object_value_test.go b/types/basetypes/object_value_test.go index 73440f29c..4ccb41a41 100644 --- a/types/basetypes/object_value_test.go +++ b/types/basetypes/object_value_test.go @@ -153,8 +153,6 @@ func TestNewObjectValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -313,8 +311,6 @@ func TestNewObjectValueFrom(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -620,8 +616,6 @@ func TestObjectValueAttributes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -677,8 +671,6 @@ func TestObjectValueAttributeTypes(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1214,7 +1206,6 @@ func TestObjectValueToTerraformValue(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -1538,7 +1529,6 @@ func TestObjectValueEqual(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -1575,8 +1565,6 @@ func TestObjectValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1614,8 +1602,6 @@ func TestObjectValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1723,7 +1709,6 @@ func TestObjectValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -1828,7 +1813,6 @@ func TestObjectValueType(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/set_type_test.go b/types/basetypes/set_type_test.go index 4e645aa18..30b31222f 100644 --- a/types/basetypes/set_type_test.go +++ b/types/basetypes/set_type_test.go @@ -30,8 +30,6 @@ func TestSetTypeElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -96,7 +94,6 @@ func TestSetTypeTerraformType(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,7 +246,6 @@ func TestSetTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -319,7 +315,6 @@ func TestSetTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +344,6 @@ func TestSetTypeString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/set_value_test.go b/types/basetypes/set_value_test.go index 260382d0b..5dbfa25a7 100644 --- a/types/basetypes/set_value_test.go +++ b/types/basetypes/set_value_test.go @@ -285,7 +285,6 @@ func TestSetTypeValidate(t *testing.T) { }, } for name, testCase := range testCases { - name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() @@ -349,8 +348,6 @@ func TestNewSetValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -467,8 +464,6 @@ func TestNewSetValueFrom(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -596,7 +591,6 @@ func TestSetValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -648,8 +642,6 @@ func TestSetValueElements(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -695,8 +687,6 @@ func TestSetValueElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -888,7 +878,6 @@ func TestSetValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -922,8 +911,6 @@ func TestSetValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -958,8 +945,6 @@ func TestSetValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -1029,7 +1014,6 @@ func TestSetValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -1098,7 +1082,6 @@ func TestSetValueType(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/string_type_test.go b/types/basetypes/string_type_test.go index 097bb89c1..bcaf9e6d1 100644 --- a/types/basetypes/string_type_test.go +++ b/types/basetypes/string_type_test.go @@ -38,7 +38,6 @@ func TestStringTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() diff --git a/types/basetypes/string_value_test.go b/types/basetypes/string_value_test.go index 583fd2f20..92357fdce 100644 --- a/types/basetypes/string_value_test.go +++ b/types/basetypes/string_value_test.go @@ -34,7 +34,6 @@ func TestStringValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() ctx := context.Background() @@ -112,7 +111,6 @@ func TestStringValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -146,8 +144,6 @@ func TestStringValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -182,8 +178,6 @@ func TestStringValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -231,7 +225,6 @@ func TestStringValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -265,8 +258,6 @@ func TestStringValueValueString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -301,8 +292,6 @@ func TestStringValueValueStringPointer(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -333,8 +322,6 @@ func TestNewStringPointerValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/tuple_type_test.go b/types/basetypes/tuple_type_test.go index 65557abbd..18fa78005 100644 --- a/types/basetypes/tuple_type_test.go +++ b/types/basetypes/tuple_type_test.go @@ -98,7 +98,6 @@ func TestTupleTypeEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -151,8 +150,6 @@ func TestTupleTypeString(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -249,7 +246,6 @@ func TestTupleTypeTerraformType(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -438,7 +434,6 @@ func TestTupleTypeValueFromTerraform(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() diff --git a/types/basetypes/tuple_value_test.go b/types/basetypes/tuple_value_test.go index 34512babf..5736ed262 100644 --- a/types/basetypes/tuple_value_test.go +++ b/types/basetypes/tuple_value_test.go @@ -99,8 +99,6 @@ func TestNewTupleValue(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -142,8 +140,6 @@ func TestTupleValueElements(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -189,8 +185,6 @@ func TestTupleValueElementType(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -398,7 +392,6 @@ func TestTupleValueEqual(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -432,8 +425,6 @@ func TestTupleValueIsNull(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -468,8 +459,6 @@ func TestTupleValueIsUnknown(t *testing.T) { } for name, testCase := range testCases { - name, testCase := name, testCase - t.Run(name, func(t *testing.T) { t.Parallel() @@ -556,7 +545,6 @@ func TestTupleValueString(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -635,7 +623,6 @@ func TestTupleValueType(t *testing.T) { } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() @@ -717,7 +704,6 @@ func TestTupleValueToTerraformValue(t *testing.T) { }, } for name, test := range tests { - name, test := name, test t.Run(name, func(t *testing.T) { t.Parallel() From d91ccc11dd1f369d2fa18a40500cb67e30db3f49 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Wed, 12 Feb 2025 11:26:07 -0500 Subject: [PATCH 12/42] docs: Remove experimental verbiage from ephemeral resources (#1092) * docs: Remove experimental verbiage from ephemeral resources * add changelog --- .changes/unreleased/NOTES-20250212-105343.yaml | 6 ++++++ ephemeral/doc.go | 3 --- ephemeral/ephemeral_resource.go | 3 --- ephemeral/schema/doc.go | 3 --- provider/provider.go | 3 --- 5 files changed, 6 insertions(+), 12 deletions(-) create mode 100644 .changes/unreleased/NOTES-20250212-105343.yaml diff --git a/.changes/unreleased/NOTES-20250212-105343.yaml b/.changes/unreleased/NOTES-20250212-105343.yaml new file mode 100644 index 000000000..405d22b85 --- /dev/null +++ b/.changes/unreleased/NOTES-20250212-105343.yaml @@ -0,0 +1,6 @@ +kind: NOTES +body: 'ephemeral: Ephemeral resources are now considered generally available and protected + by compatibility promises.' +time: 2025-02-12T10:53:43.499303-05:00 +custom: + Issue: "1052" diff --git a/ephemeral/doc.go b/ephemeral/doc.go index 2537d610c..02b0e6e3a 100644 --- a/ephemeral/doc.go +++ b/ephemeral/doc.go @@ -20,7 +20,4 @@ // that has its own configuration and lifecycle logic. The [ephemeral.EphemeralResource] // implementations are referenced by the [provider.ProviderWithEphemeralResources] type // EphemeralResources method, which enables the ephemeral resource practitioner usage. -// -// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until -// these notices are removed. package ephemeral diff --git a/ephemeral/ephemeral_resource.go b/ephemeral/ephemeral_resource.go index c6cd2b32d..b15707f3b 100644 --- a/ephemeral/ephemeral_resource.go +++ b/ephemeral/ephemeral_resource.go @@ -24,9 +24,6 @@ import ( // HashiCorp Vault leases, which can be renewed without changing their data. // // - Close: Allows providers to clean up the ephemeral resource via EphemeralResourceWithClose. -// -// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until -// these notices are removed. type EphemeralResource interface { // Metadata should return the full name of the ephemeral resource, such as // examplecloud_thing. diff --git a/ephemeral/schema/doc.go b/ephemeral/schema/doc.go index 93c0835e2..12f7c3362 100644 --- a/ephemeral/schema/doc.go +++ b/ephemeral/schema/doc.go @@ -5,7 +5,4 @@ // Ephemeral resource schemas define the structure and value types for configuration // and result data. Schemas are implemented via the ephemeral.EphemeralResource type // Schema method. -// -// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until -// these notices are removed. package schema diff --git a/provider/provider.go b/provider/provider.go index ba18927cb..61dfd39fe 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -90,9 +90,6 @@ type ProviderWithFunctions interface { // include ephemeral resources for usage in practitioner configurations. // // Ephemeral resources are supported in Terraform version 1.10 and later. -// -// NOTE: Ephemeral resource support is experimental and exposed without compatibility promises until -// these notices are removed. type ProviderWithEphemeralResources interface { Provider From 0724df105602e6b6676e201b7c0c5e1d187df990 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Thu, 13 Feb 2025 18:10:34 -0500 Subject: [PATCH 13/42] Improve dynamic attribute internal validation handling (#1090) * Remove unused `RequiredWriteOnlyNilsAttributePaths()` function * Remove dynamic type conditional from write_only_nullification.go * Add a link to private state page * Add a return statement after diagnostic * Add validation handling for dynamic underlying null values * Remove null check * Update logic to check null/unknowness in both the container and underlying value * Add changelog entry --- .../unreleased/BUG FIXES-20250213-164700.yaml | 5 + internal/fwserver/attribute_validation.go | 59 ++-- .../fwserver/attribute_validation_test.go | 91 ++++++ .../fwserver/server_planresourcechange.go | 63 +--- .../server_planresourcechange_test.go | 300 ------------------ .../fwserver/server_upgraderesourcestate.go | 11 - internal/fwserver/write_only_nullification.go | 9 - .../fwserver/write_only_nullification_test.go | 37 +-- .../resources/write-only-arguments.mdx | 2 +- 9 files changed, 142 insertions(+), 435 deletions(-) create mode 100644 .changes/unreleased/BUG FIXES-20250213-164700.yaml diff --git a/.changes/unreleased/BUG FIXES-20250213-164700.yaml b/.changes/unreleased/BUG FIXES-20250213-164700.yaml new file mode 100644 index 000000000..3c49a6f81 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20250213-164700.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'internal/fwserver: Fixed bug where dynamic attributes would not prompt invalid configuration error messages' +time: 2025-02-13T16:47:00.4821-05:00 +custom: + Issue: "1090" diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 3c2492b14..e040551ec 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -101,12 +101,30 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt return } + configHasNullValue := attributeConfig.IsNull() + configHasUnknownValue := attributeConfig.IsUnknown() + // If the value is dynamic, we still need to check if the underlying value is null or unknown + if dynamicValuable, isDynamic := attributeConfig.(basetypes.DynamicValuable); !configHasNullValue && !configHasUnknownValue && isDynamic { + dynamicConfigVal, diags := dynamicValuable.ToDynamicValue(ctx) + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + if dynamicConfigVal.IsUnderlyingValueNull() { + configHasNullValue = true + } + + if dynamicConfigVal.IsUnderlyingValueUnknown() { + configHasUnknownValue = true + } + } + // Terraform CLI does not automatically perform certain configuration // checks yet. If it eventually does, this logic should remain at least // until Terraform CLI versions 0.12 through the release containing the // checks are considered end-of-life. // Reference: https://github.com/hashicorp/terraform/issues/30669 - if a.IsComputed() && !a.IsOptional() && !attributeConfig.IsNull() { + if a.IsComputed() && !a.IsOptional() && !configHasNullValue { resp.Diagnostics.AddAttributeError( req.AttributePath, "Invalid Configuration for Read-Only Attribute", @@ -120,7 +138,7 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt // until Terraform CLI versions 0.12 through the release containing the // checks are considered end-of-life. // Reference: https://github.com/hashicorp/terraform/issues/30669 - if a.IsRequired() && attributeConfig.IsNull() { + if a.IsRequired() && configHasNullValue { resp.Diagnostics.AddAttributeError( req.AttributePath, "Missing Configuration for Required Attribute", @@ -134,14 +152,13 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt // // Write-only attributes can only be successfully used with a supporting client, so the only option for a practitoner to utilize a write-only attribute // is to upgrade their Terraform CLI version to v1.11.0 or later. - if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !attributeConfig.IsNull() { + if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !configHasNullValue { resp.Diagnostics.AddAttributeError( req.AttributePath, "WriteOnly Attribute Not Allowed", fmt.Sprintf("The resource contains a non-null value for WriteOnly attribute %s. Write-only attributes are only supported in Terraform 1.11 and later.", req.AttributePath.String()), ) } - req.AttributeConfig = attributeConfig switch attributeWithValidators := a.(type) { @@ -174,33 +191,13 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt AttributeValidateNestedAttributes(ctx, a, req, resp) // Show deprecation warnings only for known values. - if a.GetDeprecationMessage() != "" && !attributeConfig.IsNull() && !attributeConfig.IsUnknown() { - // Dynamic values need to perform more logic to check the config value for null/unknown-ness - dynamicValuable, ok := attributeConfig.(basetypes.DynamicValuable) - if !ok { - resp.Diagnostics.AddAttributeWarning( - req.AttributePath, - "Attribute Deprecated", - a.GetDeprecationMessage(), - ) - return - } - - dynamicConfigVal, diags := dynamicValuable.ToDynamicValue(ctx) - resp.Diagnostics.Append(diags...) - if diags.HasError() { - return - } - - // For dynamic values, it's possible to be known when only the type is known. - // The underlying value can still be null or unknown, so check for that here - if !dynamicConfigVal.IsUnderlyingValueNull() && !dynamicConfigVal.IsUnderlyingValueUnknown() { - resp.Diagnostics.AddAttributeWarning( - req.AttributePath, - "Attribute Deprecated", - a.GetDeprecationMessage(), - ) - } + if a.GetDeprecationMessage() != "" && !configHasNullValue && !configHasUnknownValue { + resp.Diagnostics.AddAttributeWarning( + req.AttributePath, + "Attribute Deprecated", + a.GetDeprecationMessage(), + ) + return } } diff --git a/internal/fwserver/attribute_validation_test.go b/internal/fwserver/attribute_validation_test.go index 21f8349b5..04473e359 100644 --- a/internal/fwserver/attribute_validation_test.go +++ b/internal/fwserver/attribute_validation_test.go @@ -181,6 +181,29 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "config-computed-dynamic-underlying-null-value": { + req: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.DynamicPseudoType, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Computed: true, + Type: types.DynamicType, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{}, + }, "config-optional-computed-null": { req: ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -331,6 +354,38 @@ func TestAttributeValidate(t *testing.T) { }, resp: ValidateAttributeResponse{}, }, + "config-required-dynamic-underlying-null-value": { + req: ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.DynamicPseudoType, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Required: true, + Type: types.DynamicType, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Missing Configuration for Required Attribute", + "Must set a configuration value for the test attribute as the provider has marked it as required.\n\n"+ + "Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.", + ), + }, + }, + }, "no-validation": { req: ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -1763,6 +1818,42 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "write-only-attr-with-required-dynamic-underlying-null-value": { + req: ValidateAttributeRequest{ + ClientCapabilities: validator.ValidateSchemaClientCapabilities{ + WriteOnlyAttributesAllowed: true, + }, + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.DynamicPseudoType, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.DynamicType, + WriteOnly: true, + Required: true, + }, + }, + }, + }, + }, + resp: ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Missing Configuration for Required Attribute", + "Must set a configuration value for the test attribute as the provider has marked it as required.\n\n"+ + "Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.", + ), + }, + }, + }, "write-only-attr-with-optional": { req: ValidateAttributeRequest{ ClientCapabilities: validator.ValidateSchemaClientCapabilities{ diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index aacd2c998..bd81126f5 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/internal/fromtftypes" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/logging" @@ -338,6 +337,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange "This is always an issue in the Terraform Provider and should be reported to the provider developers.\n\n"+ "Ensure all resource plan modifiers do not attempt to change resource plan data from being a null value if the request plan is a null value.", ) + return } // Set any write-only attributes in the plan to null @@ -513,67 +513,6 @@ func NormaliseRequiresReplace(ctx context.Context, rs path.Paths) path.Paths { return ret[:j] } -// RequiredWriteOnlyNilsAttributePaths returns a tftypes.Walk() function -// that populates reqWriteOnlyNilsPaths with the paths of Required and WriteOnly -// attributes that have a null value. -func RequiredWriteOnlyNilsAttributePaths(ctx context.Context, schema fwschema.Schema, reqWriteOnlyNilsPaths *path.Paths) func(path *tftypes.AttributePath, value tftypes.Value) (bool, error) { - return func(attrPath *tftypes.AttributePath, value tftypes.Value) (bool, error) { - // we are only modifying attributes, not the entire resource - if len(attrPath.Steps()) < 1 { - return true, nil - } - - ctx = logging.FrameworkWithAttributePath(ctx, attrPath.String()) - - attribute, err := schema.AttributeAtTerraformPath(ctx, attrPath) - - if err != nil { - if errors.Is(err, fwschema.ErrPathInsideAtomicAttribute) { - // atomic attributes can be nested block objects that contain child writeOnly attributes - return true, nil - } - - if errors.Is(err, fwschema.ErrPathIsBlock) { - // blocks do not have the writeOnly field but can contain child writeOnly attributes - return true, nil - } - - if errors.Is(err, fwschema.ErrPathInsideDynamicAttribute) { - return false, nil - } - - logging.FrameworkError(ctx, "couldn't find attribute in resource schema") - return false, fmt.Errorf("couldn't find attribute in resource schema: %w", err) - } - - if attribute.IsWriteOnly() { - if attribute.IsRequired() && value.IsNull() { - fwPath, diags := fromtftypes.AttributePath(ctx, attrPath, schema) - if diags.HasError() { - for _, diagErr := range diags.Errors() { - logging.FrameworkError(ctx, - "Error converting tftypes.AttributePath to path.Path", - map[string]any{ - logging.KeyError: diagErr.Detail(), - }, - ) - } - - return false, fmt.Errorf("couldn't convert tftypes.AttributePath to path.Path") - } - reqWriteOnlyNilsPaths.Append(fwPath) - - // if the value is nil, there is no need to continue walking - return false, nil - } - // check for any writeOnly child attributes - return true, nil - } - - return false, nil - } -} - // planToState returns a *tfsdk.State with a copied value from a tfsdk.Plan. func planToState(plan tfsdk.Plan) *tfsdk.State { return &tfsdk.State{ diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 783fb2e51..282e223ed 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "math/big" - "sort" "testing" "github.com/google/go-cmp/cmp" @@ -364,305 +363,6 @@ func TestMarkComputedNilsAsUnknown(t *testing.T) { } } -func TestRequiredWriteOnlyNilsAttributePath(t *testing.T) { - t.Parallel() - - s := schema.Schema{ - Attributes: map[string]schema.Attribute{ - "string-value": schema.StringAttribute{ - Required: true, - }, - "string-nil-optional-writeonly": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "string-value-optional-writeonly": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "string-nil-required-writeonly": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-value-required-writeonly": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "list-value": schema.ListAttribute{ - ElementType: types.StringType, - Required: true, - }, - "list-nil-optional-writeonly": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - WriteOnly: true, - }, - "list-value-optional-writeonly": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - WriteOnly: true, - }, - "list-nil-required-writeonly": schema.ListAttribute{ - ElementType: types.StringType, - Required: true, - WriteOnly: true, - }, - "list-value-required-writeonly": schema.ListAttribute{ - ElementType: types.StringType, - Required: true, - WriteOnly: true, - }, - "dynamic-value": schema.DynamicAttribute{ - Required: true, - }, - "dynamic-nil-optional-writeonly": schema.DynamicAttribute{ - Optional: true, - WriteOnly: true, - }, - "dynamic-value-optional-writeonly": schema.DynamicAttribute{ - Optional: true, - WriteOnly: true, - }, - "dynamic-nil-required-writeonly": schema.DynamicAttribute{ - Required: true, - WriteOnly: true, - }, - "dynamic-value-required-writeonly": schema.DynamicAttribute{ - Required: true, - WriteOnly: true, - }, - // underlying values of dynamic attributes should be left alone - "dynamic-value-with-underlying-list-required-writeonly": schema.DynamicAttribute{ - Required: true, - WriteOnly: true, - }, - "object-nil-required-writeonly": schema.ObjectAttribute{ - AttributeTypes: map[string]attr.Type{ - "string-nil": types.StringType, - "string-set": types.StringType, - }, - Required: true, - WriteOnly: true, - }, - "object-value-required-writeonly": schema.ObjectAttribute{ - AttributeTypes: map[string]attr.Type{ - "string-nil": types.StringType, - "string-set": types.StringType, - }, - Required: true, - WriteOnly: true, - }, - "nested-nil-required-writeonly": schema.SingleNestedAttribute{ - Attributes: map[string]schema.Attribute{ - "string-nil": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-set": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - }, - Required: true, - WriteOnly: true, - }, - "nested-value-required-writeonly": schema.SingleNestedAttribute{ - Attributes: map[string]schema.Attribute{ - "string-nil": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-set": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - }, - Required: true, - WriteOnly: true, - }, - "optional-nested-value-required-writeonly-attributes": schema.SingleNestedAttribute{ - Attributes: map[string]schema.Attribute{ - "string-nil": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-set": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - }, - Optional: true, - WriteOnly: true, - }, - }, - Blocks: map[string]schema.Block{ - "block-nil-required-writeonly-attributes": schema.SetNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "string-nil": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-set": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - }, - }, - }, - "block-value-required-writeonly-attributes": schema.SetNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "string-nil": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - "string-set": schema.StringAttribute{ - Required: true, - WriteOnly: true, - }, - }, - }, - }, - }, - } - input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ - "string-value": tftypes.NewValue(tftypes.String, "hello, world"), - "string-nil-optional-writeonly": tftypes.NewValue(tftypes.String, nil), - "string-value-optional-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), - "string-nil-required-writeonly": tftypes.NewValue(tftypes.String, nil), - "string-value-required-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), - "list-value": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{tftypes.NewValue(tftypes.String, "hello, world")}), - "list-nil-optional-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, nil), - "list-value-optional-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{tftypes.NewValue(tftypes.String, "hello, world")}), - "list-nil-required-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, nil), - "list-value-required-writeonly": tftypes.NewValue(tftypes.List{ElementType: tftypes.String}, []tftypes.Value{ - tftypes.NewValue(tftypes.String, "hello, world"), - tftypes.NewValue(tftypes.String, nil), - }), - "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-nil-optional-writeonly": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-value-optional-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-nil-required-writeonly": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-value-required-writeonly": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-value-with-underlying-list-required-writeonly": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Bool, - }, - []tftypes.Value{ - tftypes.NewValue(tftypes.Bool, true), - tftypes.NewValue(tftypes.Bool, nil), - }, - ), - "object-nil-required-writeonly": tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, nil), - "object-value-required-writeonly": tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, map[string]tftypes.Value{ - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-set": tftypes.NewValue(tftypes.String, "foo"), - }), - "nested-nil-required-writeonly": tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, nil), - "nested-value-required-writeonly": tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, map[string]tftypes.Value{ - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-set": tftypes.NewValue(tftypes.String, "bar"), - }), - "optional-nested-value-required-writeonly-attributes": tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, map[string]tftypes.Value{ - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-set": tftypes.NewValue(tftypes.String, "bar"), - }), - "block-nil-required-writeonly-attributes": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, - }, nil), - "block-value-required-writeonly-attributes": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "string-nil": tftypes.String, - "string-set": tftypes.String, - }, - }, map[string]tftypes.Value{ - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-set": tftypes.NewValue(tftypes.String, "bar"), - }), - }), - }) - expected := path.Paths{ - path.Root("block-value-required-writeonly-attributes"). - AtSetValue(types.ObjectValueMust( - map[string]attr.Type{ - "string-nil": types.StringType, - "string-set": types.StringType, - }, - map[string]attr.Value{ - "string-nil": types.StringNull(), - "string-set": types.StringValue("bar"), - }, - )). - AtName("string-nil"), - path.Root("dynamic-nil-required-writeonly"), - path.Root("list-nil-required-writeonly"), - path.Root("nested-value-required-writeonly").AtName("string-nil"), - path.Root("object-nil-required-writeonly"), - path.Root("optional-nested-value-required-writeonly-attributes").AtName("string-nil"), - path.Root("string-nil-required-writeonly"), - path.Root("nested-nil-required-writeonly"), - } - - var got path.Paths - err := tftypes.Walk(input, fwserver.RequiredWriteOnlyNilsAttributePaths(context.Background(), s, &got)) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - - sort.Slice(got, func(i, j int) bool { - return got[i].String() < got[j].String() - }) - - sort.Slice(expected, func(i, j int) bool { - return expected[i].String() < expected[j].String() - }) - - if diff := cmp.Diff(got, expected, cmpopts.EquateEmpty()); diff != "" { - t.Errorf("Unexpected diff (+wanted, -got): %s", diff) - return - } -} - func TestNormaliseRequiresReplace(t *testing.T) { t.Parallel() diff --git a/internal/fwserver/server_upgraderesourcestate.go b/internal/fwserver/server_upgraderesourcestate.go index 536c3dfd0..e64a1339c 100644 --- a/internal/fwserver/server_upgraderesourcestate.go +++ b/internal/fwserver/server_upgraderesourcestate.go @@ -264,16 +264,5 @@ func (s *Server) UpgradeResourceState(ctx context.Context, req *UpgradeResourceS } upgradeResourceStateResponse.State.Raw = modifiedState - // If the write-only nullification results in a null state, then this is a provider error - if upgradeResourceStateResponse.State.Raw.Type() == nil || upgradeResourceStateResponse.State.Raw.IsNull() { - resp.Diagnostics.AddError( - "Missing Upgraded Resource State", - fmt.Sprintf("After attempting a resource state upgrade to version %d, the provider did not return any state data. ", req.Version)+ - "Preventing the unexpected loss of resource state data. "+ - "This is always an issue with the Terraform Provider and should be reported to the provider developer.", - ) - return - } - resp.UpgradedState = &upgradeResourceStateResponse.State } diff --git a/internal/fwserver/write_only_nullification.go b/internal/fwserver/write_only_nullification.go index 5bfc77609..0d95152e8 100644 --- a/internal/fwserver/write_only_nullification.go +++ b/internal/fwserver/write_only_nullification.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/logging" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) // NullifyWriteOnlyAttributes transforms a tftypes.Value, setting all write-only attribute values @@ -58,14 +57,6 @@ func NullifyWriteOnlyAttributes(ctx context.Context, resourceSchema fwschema.Sch // Value type from new state to create null with newValueType := attribute.GetType().TerraformType(ctx) - // If the attribute is dynamic set the new value type to DynamicPseudoType - // instead of the underlying concrete type - // TODO: verify if this is the correct behavior once Terraform Core implementation is complete - _, isDynamic := attribute.GetType().(basetypes.DynamicTypable) - if isDynamic { - newValueType = tftypes.DynamicPseudoType - } - if attribute.IsWriteOnly() && !val.IsNull() { logging.FrameworkDebug(ctx, "Nullifying write-only attribute in the newState") diff --git a/internal/fwserver/write_only_nullification_test.go b/internal/fwserver/write_only_nullification_test.go index 7aba06585..c73851772 100644 --- a/internal/fwserver/write_only_nullification_test.go +++ b/internal/fwserver/write_only_nullification_test.go @@ -39,9 +39,6 @@ func TestNullifyWriteOnlyAttributes(t *testing.T) { "dynamic-nil": schema.DynamicAttribute{ Optional: true, }, - "dynamic-underlying-string-nil-computed": schema.DynamicAttribute{ - WriteOnly: true, - }, "dynamic-nil-write-only": schema.DynamicAttribute{ Optional: true, WriteOnly: true, @@ -131,15 +128,14 @@ func TestNullifyWriteOnlyAttributes(t *testing.T) { }, } input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ - "string-value": tftypes.NewValue(tftypes.String, "hello, world"), - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), - "string-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-underlying-string-nil-computed": tftypes.NewValue(tftypes.String, nil), - "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), + "string-value": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), + "string-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-write-only": tftypes.NewValue(tftypes.String, "hello, world"), "dynamic-value-with-underlying-list-write-only": tftypes.NewValue( tftypes.List{ ElementType: tftypes.Bool, @@ -207,15 +203,14 @@ func TestNullifyWriteOnlyAttributes(t *testing.T) { }), }) expected := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ - "string-value": tftypes.NewValue(tftypes.String, "hello, world"), - "string-nil": tftypes.NewValue(tftypes.String, nil), - "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), - "string-value-write-only": tftypes.NewValue(tftypes.String, nil), - "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), - "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-underlying-string-nil-computed": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), - "dynamic-value-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "string-value": tftypes.NewValue(tftypes.String, "hello, world"), + "string-nil": tftypes.NewValue(tftypes.String, nil), + "string-nil-write-only": tftypes.NewValue(tftypes.String, nil), + "string-value-write-only": tftypes.NewValue(tftypes.String, nil), + "dynamic-value": tftypes.NewValue(tftypes.String, "hello, world"), + "dynamic-nil": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-nil-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), + "dynamic-value-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), "dynamic-value-with-underlying-list-write-only": tftypes.NewValue(tftypes.DynamicPseudoType, nil), "object-nil-write-only": tftypes.NewValue(tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx index b418e34f0..79549b9cb 100644 --- a/website/docs/plugin/framework/resources/write-only-arguments.mdx +++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx @@ -109,4 +109,4 @@ Since write-only arguments have no prior values, user intent or value changes ca - Pair write-only arguments with a configuration attribute (required or optional) to “trigger” the use of the write-only argument - For example, a `password_wo` write-only argument can be paired with a configured `password_wo_version` attribute. When the `password_wo_version` is modified, the provider will send the `password_wo` value to the API. - Use a keepers attribute (which is used in the [Random Provider](https://registry.terraform.io/providers/hashicorp/random/latest/docs#resource-keepers)) that will take in arbitrary key-pair values. Whenever there is a change to the `keepers` attribute, the provider will use the write-only argument value. -- Use the resource's [private state] to store secure hashes of write-only argument values, the provider will then use the hash to determine if a write-only argument value has changed in later Terraform runs. \ No newline at end of file +- Use the resource's [private state](/terraform/plugin/framework/resources/private-state) to store secure hashes of write-only argument values, the provider will then use the hash to determine if a write-only argument value has changed in later Terraform runs. \ No newline at end of file From ecd80f67daed0b92b243ae59bb1ee2077f8077c7 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 18 Feb 2025 18:45:30 -0500 Subject: [PATCH 14/42] resource/schema: Add validation to prevent write-only attributes in sets and set based data (#1095) * Add validation to prevent write-only attributes in sets, set nested blocks, and set nested attributes * Update website documentation --- .../write_only_nested_attribute_validation.go | 48 + .../fwserver/write_only_nullification_test.go | 446 ---------- resource/schema/set_attribute.go | 14 +- resource/schema/set_attribute_test.go | 6 - resource/schema/set_nested_attribute.go | 25 +- resource/schema/set_nested_attribute_test.go | 67 +- resource/schema/set_nested_block.go | 7 +- resource/schema/set_nested_block_test.go | 29 +- ...e_only_nested_attribute_validation_test.go | 830 ++++++++++++++---- .../handling-data/attributes/set-nested.mdx | 14 - .../handling-data/attributes/set.mdx | 12 - .../resources/write-only-arguments.mdx | 10 +- 12 files changed, 786 insertions(+), 722 deletions(-) diff --git a/internal/fwschema/write_only_nested_attribute_validation.go b/internal/fwschema/write_only_nested_attribute_validation.go index 4cba87413..bb4812cb2 100644 --- a/internal/fwschema/write_only_nested_attribute_validation.go +++ b/internal/fwschema/write_only_nested_attribute_validation.go @@ -52,6 +52,34 @@ func ContainsAnyWriteOnlyChildAttributes(nestedAttr NestedAttribute) bool { return false } +// BlockContainsAnyWriteOnlyChildAttributes will return true if any child attribute for the +// given nested block has WriteOnly set to true. +func BlockContainsAnyWriteOnlyChildAttributes(block Block) bool { + nestedObjAttrs := block.GetNestedObject().GetAttributes() + nestedObjBlocks := block.GetNestedObject().GetBlocks() + + for _, childAttr := range nestedObjAttrs { + if childAttr.IsWriteOnly() { + return true + } + + nestedAttribute, ok := childAttr.(NestedAttribute) + if ok { + if ContainsAnyWriteOnlyChildAttributes(nestedAttribute) { + return true + } + } + } + + for _, childBlock := range nestedObjBlocks { + if BlockContainsAnyWriteOnlyChildAttributes(childBlock) { + return true + } + } + + return false +} + func InvalidWriteOnlyNestedAttributeDiag(attributePath path.Path) diag.Diagnostic { return diag.NewErrorDiagnostic( "Invalid Schema Implementation", @@ -62,6 +90,26 @@ func InvalidWriteOnlyNestedAttributeDiag(attributePath path.Path) diag.Diagnosti ) } +func InvalidSetNestedAttributeWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic { + return diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("%q is a set nested attribute that contains a WriteOnly child attribute.\n\n", attributePath)+ + "Every child attribute of a set nested attribute must have WriteOnly set to false.", + ) +} + +func SetBlockCollectionWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic { + return diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("%q is a set nested block that contains a WriteOnly child attribute.\n\n", attributePath)+ + "Every child attribute within a set nested block must have WriteOnly set to false.", + ) +} + func InvalidComputedNestedAttributeWithWriteOnlyDiag(attributePath path.Path) diag.Diagnostic { return diag.NewErrorDiagnostic( "Invalid Schema Implementation", diff --git a/internal/fwserver/write_only_nullification_test.go b/internal/fwserver/write_only_nullification_test.go index c73851772..47ca9780f 100644 --- a/internal/fwserver/write_only_nullification_test.go +++ b/internal/fwserver/write_only_nullification_test.go @@ -539,93 +539,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { Optional: true, WriteOnly: true, }, - "set-nested-attribute": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "nested-set-nested-attribute": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - }, - "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - }, - "set-nested-attribute-wo": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "nested-set-nested-attribute": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - }, - "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - WriteOnly: true, - }, }, Blocks: map[string]schema.Block{ "single-nested-block": schema.SingleNestedBlock{ @@ -788,92 +701,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { }, }, }, - "set-nested-block": schema.SetNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "nested-set-nested-attribute": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - }, - "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - WriteOnly: true, - }, - }, - Blocks: map[string]schema.Block{ - "nested-set-nested-block": schema.SetNestedBlock{ - NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - "nested-set-nested-attribute": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - }, - "nested-set-nested-attribute-wo": schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "nested-string": schema.StringAttribute{ - Optional: true, - }, - "nested-string-wo": schema.StringAttribute{ - Optional: true, - WriteOnly: true, - }, - }, - }, - Optional: true, - WriteOnly: true, - }, - }, - }, - }, - }, - }, - }, }, } input := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ @@ -1053,74 +880,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { }), }), }), - "set-nested-attribute": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - }), - }), - "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - }), - }), "single-nested-block": tftypes.NewValue(tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "nested-string": tftypes.String, @@ -1257,94 +1016,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { }), }), }), - "set-nested-block": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-block": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-block": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - "nested-set-nested-block": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, "foo-wo"), - }), - }), - }), - }), - }), - }), }) expected := tftypes.NewValue(s.Type().TerraformType(context.Background()), map[string]tftypes.Value{ "single-nested-attribute": tftypes.NewValue(tftypes.Object{ @@ -1450,45 +1121,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { }, }, }, nil), - "set-nested-attribute": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), - }), - }), - "set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, nil), "single-nested-block": tftypes.NewValue(tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "nested-string": tftypes.String, @@ -1609,84 +1241,6 @@ func TestNullifyWriteOnlyAttributes_NestedTypes(t *testing.T) { }), }), }), - "set-nested-block": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-block": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-block": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), - "nested-set-nested-block": tftypes.NewValue(tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, - }, []tftypes.Value{ - tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested-string": tftypes.String, - "nested-string-wo": tftypes.String, - "nested-set-nested-attribute": tftypes.Set{ElementType: nestedObjectType}, - "nested-set-nested-attribute-wo": tftypes.Set{ElementType: nestedObjectType}, - }, - }, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - "nested-set-nested-attribute": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, []tftypes.Value{ - tftypes.NewValue(nestedObjectType, map[string]tftypes.Value{ - "nested-string": tftypes.NewValue(tftypes.String, "foo"), - "nested-string-wo": tftypes.NewValue(tftypes.String, nil), - }), - }), - "nested-set-nested-attribute-wo": tftypes.NewValue(tftypes.Set{ElementType: nestedObjectType}, nil), - }), - }), - }), - }), }) got, err := tftypes.Transform(input, NullifyWriteOnlyAttributes(context.Background(), s)) if err != nil { diff --git a/resource/schema/set_attribute.go b/resource/schema/set_attribute.go index 843f58fb3..b65656dac 100644 --- a/resource/schema/set_attribute.go +++ b/resource/schema/set_attribute.go @@ -166,16 +166,6 @@ type SetAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Set - - // WriteOnly indicates that Terraform will not store this attribute value - // in the plan or state artifacts. - // If WriteOnly is true, either Optional or Required must also be true. - // WriteOnly cannot be set with Computed. - // - // This functionality is only supported in Terraform 1.11 and later. - // Practitioners that choose a value for this attribute with older - // versions of Terraform will receive an error. - WriteOnly bool } // ApplyTerraform5AttributePathStep returns the result of stepping into a set @@ -240,9 +230,9 @@ func (a SetAttribute) IsSensitive() bool { return a.Sensitive } -// IsWriteOnly returns the WriteOnly field value. +// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data. func (a SetAttribute) IsWriteOnly() bool { - return a.WriteOnly + return false } // SetDefaultValue returns the Default field value. diff --git a/resource/schema/set_attribute_test.go b/resource/schema/set_attribute_test.go index ebc7f291b..2795fbde8 100644 --- a/resource/schema/set_attribute_test.go +++ b/resource/schema/set_attribute_test.go @@ -394,12 +394,6 @@ func TestSetAttributeIsWriteOnly(t *testing.T) { attribute: schema.SetAttribute{}, expected: false, }, - "writeOnly": { - attribute: schema.SetAttribute{ - WriteOnly: true, - }, - expected: true, - }, } for name, testCase := range testCases { diff --git a/resource/schema/set_nested_attribute.go b/resource/schema/set_nested_attribute.go index 56c449307..5d408cf1a 100644 --- a/resource/schema/set_nested_attribute.go +++ b/resource/schema/set_nested_attribute.go @@ -173,19 +173,6 @@ type SetNestedAttribute struct { // computed and the value could be altered by other changes then a default // should be avoided and a plan modifier should be used instead. Default defaults.Set - - // WriteOnly indicates that Terraform will not store this attribute value - // in the plan or state artifacts. - // If WriteOnly is true, either Optional or Required must also be true. - // WriteOnly cannot be set with Computed. - // - // If WriteOnly is true for a nested attribute, all of its child attributes - // must also set WriteOnly to true and no child attribute can be Computed. - // - // This functionality is only supported in Terraform 1.11 and later. - // Practitioners that choose a value for this attribute with older - // versions of Terraform will receive an error. - WriteOnly bool } // ApplyTerraform5AttributePathStep returns the Attributes field value if step @@ -268,9 +255,9 @@ func (a SetNestedAttribute) IsSensitive() bool { return a.Sensitive } -// IsWriteOnly returns the WriteOnly field value. +// IsWriteOnly returns false as write-only attributes are not supported for sets and set-based data. func (a SetNestedAttribute) IsWriteOnly() bool { - return a.WriteOnly + return false } // SetDefaultValue returns the Default field value. @@ -297,12 +284,8 @@ func (a SetNestedAttribute) ValidateImplementation(ctx context.Context, req fwsc resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path)) } - if a.IsWriteOnly() && !fwschema.ContainsAllWriteOnlyChildAttributes(a) { - resp.Diagnostics.Append(fwschema.InvalidWriteOnlyNestedAttributeDiag(req.Path)) - } - - if a.IsComputed() && fwschema.ContainsAnyWriteOnlyChildAttributes(a) { - resp.Diagnostics.Append(fwschema.InvalidComputedNestedAttributeWithWriteOnlyDiag(req.Path)) + if fwschema.ContainsAnyWriteOnlyChildAttributes(a) { + resp.Diagnostics.Append(fwschema.InvalidSetNestedAttributeWithWriteOnlyDiag(req.Path)) } if a.SetDefaultValue() != nil { diff --git a/resource/schema/set_nested_attribute_test.go b/resource/schema/set_nested_attribute_test.go index 0e24d3f72..a0210f410 100644 --- a/resource/schema/set_nested_attribute_test.go +++ b/resource/schema/set_nested_attribute_test.go @@ -563,12 +563,6 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { attribute: schema.SetNestedAttribute{}, expected: false, }, - "writeOnly": { - attribute: schema.SetNestedAttribute{ - WriteOnly: true, - }, - expected: true, - }, } for name, testCase := range testCases { @@ -913,9 +907,9 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }, }, }, - "writeOnly-with-child-writeOnly-no-error-diagnostic": { + "child-writeOnly-attribute-error-diagnostic": { attribute: schema.SetNestedAttribute{ - WriteOnly: true, + Optional: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "test_attr": schema.StringAttribute{ @@ -928,59 +922,32 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { Name: "test", Path: path.Root("test"), }, - expected: &fwschema.ValidateImplementationResponse{}, - }, - "writeOnly-without-child-writeOnly-error-diagnostic": { - attribute: schema.SetNestedAttribute{ - WriteOnly: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "test_attr": schema.StringAttribute{ - Computed: true, - }, - }, - }, - }, - request: fwschema.ValidateImplementationRequest{ - Name: "test", - Path: path.Root("test"), - }, expected: &fwschema.ValidateImplementationResponse{ Diagnostics: diag.Diagnostics{ diag.NewErrorDiagnostic( "Invalid Schema Implementation", "When validating the schema, an implementation issue was found. "+ "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ - "\"test\" is a WriteOnly nested attribute that contains a non-WriteOnly child attribute.\n\n"+ - "Every child attribute of a WriteOnly nested attribute must also have WriteOnly set to true.", + "\"test\" is a set nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a set nested attribute must have WriteOnly set to false.", ), }, }, }, - "computed-without-child-writeOnly-no-error-diagnostic": { + "nested-attribute-with-child-writeOnly-attribute-error-diagnostic": { attribute: schema.SetNestedAttribute{ - Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "test_attr": schema.StringAttribute{ - Computed: true, - }, - }, - }, - }, - request: fwschema.ValidateImplementationRequest{ - Name: "test", - Path: path.Root("test"), - }, - expected: &fwschema.ValidateImplementationResponse{}, - }, - "computed-with-child-writeOnly-error-diagnostic": { - attribute: schema.SetNestedAttribute{ - Computed: true, + Optional: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ - "test_attr": schema.StringAttribute{ - WriteOnly: true, + "test_nested_set": schema.SetNestedAttribute{ + Optional: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, }, }, }, @@ -995,8 +962,8 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { "Invalid Schema Implementation", "When validating the schema, an implementation issue was found. "+ "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ - "\"test\" is a Computed nested attribute that contains a WriteOnly child attribute.\n\n"+ - "Every child attribute of a Computed nested attribute must have WriteOnly set to false.", + "\"test\" is a set nested attribute that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute of a set nested attribute must have WriteOnly set to false.", ), }, }, diff --git a/resource/schema/set_nested_block.go b/resource/schema/set_nested_block.go index 78de8afb1..6b4b728bc 100644 --- a/resource/schema/set_nested_block.go +++ b/resource/schema/set_nested_block.go @@ -7,6 +7,8 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" @@ -15,7 +17,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) // Ensure the implementation satisifies the desired interfaces. @@ -223,6 +224,10 @@ func (b SetNestedBlock) Type() attr.Type { // errors or panics. This logic runs during the GetProviderSchema RPC and // should never include false positives. func (b SetNestedBlock) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if fwschema.BlockContainsAnyWriteOnlyChildAttributes(b) { + resp.Diagnostics.Append(fwschema.SetBlockCollectionWithWriteOnlyDiag(req.Path)) + } + if b.CustomType == nil && fwtype.ContainsCollectionWithDynamic(b.Type()) { resp.Diagnostics.Append(fwtype.BlockCollectionWithDynamicTypeDiag(req.Path)) } diff --git a/resource/schema/set_nested_block_test.go b/resource/schema/set_nested_block_test.go index 4aee18302..409e5d0e5 100644 --- a/resource/schema/set_nested_block_test.go +++ b/resource/schema/set_nested_block_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" @@ -20,7 +22,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestSetNestedBlockApplyTerraform5AttributePathStep(t *testing.T) { @@ -548,6 +549,32 @@ func TestSetNestedBlockValidateImplementation(t *testing.T) { }, }, }, + "nestedobject-write-only": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "test_attr": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Schema Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is a set nested block that contains a WriteOnly child attribute.\n\n"+ + "Every child attribute within a set nested block must have WriteOnly set to false.", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/schema/write_only_nested_attribute_validation_test.go b/resource/schema/write_only_nested_attribute_validation_test.go index acc2ca1de..b14b97931 100644 --- a/resource/schema/write_only_nested_attribute_validation_test.go +++ b/resource/schema/write_only_nested_attribute_validation_test.go @@ -7,14 +7,13 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" - "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" ) func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { t.Parallel() tests := map[string]struct { - nestedAttr metaschema.NestedAttribute + nestedAttr schema.NestedAttribute expected bool }{ "empty nested attribute returns true": { @@ -138,7 +137,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { }, }, "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: false, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ @@ -203,7 +201,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { }, "set nested attribute with multiple writeOnly child attributes returns true": { nestedAttr: schema.SetNestedAttribute{ - WriteOnly: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ @@ -219,7 +216,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { }, "set nested attribute with one non-writeOnly child attribute returns false": { nestedAttr: schema.SetNestedAttribute{ - WriteOnly: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ @@ -233,107 +229,6 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { }, expected: false, }, - "set nested attribute with writeOnly child nested attributes returns true": { - nestedAttr: schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: true, - }, - }, - }, - }, - }, - }, - }, - expected: true, - }, - "set nested attribute with non-writeOnly child nested attribute returns false": { - nestedAttr: schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: false, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: true, - }, - }, - }, - }, - }, - }, - }, - expected: false, - }, - "set nested attribute with one non-writeOnly child nested attribute returns false": { - nestedAttr: schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: true, - }, - }, - }, - }, - "list_nested_attribute": schema.ListNestedAttribute{ - WriteOnly: false, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: true, - }, - }, - }, - }, - }, - }, - }, - expected: false, - }, - "set nested attribute with one non-writeOnly nested child attribute returns false": { - nestedAttr: schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: false, - }, - }, - }, - }, - }, - }, - }, - expected: false, - }, "map nested attribute with writeOnly child attribute returns true": { nestedAttr: schema.MapNestedAttribute{ NestedObject: schema.NestedAttributeObject{ @@ -634,7 +529,7 @@ func TestContainsAllWriteOnlyChildAttributes(t *testing.T) { func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { t.Parallel() tests := map[string]struct { - nestedAttr metaschema.NestedAttribute + nestedAttr schema.NestedAttribute expected bool }{ "empty nested attribute returns false": { @@ -763,7 +658,6 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { }, }, "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: false, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ @@ -863,16 +757,15 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ - WriteOnly: false, - Computed: true, + WriteOnly: true, + Optional: true, }, "float32_attribute": schema.Float32Attribute{ - WriteOnly: false, - Computed: true, + WriteOnly: true, + Optional: true, }, }, }, @@ -882,7 +775,7 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { }, expected: true, }, - "set nested attribute with non-writeOnly child nested attribute returns false": { + "set nested attribute with no writeOnly child nested attributes returns false": { nestedAttr: schema.SetNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ @@ -905,51 +798,11 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { }, expected: false, }, - "set nested attribute with one non-writeOnly child nested attribute returns true": { - nestedAttr: schema.SetNestedAttribute{ - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: false, - Computed: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: false, - Computed: true, - }, - }, - }, - }, - "list_nested_attribute": schema.ListNestedAttribute{ - WriteOnly: false, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "string_attribute": schema.StringAttribute{ - WriteOnly: false, - Computed: true, - }, - "float32_attribute": schema.Float32Attribute{ - WriteOnly: false, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - expected: true, - }, - "set nested attribute with one non-writeOnly nested child attribute returns true": { + "set nested attribute with one writeOnly nested child attribute returns true": { nestedAttr: schema.SetNestedAttribute{ NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "set_nested_attribute": schema.SetNestedAttribute{ - WriteOnly: false, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "string_attribute": schema.StringAttribute{ @@ -1279,3 +1132,670 @@ func TestContainsAnyWriteOnlyChildAttributes(t *testing.T) { }) } } + +func TestBlockContainsAnyWriteOnlyChildAttributes(t *testing.T) { + t.Parallel() + tests := map[string]struct { + block schema.Block + expected bool + }{ + "empty nested block returns false": { + block: schema.ListNestedBlock{}, + expected: false, + }, + "list nested block with writeOnly child attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested block with non-writeOnly child attribute returns false": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "list nested block with multiple writeOnly child attributes returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "list nested block with one non-writeOnly child attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: true, + }, + "list nested block with writeOnly child nested attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list nested block with non-writeOnly child nested attribute returns false": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "list nested block with one non-writeOnly child nested attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list nested block with one non-writeOnly nested child attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list double-nested block with top-level writeOnly nested child attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_list_nested_block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "list double-nested block with one non-writeOnly nested child attribute returns true": { + block: schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_list_nested_block": schema.ListNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: false, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested block with non-writeOnly child attribute returns false": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: false, + }, + "set nested block with multiple writeOnly child attributes returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + expected: true, + }, + "set nested block with one non-writeOnly child attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + expected: true, + }, + "set nested block with writeOnly child nested attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested block with non-writeOnly child nested attribute returns false": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: false, + }, + "set nested block with one non-writeOnly child nested attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set nested block with one non-writeOnly nested child attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set double-nested block with top-level writeOnly nested child attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_set_nested_block": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "set double-nested block with one non-writeOnly nested child attribute returns true": { + block: schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_set_nested_block": schema.SetNestedBlock{ + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "set_nested_attribute": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested block with non-writeOnly child attribute returns false": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + }, + }, + expected: false, + }, + "single nested block with multiple writeOnly child attributes returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + expected: true, + }, + "single nested block with one non-writeOnly child attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + expected: true, + }, + "single nested block with writeOnly child nested attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested block with non-writeOnly child nested attribute returns false": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + expected: false, + }, + "single nested block with one non-writeOnly child nested attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + "list_nested_attribute": schema.ListNestedAttribute{ + WriteOnly: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + Computed: true, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + Computed: true, + }, + }, + }, + }, + }, + }, + expected: true, + }, + "single nested block with one non-writeOnly nested child attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + expected: true, + }, + "single double-nested block with top-level writeOnly nested child attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_single_nested_block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + expected: true, + }, + "single double-nested block with one non-writeOnly nested child attribute returns true": { + block: schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: false, + }, + }, + }, + }, + Blocks: map[string]schema.Block{ + "double_single_nested_block": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "single_nested_attribute": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "string_attribute": schema.StringAttribute{ + WriteOnly: false, + }, + "float32_attribute": schema.Float32Attribute{ + WriteOnly: true, + }, + }, + }, + }, + }, + }, + }, + expected: true, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + if got := fwschema.BlockContainsAnyWriteOnlyChildAttributes(tt.block); got != tt.expected { + t.Errorf("BlockContainsAllWriteOnlyChildAttributes() = %v, want %v", got, tt.expected) + } + }) + } +} diff --git a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx index 9e4f8afe9..01ce7e51f 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx @@ -159,20 +159,6 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. -### WriteOnly - - - - Only managed resources implement this concept. - - - -Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). -Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) -and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. - -If a nested attribute has the `WriteOnly` field set, all child attributes must also have `WriteOnly` set. - ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/handling-data/attributes/set.mdx b/website/docs/plugin/framework/handling-data/attributes/set.mdx index 9f3d7dcd3..512489aad 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set.mdx @@ -128,18 +128,6 @@ The [`setplanmodifier`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin Set the `Sensitive` field if the attribute value should always be considered [sensitive data](/terraform/language/state/sensitive-data). In Terraform, this will generally mask the value in practitioner output. This setting cannot be conditionally set and does not impact how data is stored in the state. -### WriteOnly - - - - Only managed resources implement this concept. - - - -Set the `WriteOnly` field to define a [write-only argument](/terraform/plugin/framework/resources/write-only-arguments). -Write-only arguments can accept [ephemeral values](/terraform/language/resources/ephemeral) -and are not persisted in the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. - ### Validation Set the `Validators` field to define [validation](/terraform/plugin/framework/validation#attribute-validation). This validation logic is ran in addition to any validation contained within a [custom type](#custom-types). diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx index 79549b9cb..ee61e2b99 100644 --- a/website/docs/plugin/framework/resources/write-only-arguments.mdx +++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx @@ -15,7 +15,9 @@ which should either use the value by making the appropriate change to the API or The following are high level differences between `Required`/`Optional` arguments and write-only arguments: -- Write-only arguments can accept ephemeral and non-ephemeral values +- Write-only arguments can accept ephemeral and non-ephemeral values. + +- Write-only arguments cannot be used with set attributes, set nested attributes, and set nested blocks. - Write-only argument values are only available in the configuration. The prior state, planned state, and final state values for write-only arguments should always be `null`. @@ -25,15 +27,15 @@ write-only arguments should always be `null`. - Write-only argument values cannot produce a Terraform plan difference. - This is because the prior state value for a write-only argument will always be `null` and the planned/final state value will also be `null`, therefore, it cannot produce a diff on its own. - - The one exception to this case is if the write-only argument is added to `requires_replace` during Plan Modification (i.e., using the [`RequiresReplace()`](/terraform/plugin/framework/resources/plan-modification#requiresreplace) plan modifier), in that case, the write-only argument will always cause a diff/trigger a resource recreation + - The one exception to this case is if the write-only argument is added to `requires_replace` during Plan Modification (i.e., using the [`RequiresReplace()`](/terraform/plugin/framework/resources/plan-modification#requiresreplace) plan modifier), in that case, the write-only argument will always cause a diff/trigger a resource recreation. - Since write-only arguments can accept ephemeral values, write-only argument configuration values are not expected to be consistent between plan and apply. ## Schema An attribute can be made write-only by setting the `WriteOnly` field to `true` in the schema. Attributes with `WriteOnly` set to `true` must also have -one of `Required` or `Optional` set to `true`. If a nested attribute has `WriteOnly` set to `true`, all child attributes must also have `WriteOnly` set to `true`. -`Computed` cannot be set to true for write-only arguments. +one of `Required` or `Optional` set to `true`. If a list nested, map nested, or single nested attribute has `WriteOnly` set to `true`, all child attributes must also have `WriteOnly` set to `true`. +A set nested block cannot have any child attributes with `WriteOnly` set to `true`. `Computed` cannot be set to true for write-only arguments. **Schema example:** From 8288f627f3e85d4a67c6ac6076daf65cb0bf58f1 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 08:31:39 -0500 Subject: [PATCH 15/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1094) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-go.yml | 2 +- .github/workflows/ci-goreleaser.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 82cd84f02..9934244e9 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - run: go mod download - - uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0 + - uses: golangci/golangci-lint-action@2e788936b09dd82dc280e845628a40d2ba6b204c # v6.3.1 terraform-provider-corner-tfprotov5: defaults: run: diff --git a/.github/workflows/ci-goreleaser.yml b/.github/workflows/ci-goreleaser.yml index 989b03503..2c62e130b 100644 --- a/.github/workflows/ci-goreleaser.yml +++ b/.github/workflows/ci-goreleaser.yml @@ -18,6 +18,6 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 + - uses: goreleaser/goreleaser-action@026299872805cb2db698e02dd7fb506a4da5122d # v6.2.0 with: args: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e72b8fc5..14c71529e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: cd .changes sed -e "1{/# /d;}" -e "2{/^$/d;}" ${{ needs.changelog-version.outputs.version }}.md > /tmp/release-notes.txt - - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0 + - uses: goreleaser/goreleaser-action@026299872805cb2db698e02dd7fb506a4da5122d # v6.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 7c9193d31400679ce0d6f815efd404ca03a5b642 Mon Sep 17 00:00:00 2001 From: hc-github-team-tf-provider-devex Date: Wed, 19 Feb 2025 15:01:52 +0000 Subject: [PATCH 16/42] Update changelog --- .changes/1.14.0.md | 16 ++++++++++++++++ .../unreleased/BUG FIXES-20250117-110109.yaml | 6 ------ .../unreleased/BUG FIXES-20250213-164700.yaml | 5 ----- .../unreleased/FEATURES-20250206-114700.yaml | 6 ------ .changes/unreleased/NOTES-20250206-114436.yaml | 5 ----- .changes/unreleased/NOTES-20250212-105343.yaml | 6 ------ CHANGELOG.md | 16 ++++++++++++++++ 7 files changed, 32 insertions(+), 28 deletions(-) create mode 100644 .changes/1.14.0.md delete mode 100644 .changes/unreleased/BUG FIXES-20250117-110109.yaml delete mode 100644 .changes/unreleased/BUG FIXES-20250213-164700.yaml delete mode 100644 .changes/unreleased/FEATURES-20250206-114700.yaml delete mode 100644 .changes/unreleased/NOTES-20250206-114436.yaml delete mode 100644 .changes/unreleased/NOTES-20250212-105343.yaml diff --git a/.changes/1.14.0.md b/.changes/1.14.0.md new file mode 100644 index 000000000..7d364f3b8 --- /dev/null +++ b/.changes/1.14.0.md @@ -0,0 +1,16 @@ +## 1.14.0 (February 19, 2025) + +NOTES: + +* Write-only attribute support is in technical preview and offered without compatibility promises until Terraform 1.11 is generally available. ([#1044](https://github.com/hashicorp/terraform-plugin-framework/issues/1044)) +* ephemeral: Ephemeral resources are now considered generally available and protected by compatibility promises. ([#1052](https://github.com/hashicorp/terraform-plugin-framework/issues/1052)) + +FEATURES: + +* resource/schema: Added `WriteOnly` schema field for managed resource schemas to indicate a write-only attribute. Write-only attribute values are not saved to the Terraform plan or state artifacts. ([#1044](https://github.com/hashicorp/terraform-plugin-framework/issues/1044)) + +BUG FIXES: + +* internal/fwschemadata: Set semantic equality logic has been adjusted and will now ignore order of elements during comparison. ([#1061](https://github.com/hashicorp/terraform-plugin-framework/issues/1061)) +* internal/fwserver: Fixed bug where dynamic attributes would not prompt invalid configuration error messages ([#1090](https://github.com/hashicorp/terraform-plugin-framework/issues/1090)) + diff --git a/.changes/unreleased/BUG FIXES-20250117-110109.yaml b/.changes/unreleased/BUG FIXES-20250117-110109.yaml deleted file mode 100644 index 8ce6b651d..000000000 --- a/.changes/unreleased/BUG FIXES-20250117-110109.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: BUG FIXES -body: 'internal/fwschemadata: Set semantic equality logic has been adjusted and will - now ignore order of elements during comparison.' -time: 2025-01-17T11:01:09.848503-05:00 -custom: - Issue: "1061" diff --git a/.changes/unreleased/BUG FIXES-20250213-164700.yaml b/.changes/unreleased/BUG FIXES-20250213-164700.yaml deleted file mode 100644 index 3c49a6f81..000000000 --- a/.changes/unreleased/BUG FIXES-20250213-164700.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: BUG FIXES -body: 'internal/fwserver: Fixed bug where dynamic attributes would not prompt invalid configuration error messages' -time: 2025-02-13T16:47:00.4821-05:00 -custom: - Issue: "1090" diff --git a/.changes/unreleased/FEATURES-20250206-114700.yaml b/.changes/unreleased/FEATURES-20250206-114700.yaml deleted file mode 100644 index ce306a4c9..000000000 --- a/.changes/unreleased/FEATURES-20250206-114700.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: FEATURES -body: 'resource/schema: Added `WriteOnly` schema field for managed resource schemas to indicate a write-only attribute. -Write-only attribute values are not saved to the Terraform plan or state artifacts.' -time: 2025-02-06T11:47:00.176842-05:00 -custom: - Issue: "1044" diff --git a/.changes/unreleased/NOTES-20250206-114436.yaml b/.changes/unreleased/NOTES-20250206-114436.yaml deleted file mode 100644 index 0ce8a7603..000000000 --- a/.changes/unreleased/NOTES-20250206-114436.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: NOTES -body: Write-only attribute support is in technical preview and offered without compatibility promises until Terraform 1.11 is generally available. -time: 2025-02-06T11:44:36.156747-05:00 -custom: - Issue: "1044" diff --git a/.changes/unreleased/NOTES-20250212-105343.yaml b/.changes/unreleased/NOTES-20250212-105343.yaml deleted file mode 100644 index 405d22b85..000000000 --- a/.changes/unreleased/NOTES-20250212-105343.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: NOTES -body: 'ephemeral: Ephemeral resources are now considered generally available and protected - by compatibility promises.' -time: 2025-02-12T10:53:43.499303-05:00 -custom: - Issue: "1052" diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f78f7fd..4492af198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 1.14.0 (February 19, 2025) + +NOTES: + +* Write-only attribute support is in technical preview and offered without compatibility promises until Terraform 1.11 is generally available. ([#1044](https://github.com/hashicorp/terraform-plugin-framework/issues/1044)) +* ephemeral: Ephemeral resources are now considered generally available and protected by compatibility promises. ([#1052](https://github.com/hashicorp/terraform-plugin-framework/issues/1052)) + +FEATURES: + +* resource/schema: Added `WriteOnly` schema field for managed resource schemas to indicate a write-only attribute. Write-only attribute values are not saved to the Terraform plan or state artifacts. ([#1044](https://github.com/hashicorp/terraform-plugin-framework/issues/1044)) + +BUG FIXES: + +* internal/fwschemadata: Set semantic equality logic has been adjusted and will now ignore order of elements during comparison. ([#1061](https://github.com/hashicorp/terraform-plugin-framework/issues/1061)) +* internal/fwserver: Fixed bug where dynamic attributes would not prompt invalid configuration error messages ([#1090](https://github.com/hashicorp/terraform-plugin-framework/issues/1090)) + ## 1.13.0 (October 31, 2024) NOTES: From 3000d8cd3b4383186cc5d27094d98b2a534904b1 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 19 Feb 2025 16:19:21 -0500 Subject: [PATCH 17/42] internal/fwserver: Move write-only nullification to earlier in PlanResourceChange (#1097) * Move write-only nullification so that it happens before marking computed attributes as unknown * Add changelog entry --- .../unreleased/BUG FIXES-20250219-152752.yaml | 5 + .../fwserver/server_planresourcechange.go | 24 ++-- .../server_planresourcechange_test.go | 109 ++++++++++++++++++ 3 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 .changes/unreleased/BUG FIXES-20250219-152752.yaml diff --git a/.changes/unreleased/BUG FIXES-20250219-152752.yaml b/.changes/unreleased/BUG FIXES-20250219-152752.yaml new file mode 100644 index 000000000..bbddefb0a --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20250219-152752.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'internal/fwserver: fixed bug where write-only attributes set in configuration would cause perpetual diffs for computed attributes.' +time: 2025-02-19T15:27:52.52508-05:00 +custom: + Issue: "1097" diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index bd81126f5..52b06688b 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -155,6 +155,18 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resp.PlannedState.Raw = data.TerraformValue } + // Set any write-only attributes in the plan to null + modifiedPlan, err := tftypes.Transform(resp.PlannedState.Raw, NullifyWriteOnlyAttributes(ctx, resp.PlannedState.Schema)) + if err != nil { + resp.Diagnostics.AddError( + "Error Modifying Planned State", + "There was an unexpected error modifying the PlannedState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), + ) + return + } + + resp.PlannedState.Raw = modifiedPlan + // After ensuring there are proposed changes, mark any computed attributes // that are null in the config as unknown in the plan, so providers have // the choice to update them. @@ -339,18 +351,6 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange ) return } - - // Set any write-only attributes in the plan to null - modifiedPlan, err := tftypes.Transform(resp.PlannedState.Raw, NullifyWriteOnlyAttributes(ctx, resp.PlannedState.Schema)) - if err != nil { - resp.Diagnostics.AddError( - "Error Modifying Planned State", - "There was an unexpected error modifying the PlannedState. This is always a problem with the provider. Please report the following to the provider developer:\n\n"+err.Error(), - ) - return - } - - resp.PlannedState.Raw = modifiedPlan } func MarkComputedNilsAsUnknown(ctx context.Context, config tftypes.Value, resourceSchema fwschema.Schema) func(*tftypes.AttributePath, tftypes.Value) (tftypes.Value, error) { diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index 282e223ed..f1607daeb 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -437,6 +437,14 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testSchemaTypeWriteOnly := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_computed": tftypes.String, + "test_required": tftypes.String, + "test_write_only": tftypes.String, + }, + } + testSchemaTypeDefault := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_computed_bool": tftypes.Bool, @@ -566,6 +574,21 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testSchemaWriteOnly := schema.Schema{ + Attributes: map[string]schema.Attribute{ + "test_computed": schema.StringAttribute{ + Computed: true, + }, + "test_required": schema.StringAttribute{ + Required: true, + }, + "test_write_only": schema.StringAttribute{ + Optional: true, + WriteOnly: true, + }, + }, + } + testSchemaDefault := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed_bool": schema.BoolAttribute{ @@ -1069,6 +1092,11 @@ func TestServerPlanResourceChange(t *testing.T) { Schema: testSchema, } + testEmptyStateWriteOnly := &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, nil), + Schema: testSchemaWriteOnly, + } + testEmptyStateDefault := &tfsdk.State{ Raw: tftypes.NewValue(testSchemaTypeDefault, nil), Schema: testSchemaDefault, @@ -1367,6 +1395,43 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "create-mark-computed-config-nils-as-unknown-write-only": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWriteOnly, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWriteOnly, + }, + PriorState: testEmptyStateWriteOnly, + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.Resource{}, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "create-set-default-values": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -3883,6 +3948,50 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "update-mark-computed-config-nils-as-unknown-write-only": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWriteOnly, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "prior-state-val"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, "test-write-only-value"), + }), + Schema: testSchemaWriteOnly, + }, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "prior-state-val"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + ResourceSchema: testSchemaWriteOnly, + Resource: &testprovider.Resource{}, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaTypeWriteOnly, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "prior-state-val"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + "test_write_only": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchemaWriteOnly, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "update-set-default-values": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, From 083644c9fda0e22aa7821709790517d5c09c37a8 Mon Sep 17 00:00:00 2001 From: hc-github-team-tf-provider-devex Date: Thu, 20 Feb 2025 14:44:35 +0000 Subject: [PATCH 18/42] Update changelog --- .changes/1.14.1.md | 6 ++++++ .changes/unreleased/BUG FIXES-20250219-152752.yaml | 5 ----- CHANGELOG.md | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 .changes/1.14.1.md delete mode 100644 .changes/unreleased/BUG FIXES-20250219-152752.yaml diff --git a/.changes/1.14.1.md b/.changes/1.14.1.md new file mode 100644 index 000000000..4d9041db4 --- /dev/null +++ b/.changes/1.14.1.md @@ -0,0 +1,6 @@ +## 1.14.1 (February 20, 2025) + +BUG FIXES: + +* internal/fwserver: fixed bug where write-only attributes set in configuration would cause perpetual diffs for computed attributes. ([#1097](https://github.com/hashicorp/terraform-plugin-framework/issues/1097)) + diff --git a/.changes/unreleased/BUG FIXES-20250219-152752.yaml b/.changes/unreleased/BUG FIXES-20250219-152752.yaml deleted file mode 100644 index bbddefb0a..000000000 --- a/.changes/unreleased/BUG FIXES-20250219-152752.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: BUG FIXES -body: 'internal/fwserver: fixed bug where write-only attributes set in configuration would cause perpetual diffs for computed attributes.' -time: 2025-02-19T15:27:52.52508-05:00 -custom: - Issue: "1097" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4492af198..7e7c35c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.14.1 (February 20, 2025) + +BUG FIXES: + +* internal/fwserver: fixed bug where write-only attributes set in configuration would cause perpetual diffs for computed attributes. ([#1097](https://github.com/hashicorp/terraform-plugin-framework/issues/1097)) + ## 1.14.0 (February 19, 2025) NOTES: From 30413adf50be9d75d6f860b1d07d551caae7b968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:00:22 -0500 Subject: [PATCH 19/42] build(deps): Bump github.com/hashicorp/copywrite in /tools (#1098) Bumps [github.com/hashicorp/copywrite](https://github.com/hashicorp/copywrite) from 0.20.0 to 0.21.0. - [Release notes](https://github.com/hashicorp/copywrite/releases) - [Changelog](https://github.com/hashicorp/copywrite/blob/main/.goreleaser.yaml) - [Commits](https://github.com/hashicorp/copywrite/compare/v0.20.0...v0.21.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/copywrite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index c7560ffca..7a3a2905f 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -2,7 +2,7 @@ module tools go 1.22.7 -require github.com/hashicorp/copywrite v0.20.0 +require github.com/hashicorp/copywrite v0.21.0 require ( github.com/AlecAivazis/survey/v2 v2.3.7 // indirect diff --git a/tools/go.sum b/tools/go.sum index e9c0dc344..6da31e8ee 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -146,8 +146,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/copywrite v0.20.0 h1:i+iNq4lWsGopKIhC0HfZjUvNAnXnU/Pc5e+4L5WF+1Y= -github.com/hashicorp/copywrite v0.20.0/go.mod h1:mu6DAyUI6m6vq8weoJn9a0HDuUUrV+0GQdRp4mD50yU= +github.com/hashicorp/copywrite v0.21.0 h1:IE8uByQdos8s0uAyHF4O8RHV5cJEhmIc+Awk+wkKXKI= +github.com/hashicorp/copywrite v0.21.0/go.mod h1:mu6DAyUI6m6vq8weoJn9a0HDuUUrV+0GQdRp4mD50yU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= From 1d90a2104b827cd6baac2460b6aeed9fe2c986c9 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:20:05 -0500 Subject: [PATCH 20/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1099) Co-authored-by: hashicorp-tsccr[bot] Co-authored-by: Selena Goods --- .github/workflows/ci-go.yml | 2 +- .github/workflows/ci-goreleaser.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 9934244e9..ff447cff9 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - run: go mod download - - uses: golangci/golangci-lint-action@2e788936b09dd82dc280e845628a40d2ba6b204c # v6.3.1 + - uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 # v6.5.0 terraform-provider-corner-tfprotov5: defaults: run: diff --git a/.github/workflows/ci-goreleaser.yml b/.github/workflows/ci-goreleaser.yml index 2c62e130b..76735ffb5 100644 --- a/.github/workflows/ci-goreleaser.yml +++ b/.github/workflows/ci-goreleaser.yml @@ -18,6 +18,6 @@ jobs: - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version-file: 'go.mod' - - uses: goreleaser/goreleaser-action@026299872805cb2db698e02dd7fb506a4da5122d # v6.2.0 + - uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # v6.2.1 with: args: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 14c71529e..eb7bc6abf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: cd .changes sed -e "1{/# /d;}" -e "2{/^$/d;}" ${{ needs.changelog-version.outputs.version }}.md > /tmp/release-notes.txt - - uses: goreleaser/goreleaser-action@026299872805cb2db698e02dd7fb506a4da5122d # v6.2.0 + - uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # v6.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 132c771a8b105239d2b75fca6f51c127f90749cf Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 3 Mar 2025 09:13:29 -0500 Subject: [PATCH 21/42] Add Callout for `PreferWriteOnlyAttribute()` validator in documentation (#1105) * Add callout for PreferWriteOnlyAttribute validators in the documentation * Update wording --- .../plugin/framework/resources/write-only-arguments.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx index ee61e2b99..58a3da1e0 100644 --- a/website/docs/plugin/framework/resources/write-only-arguments.mdx +++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx @@ -53,6 +53,13 @@ retrieving values from configuration. ## PreferWriteOnlyAttribute Validators + + + These validators will produce persistent warnings for practitioners on every Terraform run as long as the specified non-write-only attribute + has a value in the configuration. The validators will also produce warnings for users of shared modules who cannot immediately take action on the warning. + + + The `PreferWriteOnlyAttribute()` validators available in the [`terraform-plugin-framework-validators` Go module](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework-validators) can be used when you have a write-only version of an existing attribute, and you want to encourage practitioners to use the write-only version whenever possible. From dc2036d08fad3d5c3ac08242a9577f314feb6250 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Date: Tue, 4 Mar 2025 07:53:02 -0800 Subject: [PATCH 22/42] Migrate docs to web-unified-docs (#1106) * migrate docs to web-unified-docs * remove link in migration callout to resolve content checks * add link to unified docs repo in callout --- website/README.md | 3 +++ website/docs/plugin/framework/acctests.mdx | 3 +++ website/docs/plugin/framework/data-sources/configure.mdx | 3 +++ website/docs/plugin/framework/data-sources/index.mdx | 3 +++ website/docs/plugin/framework/data-sources/timeouts.mdx | 3 +++ .../plugin/framework/data-sources/validate-configuration.mdx | 3 +++ website/docs/plugin/framework/debugging.mdx | 3 +++ website/docs/plugin/framework/deprecations.mdx | 3 +++ website/docs/plugin/framework/diagnostics.mdx | 3 +++ website/docs/plugin/framework/ephemeral-resources/close.mdx | 3 +++ .../docs/plugin/framework/ephemeral-resources/configure.mdx | 3 +++ website/docs/plugin/framework/ephemeral-resources/index.mdx | 3 +++ website/docs/plugin/framework/ephemeral-resources/open.mdx | 3 +++ website/docs/plugin/framework/ephemeral-resources/renew.mdx | 3 +++ .../framework/ephemeral-resources/validate-configuration.mdx | 3 +++ website/docs/plugin/framework/functions/concepts.mdx | 3 +++ website/docs/plugin/framework/functions/documentation.mdx | 3 +++ website/docs/plugin/framework/functions/errors.mdx | 3 +++ website/docs/plugin/framework/functions/implementation.mdx | 3 +++ website/docs/plugin/framework/functions/index.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/bool.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/dynamic.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/float32.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/float64.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/index.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/int32.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/int64.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/list.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/map.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/number.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/object.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/set.mdx | 3 +++ website/docs/plugin/framework/functions/parameters/string.mdx | 3 +++ website/docs/plugin/framework/functions/returns/bool.mdx | 3 +++ website/docs/plugin/framework/functions/returns/dynamic.mdx | 3 +++ website/docs/plugin/framework/functions/returns/float32.mdx | 3 +++ website/docs/plugin/framework/functions/returns/float64.mdx | 3 +++ website/docs/plugin/framework/functions/returns/index.mdx | 3 +++ website/docs/plugin/framework/functions/returns/int32.mdx | 3 +++ website/docs/plugin/framework/functions/returns/int64.mdx | 3 +++ website/docs/plugin/framework/functions/returns/list.mdx | 3 +++ website/docs/plugin/framework/functions/returns/map.mdx | 3 +++ website/docs/plugin/framework/functions/returns/number.mdx | 3 +++ website/docs/plugin/framework/functions/returns/object.mdx | 3 +++ website/docs/plugin/framework/functions/returns/set.mdx | 3 +++ website/docs/plugin/framework/functions/returns/string.mdx | 3 +++ website/docs/plugin/framework/functions/testing.mdx | 3 +++ .../docs/plugin/framework/getting-started/code-walkthrough.mdx | 3 +++ .../docs/plugin/framework/handling-data/accessing-values.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/bool.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/dynamic.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/float32.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/float64.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/index.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/int32.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/int64.mdx | 3 +++ .../plugin/framework/handling-data/attributes/list-nested.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/list.mdx | 3 +++ .../plugin/framework/handling-data/attributes/map-nested.mdx | 3 +++ website/docs/plugin/framework/handling-data/attributes/map.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/number.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/object.mdx | 3 +++ .../plugin/framework/handling-data/attributes/set-nested.mdx | 3 +++ website/docs/plugin/framework/handling-data/attributes/set.mdx | 3 +++ .../framework/handling-data/attributes/single-nested.mdx | 3 +++ .../docs/plugin/framework/handling-data/attributes/string.mdx | 3 +++ website/docs/plugin/framework/handling-data/blocks/index.mdx | 3 +++ .../docs/plugin/framework/handling-data/blocks/list-nested.mdx | 3 +++ .../docs/plugin/framework/handling-data/blocks/set-nested.mdx | 3 +++ .../plugin/framework/handling-data/blocks/single-nested.mdx | 3 +++ website/docs/plugin/framework/handling-data/dynamic-data.mdx | 3 +++ .../docs/plugin/framework/handling-data/path-expressions.mdx | 3 +++ website/docs/plugin/framework/handling-data/paths.mdx | 3 +++ website/docs/plugin/framework/handling-data/schemas.mdx | 3 +++ .../docs/plugin/framework/handling-data/terraform-concepts.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/bool.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/custom.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/dynamic.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/float32.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/float64.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/index.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/int32.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/int64.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/list.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/map.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/number.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/object.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/set.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/string.mdx | 3 +++ website/docs/plugin/framework/handling-data/types/tuple.mdx | 3 +++ website/docs/plugin/framework/handling-data/writing-state.mdx | 3 +++ website/docs/plugin/framework/index.mdx | 3 +++ website/docs/plugin/framework/internals/index.mdx | 3 +++ website/docs/plugin/framework/internals/rpcs.mdx | 3 +++ .../framework/migrating/attributes-blocks/attribute-schema.mdx | 3 +++ .../framework/migrating/attributes-blocks/blocks-computed.mdx | 3 +++ .../plugin/framework/migrating/attributes-blocks/blocks.mdx | 3 +++ .../framework/migrating/attributes-blocks/default-values.mdx | 3 +++ .../plugin/framework/migrating/attributes-blocks/fields.mdx | 3 +++ .../plugin/framework/migrating/attributes-blocks/force-new.mdx | 3 +++ .../plugin/framework/migrating/attributes-blocks/types.mdx | 3 +++ .../migrating/attributes-blocks/validators-custom.mdx | 3 +++ .../migrating/attributes-blocks/validators-predefined.mdx | 3 +++ website/docs/plugin/framework/migrating/benefits.mdx | 3 +++ website/docs/plugin/framework/migrating/data-sources/index.mdx | 3 +++ .../docs/plugin/framework/migrating/data-sources/timeouts.mdx | 3 +++ website/docs/plugin/framework/migrating/index.mdx | 3 +++ website/docs/plugin/framework/migrating/mux.mdx | 3 +++ website/docs/plugin/framework/migrating/providers/index.mdx | 3 +++ website/docs/plugin/framework/migrating/resources/crud.mdx | 3 +++ website/docs/plugin/framework/migrating/resources/import.mdx | 3 +++ website/docs/plugin/framework/migrating/resources/index.mdx | 3 +++ .../plugin/framework/migrating/resources/plan-modification.mdx | 3 +++ .../plugin/framework/migrating/resources/state-upgrade.mdx | 3 +++ website/docs/plugin/framework/migrating/resources/timeouts.mdx | 3 +++ website/docs/plugin/framework/migrating/schema/index.mdx | 3 +++ website/docs/plugin/framework/migrating/testing.mdx | 3 +++ website/docs/plugin/framework/provider-servers.mdx | 3 +++ website/docs/plugin/framework/providers/index.mdx | 3 +++ .../docs/plugin/framework/providers/validate-configuration.mdx | 3 +++ website/docs/plugin/framework/resources/configure.mdx | 3 +++ website/docs/plugin/framework/resources/create.mdx | 3 +++ website/docs/plugin/framework/resources/default.mdx | 3 +++ website/docs/plugin/framework/resources/delete.mdx | 3 +++ website/docs/plugin/framework/resources/import.mdx | 3 +++ website/docs/plugin/framework/resources/index.mdx | 3 +++ website/docs/plugin/framework/resources/plan-modification.mdx | 3 +++ website/docs/plugin/framework/resources/private-state.mdx | 3 +++ website/docs/plugin/framework/resources/read.mdx | 3 +++ website/docs/plugin/framework/resources/state-move.mdx | 3 +++ website/docs/plugin/framework/resources/state-upgrade.mdx | 3 +++ website/docs/plugin/framework/resources/timeouts.mdx | 3 +++ website/docs/plugin/framework/resources/update.mdx | 3 +++ .../docs/plugin/framework/resources/validate-configuration.mdx | 3 +++ .../docs/plugin/framework/resources/write-only-arguments.mdx | 3 +++ website/docs/plugin/framework/validation.mdx | 3 +++ 136 files changed, 408 insertions(+) diff --git a/website/README.md b/website/README.md index e987f6636..785ebd354 100644 --- a/website/README.md +++ b/website/README.md @@ -1,5 +1,8 @@ # Terraform Documentation +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the `hashicorp/web-unified-docs` repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + This directory contains the portions of [the Terraform website][terraform.io] that pertain to the Terraform Plugin Framework. The files in this directory are intended to be used in conjunction with diff --git a/website/docs/plugin/framework/acctests.mdx b/website/docs/plugin/framework/acctests.mdx index e23cd6fbb..bb825d24f 100644 --- a/website/docs/plugin/framework/acctests.mdx +++ b/website/docs/plugin/framework/acctests.mdx @@ -6,6 +6,9 @@ description: >- Terraform operations. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Acceptance tests Implement provider resource and data source acceptance tests with the [terraform-plugin-testing module](/terraform/plugin/testing). These tests are designed to execute Terraform commands against real Terraform configurations, simulating practitioner experiences with creating, refreshing, updating, and deleting infrastructure. diff --git a/website/docs/plugin/framework/data-sources/configure.mdx b/website/docs/plugin/framework/data-sources/configure.mdx index 11dda382b..c751abac7 100644 --- a/website/docs/plugin/framework/data-sources/configure.mdx +++ b/website/docs/plugin/framework/data-sources/configure.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the `hashicorp/web-unified-docs` repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Configure data sources [Data sources](/terraform/plugin/framework/data-sources) may require provider-level data or remote system clients to operate correctly. The framework supports the ability to configure this data and/or clients once within the provider, then pass that information to data sources by adding the `Configure` method. diff --git a/website/docs/plugin/framework/data-sources/index.mdx b/website/docs/plugin/framework/data-sources/index.mdx index 64dfb2ecc..28b00b2c4 100644 --- a/website/docs/plugin/framework/data-sources/index.mdx +++ b/website/docs/plugin/framework/data-sources/index.mdx @@ -5,6 +5,9 @@ description: >- framework can help you implement data sources. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Data sources [Data sources](/terraform/language/data-sources) are an abstraction that allow Terraform to reference external data. Unlike [managed resources](/terraform/language/resources), Terraform does not manage the lifecycle of the resource or data. Data sources are intended to have no side-effects. diff --git a/website/docs/plugin/framework/data-sources/timeouts.mdx b/website/docs/plugin/framework/data-sources/timeouts.mdx index 7868dd402..5b4128d77 100644 --- a/website/docs/plugin/framework/data-sources/timeouts.mdx +++ b/website/docs/plugin/framework/data-sources/timeouts.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement timeouts with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Timeouts The reality of cloud infrastructure is that it typically takes time to perform operations such as booting operating systems, discovering services, and replicating state across network edges. As the provider developer you should take known delays in data source APIs into account in the `Read` function of the data source. Terraform supports configurable timeouts to assist in these situations. diff --git a/website/docs/plugin/framework/data-sources/validate-configuration.mdx b/website/docs/plugin/framework/data-sources/validate-configuration.mdx index 0a0a55dc6..df57f6d4b 100644 --- a/website/docs/plugin/framework/data-sources/validate-configuration.mdx +++ b/website/docs/plugin/framework/data-sources/validate-configuration.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Validate data source configurations [Data sources](/terraform/plugin/framework/data-sources) support validating an entire practitioner configuration in either declarative or imperative logic. Feedback, such as required syntax or acceptable combinations of values, is returned via [diagnostics](/terraform/plugin/framework/diagnostics). diff --git a/website/docs/plugin/framework/debugging.mdx b/website/docs/plugin/framework/debugging.mdx index ddae1484b..2efeec9f6 100644 --- a/website/docs/plugin/framework/debugging.mdx +++ b/website/docs/plugin/framework/debugging.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement debugger support in framework Terraform providers. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Debugging framework Providers This page contains implementation details for inspecting runtime information of a Terraform provider developed with Framework via a debugger tool by adjusting the [provider server](/terraform/plugin/framework/provider-servers) implementation. Review the top level [Debugging](/terraform/plugin/debugging) page for information pertaining to the overall Terraform provider debugging process and other inspection options, such as log-based debugging. diff --git a/website/docs/plugin/framework/deprecations.mdx b/website/docs/plugin/framework/deprecations.mdx index c86ce0ae0..c05eb08ca 100644 --- a/website/docs/plugin/framework/deprecations.mdx +++ b/website/docs/plugin/framework/deprecations.mdx @@ -5,6 +5,9 @@ description: renames in framework providers. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Deprecations, removals, and renames Terraform is trusted for managing many facets of infrastructure across many organizations. Part of that trust is due to consistent versioning guidelines and setting expectations for various levels of upgrades. Ensuring backwards compatibility for all patch and minor releases, potentially in concert with any upcoming major changes, is recommended and supported by the Terraform development framework. This allows operators to iteratively update their Terraform configurations rather than require massive refactoring. diff --git a/website/docs/plugin/framework/diagnostics.mdx b/website/docs/plugin/framework/diagnostics.mdx index 7208d4845..6a4959c6c 100644 --- a/website/docs/plugin/framework/diagnostics.mdx +++ b/website/docs/plugin/framework/diagnostics.mdx @@ -4,6 +4,9 @@ description: >- Learn how to return errors and warnings from the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Returning errors and warnings Providers use `Diagnostics` to surface errors and warnings to practitioners, diff --git a/website/docs/plugin/framework/ephemeral-resources/close.mdx b/website/docs/plugin/framework/ephemeral-resources/close.mdx index dade8f2bd..ee22fe573 100644 --- a/website/docs/plugin/framework/ephemeral-resources/close.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/close.mdx @@ -4,6 +4,9 @@ description: >- Learn how to close ephemeral resource in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Closing Ephemeral Resources Close is an optional part of the Terraform lifecycle for an ephemeral resource, which is different from the [managed resource lifecycle](https://github.com/hashicorp/terraform/blob/main/docs/resource-instance-change-lifecycle.md). During any Terraform operation (like [`terraform plan`](/terraform/cli/commands/plan) or [`terraform apply`](/terraform/cli/commands/apply)), when an ephemeral resource's data is needed, Terraform initially retrieves that data with the [`Open`](/terraform/plugin/framework/ephemeral-resources/open) lifecycle handler. Once the ephemeral resource data is no longer needed, Terraform calls the provider `CloseEphemeralResource` RPC, in which the framework calls the [`ephemeral.EphemeralResourceWithClose` interface `Close` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/ephemeral#EphemeralResourceWithClose). The request contains any `Private` data set in the latest `Open` or `Renew` call. diff --git a/website/docs/plugin/framework/ephemeral-resources/configure.mdx b/website/docs/plugin/framework/ephemeral-resources/configure.mdx index fef8e4a12..6da5694d0 100644 --- a/website/docs/plugin/framework/ephemeral-resources/configure.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/configure.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Configuring ephemeral resources [Ephemeral Resources](/terraform/plugin/framework/ephemeral-resources) may require provider-level data or remote system clients to operate correctly. The framework supports the ability to configure this data and/or clients once within the provider, then pass that information to ephemeral resources by adding the `Configure` method. diff --git a/website/docs/plugin/framework/ephemeral-resources/index.mdx b/website/docs/plugin/framework/ephemeral-resources/index.mdx index 194d65d5f..39db7f771 100644 --- a/website/docs/plugin/framework/ephemeral-resources/index.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/index.mdx @@ -6,6 +6,9 @@ description: >- to implement ephemeral resources in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Ephemeral resources diff --git a/website/docs/plugin/framework/ephemeral-resources/open.mdx b/website/docs/plugin/framework/ephemeral-resources/open.mdx index 3f7ff646a..27e2b61c8 100644 --- a/website/docs/plugin/framework/ephemeral-resources/open.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/open.mdx @@ -4,6 +4,9 @@ description: >- Learn how to open ephemeral resource in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Opening ephemeral resources Open is part of the Terraform lifecycle for an ephemeral resource, which is different from the [managed resource lifecycle](https://github.com/hashicorp/terraform/blob/main/docs/resource-instance-change-lifecycle.md). During any Terraform operation (like [`terraform plan`](/terraform/cli/commands/plan) or [`terraform apply`](/terraform/cli/commands/apply)), when an ephemeral resource's data is needed, Terraform calls the provider `OpenEphemeralResource` RPC, in which the framework calls the [`ephemeral.EphemeralResource` interface `Open` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/ephemeral#EphemeralResource.Open). The request contains the configuration supplied to Terraform for the ephemeral resource. The response contains the ephemeral result data. The data is defined by the [schema](/terraform/plugin/framework/handling-data/schemas) of the ephemeral resource. diff --git a/website/docs/plugin/framework/ephemeral-resources/renew.mdx b/website/docs/plugin/framework/ephemeral-resources/renew.mdx index dadd729f7..19582ec3d 100644 --- a/website/docs/plugin/framework/ephemeral-resources/renew.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/renew.mdx @@ -4,6 +4,9 @@ description: >- Learn how to renew ephemeral resource in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Renewing ephemeral resources Renew is an optional part of the Terraform lifecycle for an ephemeral resource, which is different from the [managed resource lifecycle](https://github.com/hashicorp/terraform/blob/main/docs/resource-instance-change-lifecycle.md). During any Terraform operation (like [`terraform plan`](/terraform/cli/commands/plan) or [`terraform apply`](/terraform/cli/commands/apply)), when an ephemeral resource's data is needed, Terraform initially retrieves that data with the [`Open`](/terraform/plugin/framework/ephemeral-resources/open) lifecycle handler. During `Open`, ephemeral resources can opt to include a timestamp in the `RenewAt` response field to indicate to Terraform when a provider must renew an ephemeral resource. If an ephemeral resource's data is still in-use and the `RenewAt` timestamp has passed, Terraform calls the provider `RenewEphemeralResource` RPC, in which the framework calls the [`ephemeral.EphemeralResourceWithRenew` interface `Renew` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/ephemeral#EphemeralResourceWithRenew). The request contains any `Private` data set in the latest `Open` or `Renew` call. The response contains `Private` data and an optional `RenewAt` field for further renew executions. diff --git a/website/docs/plugin/framework/ephemeral-resources/validate-configuration.mdx b/website/docs/plugin/framework/ephemeral-resources/validate-configuration.mdx index 242dd4cfc..fd418a9d0 100644 --- a/website/docs/plugin/framework/ephemeral-resources/validate-configuration.mdx +++ b/website/docs/plugin/framework/ephemeral-resources/validate-configuration.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Validate ephemeral resource configurations [Ephemeral resources](/terraform/plugin/framework/ephemeral-resources) support validating an entire practitioner configuration in either declarative or imperative logic. Feedback, such as required syntax or acceptable combinations of values, is returned via [diagnostics](/terraform/plugin/framework/diagnostics). diff --git a/website/docs/plugin/framework/functions/concepts.mdx b/website/docs/plugin/framework/functions/concepts.mdx index 23835d778..ab0fe40ec 100644 --- a/website/docs/plugin/framework/functions/concepts.mdx +++ b/website/docs/plugin/framework/functions/concepts.mdx @@ -5,6 +5,9 @@ description: >- functions for practitions to use in their Terraform configurations. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Provider-defined functions This page describes Terraform concepts relating to provider-defined functions within framework-based provider code. Provider-defined functions are supported in Terraform 1.8 and later. The [What is Terraform](/terraform/intro), [Terraform language](/terraform/language), and [Plugin Development](/terraform/plugin) documentation covers more general concepts behind Terraform's workflow, its configuration, and how it interacts with providers. diff --git a/website/docs/plugin/framework/functions/documentation.mdx b/website/docs/plugin/framework/functions/documentation.mdx index fd731ae0f..07825ebd4 100644 --- a/website/docs/plugin/framework/functions/documentation.mdx +++ b/website/docs/plugin/framework/functions/documentation.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Documenting functions When a function is [implemented](/terraform/plugin/framework/functions/implementation), ensure the function is discoverable by practitioners with usage information. diff --git a/website/docs/plugin/framework/functions/errors.mdx b/website/docs/plugin/framework/functions/errors.mdx index 64dc74931..0266e4ee9 100644 --- a/website/docs/plugin/framework/functions/errors.mdx +++ b/website/docs/plugin/framework/functions/errors.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Returning errors from function Providers use [`FuncError`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/function#FuncError) to diff --git a/website/docs/plugin/framework/functions/implementation.mdx b/website/docs/plugin/framework/functions/implementation.mdx index c811870ee..116a408af 100644 --- a/website/docs/plugin/framework/functions/implementation.mdx +++ b/website/docs/plugin/framework/functions/implementation.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Implement provider-defined functions The framework supports implementing functions based on Terraform's [concepts for provider-defined functions](/terraform/plugin/framework/functions/concepts). It is recommended to understand those concepts before implementing a function since the terminology is used throughout this page and there are details that simplify function handling as compared to other provider concepts. Provider-defined functions are supported in Terraform 1.8 and later. diff --git a/website/docs/plugin/framework/functions/index.mdx b/website/docs/plugin/framework/functions/index.mdx index 193c4ea1f..25339bea7 100644 --- a/website/docs/plugin/framework/functions/index.mdx +++ b/website/docs/plugin/framework/functions/index.mdx @@ -6,6 +6,9 @@ description: >- plugin framework can help you implement provider-defined functions. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Provider-defined functions diff --git a/website/docs/plugin/framework/functions/parameters/bool.mdx b/website/docs/plugin/framework/functions/parameters/bool.mdx index 3bf687562..af15774e0 100644 --- a/website/docs/plugin/framework/functions/parameters/bool.mdx +++ b/website/docs/plugin/framework/functions/parameters/bool.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Boolean function parameters Bool function parameters expect a boolean true or false value from a practitioner configuration. Values are accessible in function logic by the Go built-in `bool` type, Go built-in `*bool` type, or the [framework bool type](/terraform/plugin/framework/handling-data/types/bool). diff --git a/website/docs/plugin/framework/functions/parameters/dynamic.mdx b/website/docs/plugin/framework/functions/parameters/dynamic.mdx index e088e5c55..6e66cdeba 100644 --- a/website/docs/plugin/framework/functions/parameters/dynamic.mdx +++ b/website/docs/plugin/framework/functions/parameters/dynamic.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Dynamic function parameters diff --git a/website/docs/plugin/framework/functions/parameters/float32.mdx b/website/docs/plugin/framework/functions/parameters/float32.mdx index 423df0cb2..0ffcc9bc9 100644 --- a/website/docs/plugin/framework/functions/parameters/float32.mdx +++ b/website/docs/plugin/framework/functions/parameters/float32.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float32 Function Parameter diff --git a/website/docs/plugin/framework/functions/parameters/float64.mdx b/website/docs/plugin/framework/functions/parameters/float64.mdx index 7dbe213ac..95b0c7812 100644 --- a/website/docs/plugin/framework/functions/parameters/float64.mdx +++ b/website/docs/plugin/framework/functions/parameters/float64.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float64 function parameters diff --git a/website/docs/plugin/framework/functions/parameters/index.mdx b/website/docs/plugin/framework/functions/parameters/index.mdx index 76b4e6ed2..32a50f55f 100644 --- a/website/docs/plugin/framework/functions/parameters/index.mdx +++ b/website/docs/plugin/framework/functions/parameters/index.mdx @@ -6,6 +6,9 @@ description: >- arguments in a function definition. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Function parameters Parameters in [function definitions](/terraform/plugin/framework/functions/implementation#definition-method) describes how data values are passed to the function logic. Every parameter type has an associated [value type](/terraform/plugin/framework/handling-data/types), although this data handling is simplified for function implementations over other provider concepts, such as resource implementations. diff --git a/website/docs/plugin/framework/functions/parameters/int32.mdx b/website/docs/plugin/framework/functions/parameters/int32.mdx index de87e82b1..b08c57fc1 100644 --- a/website/docs/plugin/framework/functions/parameters/int32.mdx +++ b/website/docs/plugin/framework/functions/parameters/int32.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int32 function parameters diff --git a/website/docs/plugin/framework/functions/parameters/int64.mdx b/website/docs/plugin/framework/functions/parameters/int64.mdx index 5e8411516..23e0e5d6a 100644 --- a/website/docs/plugin/framework/functions/parameters/int64.mdx +++ b/website/docs/plugin/framework/functions/parameters/int64.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int64 function parameters diff --git a/website/docs/plugin/framework/functions/parameters/list.mdx b/website/docs/plugin/framework/functions/parameters/list.mdx index edbca773a..18f075209 100644 --- a/website/docs/plugin/framework/functions/parameters/list.mdx +++ b/website/docs/plugin/framework/functions/parameters/list.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List function parameters List function parameters expect an ordered collection of single element type value from a practitioner configuration. Values are accessible in function logic by a Go slice of an appropriate pointer type to match the element type `[]*T` or the [framework list type](/terraform/plugin/framework/handling-data/types/list). diff --git a/website/docs/plugin/framework/functions/parameters/map.mdx b/website/docs/plugin/framework/functions/parameters/map.mdx index 49d35d893..38430df0b 100644 --- a/website/docs/plugin/framework/functions/parameters/map.mdx +++ b/website/docs/plugin/framework/functions/parameters/map.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List function parameters Map function parameters expect a mapping of arbitrary string keys to values of single element type from a practitioner configuration. Values are accessible in function logic by a Go map of string keys to values of an appropriate pointer type to match the element type `map[string]*T` or the [framework map type](/terraform/plugin/framework/handling-data/types/map). diff --git a/website/docs/plugin/framework/functions/parameters/number.mdx b/website/docs/plugin/framework/functions/parameters/number.mdx index 256d521ef..0e385a564 100644 --- a/website/docs/plugin/framework/functions/parameters/number.mdx +++ b/website/docs/plugin/framework/functions/parameters/number.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Number function parameters diff --git a/website/docs/plugin/framework/functions/parameters/object.mdx b/website/docs/plugin/framework/functions/parameters/object.mdx index 41bdcbae4..2d687b6a6 100644 --- a/website/docs/plugin/framework/functions/parameters/object.mdx +++ b/website/docs/plugin/framework/functions/parameters/object.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Object function parameters Object function parameters expect a single structure mapping explicit attribute names to type definitions from a practitioner configuration. Values are accessible in function logic by a Go structure type annotated with `tfsdk` field tags or the [framework object type](/terraform/plugin/framework/handling-data/types/object). diff --git a/website/docs/plugin/framework/functions/parameters/set.mdx b/website/docs/plugin/framework/functions/parameters/set.mdx index dc53f9952..33f98133d 100644 --- a/website/docs/plugin/framework/functions/parameters/set.mdx +++ b/website/docs/plugin/framework/functions/parameters/set.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set function parameters Set function parameters expect an unordered, unique collection of single element type value from a practitioner configuration. Values are accessible in function logic by a Go slice of an appropriate pointer type to match the element type `[]*T` or the [framework set type](/terraform/plugin/framework/handling-data/types/set). diff --git a/website/docs/plugin/framework/functions/parameters/string.mdx b/website/docs/plugin/framework/functions/parameters/string.mdx index bb449009c..43cc7f9d0 100644 --- a/website/docs/plugin/framework/functions/parameters/string.mdx +++ b/website/docs/plugin/framework/functions/parameters/string.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # String function parameters String function parameters expect a collection of UTF-8 encoded bytes from a practitioner configuration. Values are accessible in function logic by the Go built-in `string` type, Go built-in `*string` type, or the [framework string type](/terraform/plugin/framework/handling-data/types/string). diff --git a/website/docs/plugin/framework/functions/returns/bool.mdx b/website/docs/plugin/framework/functions/returns/bool.mdx index 0da2af0d0..e3514a164 100644 --- a/website/docs/plugin/framework/functions/returns/bool.mdx +++ b/website/docs/plugin/framework/functions/returns/bool.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Boolean return values Bool function return values expect a boolean true or false value from function logic. Set values in function logic with the Go built-in `bool` type, Go built-in `*bool` type, or the [framework bool type](/terraform/plugin/framework/handling-data/types/bool). diff --git a/website/docs/plugin/framework/functions/returns/dynamic.mdx b/website/docs/plugin/framework/functions/returns/dynamic.mdx index 844d5926a..56ebd61cf 100644 --- a/website/docs/plugin/framework/functions/returns/dynamic.mdx +++ b/website/docs/plugin/framework/functions/returns/dynamic.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Dynamic function return values diff --git a/website/docs/plugin/framework/functions/returns/float32.mdx b/website/docs/plugin/framework/functions/returns/float32.mdx index fbd0f48e5..7cbf562e3 100644 --- a/website/docs/plugin/framework/functions/returns/float32.mdx +++ b/website/docs/plugin/framework/functions/returns/float32.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float32 return values diff --git a/website/docs/plugin/framework/functions/returns/float64.mdx b/website/docs/plugin/framework/functions/returns/float64.mdx index 2769c1d98..2dc241b3e 100644 --- a/website/docs/plugin/framework/functions/returns/float64.mdx +++ b/website/docs/plugin/framework/functions/returns/float64.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float64 return values diff --git a/website/docs/plugin/framework/functions/returns/index.mdx b/website/docs/plugin/framework/functions/returns/index.mdx index a81c5bc83..46c6f1b12 100644 --- a/website/docs/plugin/framework/functions/returns/index.mdx +++ b/website/docs/plugin/framework/functions/returns/index.mdx @@ -6,6 +6,9 @@ description: >- data in a function definition. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Return values A return in a [function definition](/terraform/plugin/framework/functions/implementation#definition-method) describes the result data value from function logic. Every return type has an associated [value type](/terraform/plugin/framework/handling-data/types), although this data handling is simplified for function implementations over other provider concepts, such as resource implementations. diff --git a/website/docs/plugin/framework/functions/returns/int32.mdx b/website/docs/plugin/framework/functions/returns/int32.mdx index b0c4b0c54..4c016a18d 100644 --- a/website/docs/plugin/framework/functions/returns/int32.mdx +++ b/website/docs/plugin/framework/functions/returns/int32.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int32 return values diff --git a/website/docs/plugin/framework/functions/returns/int64.mdx b/website/docs/plugin/framework/functions/returns/int64.mdx index a134eda66..b9dfa0a15 100644 --- a/website/docs/plugin/framework/functions/returns/int64.mdx +++ b/website/docs/plugin/framework/functions/returns/int64.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int64 return values diff --git a/website/docs/plugin/framework/functions/returns/list.mdx b/website/docs/plugin/framework/functions/returns/list.mdx index 39cc953c7..f2f5a3a6e 100644 --- a/website/docs/plugin/framework/functions/returns/list.mdx +++ b/website/docs/plugin/framework/functions/returns/list.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List return values List function return expects an ordered collection of single element type value from function logic. Set values in function logic with a Go slice of an appropriate type to match the element type `[]T` or the [framework list type](/terraform/plugin/framework/handling-data/types/list). diff --git a/website/docs/plugin/framework/functions/returns/map.mdx b/website/docs/plugin/framework/functions/returns/map.mdx index 3ae20085a..ce9d27208 100644 --- a/website/docs/plugin/framework/functions/returns/map.mdx +++ b/website/docs/plugin/framework/functions/returns/map.mdx @@ -5,6 +5,9 @@ description: >- Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Map return values Map function return expects a mapping of arbitrary string keys to values of single element type from function logic. Set values in function logic with a Go map of string keys to values of an appropriate type to match the element type `map[string]T` or the [framework map type](/terraform/plugin/framework/handling-data/types/map). diff --git a/website/docs/plugin/framework/functions/returns/number.mdx b/website/docs/plugin/framework/functions/returns/number.mdx index 74e6a2c01..21fb1cb60 100644 --- a/website/docs/plugin/framework/functions/returns/number.mdx +++ b/website/docs/plugin/framework/functions/returns/number.mdx @@ -5,6 +5,9 @@ description: >- with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Number return values diff --git a/website/docs/plugin/framework/functions/returns/object.mdx b/website/docs/plugin/framework/functions/returns/object.mdx index 7f4a354a3..383ae33bc 100644 --- a/website/docs/plugin/framework/functions/returns/object.mdx +++ b/website/docs/plugin/framework/functions/returns/object.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Object return values Object function return expects a single structure mapping explicit attribute names to type definitions from function logic. Set values in function logic with a Go structure type annotated with `tfsdk` field tags or the [framework map type](/terraform/plugin/framework/handling-data/types/map). diff --git a/website/docs/plugin/framework/functions/returns/set.mdx b/website/docs/plugin/framework/functions/returns/set.mdx index 1c32dc777..cda4deb41 100644 --- a/website/docs/plugin/framework/functions/returns/set.mdx +++ b/website/docs/plugin/framework/functions/returns/set.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set return values Set function return expects an unordered, unique collection of single element type value from function logic. Set values in function logic with a Go slice of an appropriate type to match the element type `[]T` or the [framework set type](/terraform/plugin/framework/handling-data/types/set). diff --git a/website/docs/plugin/framework/functions/returns/string.mdx b/website/docs/plugin/framework/functions/returns/string.mdx index d80f9fa5b..7d4cd6e61 100644 --- a/website/docs/plugin/framework/functions/returns/string.mdx +++ b/website/docs/plugin/framework/functions/returns/string.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # String return values String function return expects a collection of UTF-8 encoded bytes from function logic. Set values in function logic with the Go built-in `string` type, Go built-in `*string` type, or the [framework string type](/terraform/plugin/framework/handling-data/types/string). diff --git a/website/docs/plugin/framework/functions/testing.mdx b/website/docs/plugin/framework/functions/testing.mdx index dad993547..286131014 100644 --- a/website/docs/plugin/framework/functions/testing.mdx +++ b/website/docs/plugin/framework/functions/testing.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Testing functions When a function is [implemented](/terraform/plugin/framework/functions/implementation), ensure the function behaves as expected. Follow [recommendations](#recommendations) to cover how practitioner configurations may call the function. diff --git a/website/docs/plugin/framework/getting-started/code-walkthrough.mdx b/website/docs/plugin/framework/getting-started/code-walkthrough.mdx index 8aecf5506..9dceee50f 100644 --- a/website/docs/plugin/framework/getting-started/code-walkthrough.mdx +++ b/website/docs/plugin/framework/getting-started/code-walkthrough.mdx @@ -6,6 +6,9 @@ description: >- by exploring its main components and libraries. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Provider code walkthrough [Terraform providers](/terraform/language/providers) let Terraform communicate with third parties, such as cloud providers, SaaS providers, and other APIs. Terraform and Terraform providers use gRPC to communicate. Terraform operates as a gRPC client and providers operate as gRPC servers. diff --git a/website/docs/plugin/framework/handling-data/accessing-values.mdx b/website/docs/plugin/framework/handling-data/accessing-values.mdx index a3d1777f4..728d68457 100644 --- a/website/docs/plugin/framework/handling-data/accessing-values.mdx +++ b/website/docs/plugin/framework/handling-data/accessing-values.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Access state, configuration, and plan data There are various points at which the provider needs access to the data from diff --git a/website/docs/plugin/framework/handling-data/attributes/bool.mdx b/website/docs/plugin/framework/handling-data/attributes/bool.mdx index b1b7eb2e1..ee8f93c39 100644 --- a/website/docs/plugin/framework/handling-data/attributes/bool.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/bool.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use boolean attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Boolean attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx b/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx index 66da060bf..0c13c3eb2 100644 --- a/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/dynamic.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use dynamic attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Dynamic attribute diff --git a/website/docs/plugin/framework/handling-data/attributes/float32.mdx b/website/docs/plugin/framework/handling-data/attributes/float32.mdx index f0a654718..2dd15ddc4 100644 --- a/website/docs/plugin/framework/handling-data/attributes/float32.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/float32.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float32 attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/float64.mdx b/website/docs/plugin/framework/handling-data/attributes/float64.mdx index a43e8b7ab..d3838a33c 100644 --- a/website/docs/plugin/framework/handling-data/attributes/float64.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/float64.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float64 attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/index.mdx b/website/docs/plugin/framework/handling-data/attributes/index.mdx index 58397eacb..16fad163a 100644 --- a/website/docs/plugin/framework/handling-data/attributes/index.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/index.mdx @@ -7,6 +7,9 @@ description: >- custom type. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Attributes Attributes are value storing fields in resource, data source, or provider [schemas](/terraform/plugin/framework/handling-data/schemas). Every attribute has an associated [value type](/terraform/plugin/framework/handling-data/types), which describes the kind of data the attribute can hold. Attributes also can describe value plan modifiers (resources only) and value validators in addition to those defined by the value type. diff --git a/website/docs/plugin/framework/handling-data/attributes/int32.mdx b/website/docs/plugin/framework/handling-data/attributes/int32.mdx index cd9c443e9..7a2c9268d 100644 --- a/website/docs/plugin/framework/handling-data/attributes/int32.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/int32.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int32 attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/int64.mdx b/website/docs/plugin/framework/handling-data/attributes/int64.mdx index 6bba8efe4..5eb9970a0 100644 --- a/website/docs/plugin/framework/handling-data/attributes/int64.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/int64.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int64 attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx index 1a3bd86e6..ed328e6fc 100644 --- a/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/list-nested.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use list nested attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List nested attributes List nested attributes store an ordered collection of nested objects. Values are represented by a [list type](/terraform/plugin/framework/handling-data/types/list) in the framework, containing elements of [object type](/terraform/plugin/framework/handling-data/types/object). diff --git a/website/docs/plugin/framework/handling-data/attributes/list.mdx b/website/docs/plugin/framework/handling-data/attributes/list.mdx index 50ffcf6b5..d05788d9a 100644 --- a/website/docs/plugin/framework/handling-data/attributes/list.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/list.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use list attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List attributes List attributes store an ordered collection of single element type. Values are represented by a [list type](/terraform/plugin/framework/handling-data/types/list) in the framework, containing elements of the element type. diff --git a/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx index 53424755a..3a20d2b54 100644 --- a/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/map-nested.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use map nested attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Map Nested Attribute Map nested attributes store mapping of arbitrary string keys to nested objects. Values are represented by a [map type](/terraform/plugin/framework/handling-data/types/map) in the framework, containing elements of [object type](/terraform/plugin/framework/handling-data/types/object). diff --git a/website/docs/plugin/framework/handling-data/attributes/map.mdx b/website/docs/plugin/framework/handling-data/attributes/map.mdx index a5b3faea9..50c1bf468 100644 --- a/website/docs/plugin/framework/handling-data/attributes/map.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/map.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use map attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Map attributes Map attributes store a mapping of arbitrary string keys to values of single element type. Values are represented by a [map type](/terraform/plugin/framework/handling-data/types/map) in the framework, containing elements of the element type. diff --git a/website/docs/plugin/framework/handling-data/attributes/number.mdx b/website/docs/plugin/framework/handling-data/attributes/number.mdx index 270a35070..a5c81e44c 100644 --- a/website/docs/plugin/framework/handling-data/attributes/number.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/number.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Number attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/object.mdx b/website/docs/plugin/framework/handling-data/attributes/object.mdx index de7b56083..2e1b5e5a5 100644 --- a/website/docs/plugin/framework/handling-data/attributes/object.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/object.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use object attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Object attributes diff --git a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx index 01ce7e51f..ef5a577ed 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set-nested.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use set nested attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set nested attributes Set nested attributes store a unique, unordered collection of nested objects. Values are represented by a [set type](/terraform/plugin/framework/handling-data/types/set) in the framework, containing elements of [object type](/terraform/plugin/framework/handling-data/types/object). diff --git a/website/docs/plugin/framework/handling-data/attributes/set.mdx b/website/docs/plugin/framework/handling-data/attributes/set.mdx index 512489aad..17caaf849 100644 --- a/website/docs/plugin/framework/handling-data/attributes/set.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/set.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use set attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set attributes Set attributes store an unique, unordered collection of single element type. Values are represented by a [set type](/terraform/plugin/framework/handling-data/types/set) in the framework, containing elements of the element type. diff --git a/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx b/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx index 24e52a883..a628ea4b6 100644 --- a/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/single-nested.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use single nested attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Single nested attributes Single nested attributes are a single structure mapping explicit attribute names to nested attribute definitions. Values are represented by a [object type](/terraform/plugin/framework/handling-data/types/object) in the framework, containing nested attribute values of the mapped attributes. diff --git a/website/docs/plugin/framework/handling-data/attributes/string.mdx b/website/docs/plugin/framework/handling-data/attributes/string.mdx index 6d414dbff..fec7a654b 100644 --- a/website/docs/plugin/framework/handling-data/attributes/string.mdx +++ b/website/docs/plugin/framework/handling-data/attributes/string.mdx @@ -4,6 +4,9 @@ description: >- Learn how to use string attributes with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # String attributes String attributes store a collection of UTF-8 encoded bytes. Values are represented by a [string type](/terraform/plugin/framework/handling-data/types/string) in the framework. diff --git a/website/docs/plugin/framework/handling-data/blocks/index.mdx b/website/docs/plugin/framework/handling-data/blocks/index.mdx index af96c5021..20b9a129c 100644 --- a/website/docs/plugin/framework/handling-data/blocks/index.mdx +++ b/website/docs/plugin/framework/handling-data/blocks/index.mdx @@ -6,6 +6,9 @@ description: >- provider schema. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Blocks diff --git a/website/docs/plugin/framework/handling-data/blocks/list-nested.mdx b/website/docs/plugin/framework/handling-data/blocks/list-nested.mdx index e17c362f4..04b9d7aff 100644 --- a/website/docs/plugin/framework/handling-data/blocks/list-nested.mdx +++ b/website/docs/plugin/framework/handling-data/blocks/list-nested.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List nested blocks diff --git a/website/docs/plugin/framework/handling-data/blocks/set-nested.mdx b/website/docs/plugin/framework/handling-data/blocks/set-nested.mdx index e780d5b5c..bea59187c 100644 --- a/website/docs/plugin/framework/handling-data/blocks/set-nested.mdx +++ b/website/docs/plugin/framework/handling-data/blocks/set-nested.mdx @@ -4,6 +4,9 @@ description: >- Learn to implement the set nested block type with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set nested blocks diff --git a/website/docs/plugin/framework/handling-data/blocks/single-nested.mdx b/website/docs/plugin/framework/handling-data/blocks/single-nested.mdx index e078b1103..3911a0f7d 100644 --- a/website/docs/plugin/framework/handling-data/blocks/single-nested.mdx +++ b/website/docs/plugin/framework/handling-data/blocks/single-nested.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Single nested blocks diff --git a/website/docs/plugin/framework/handling-data/dynamic-data.mdx b/website/docs/plugin/framework/handling-data/dynamic-data.mdx index 07f845f41..921a6f6da 100644 --- a/website/docs/plugin/framework/handling-data/dynamic-data.mdx +++ b/website/docs/plugin/framework/handling-data/dynamic-data.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Handling dynamic data diff --git a/website/docs/plugin/framework/handling-data/path-expressions.mdx b/website/docs/plugin/framework/handling-data/path-expressions.mdx index 80cca2a1d..dc34c28a9 100644 --- a/website/docs/plugin/framework/handling-data/path-expressions.mdx +++ b/website/docs/plugin/framework/handling-data/path-expressions.mdx @@ -6,6 +6,9 @@ description: >- more actual paths within schema data. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Path expressions diff --git a/website/docs/plugin/framework/handling-data/paths.mdx b/website/docs/plugin/framework/handling-data/paths.mdx index 1df50cf70..b887da83a 100644 --- a/website/docs/plugin/framework/handling-data/paths.mdx +++ b/website/docs/plugin/framework/handling-data/paths.mdx @@ -5,6 +5,9 @@ description: >- represent a location within a schema or schema-based data. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Paths An exact location within a [schema](/terraform/plugin/framework/handling-data/schemas) or schema-based data such as [`tfsdk.Config`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Config), [`tfsdk.Plan`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#Plan), or [`tfsdk.State`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/tfsdk#State), is referred to as a path. diff --git a/website/docs/plugin/framework/handling-data/schemas.mdx b/website/docs/plugin/framework/handling-data/schemas.mdx index e406565e5..d69c26c50 100644 --- a/website/docs/plugin/framework/handling-data/schemas.mdx +++ b/website/docs/plugin/framework/handling-data/schemas.mdx @@ -5,6 +5,9 @@ description: >- specify the constraints of Terraform configuration blocks. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Schemas Schemas specify the constraints of Terraform configuration blocks. They define what fields a provider, diff --git a/website/docs/plugin/framework/handling-data/terraform-concepts.mdx b/website/docs/plugin/framework/handling-data/terraform-concepts.mdx index d8602e8a3..2d3188289 100644 --- a/website/docs/plugin/framework/handling-data/terraform-concepts.mdx +++ b/website/docs/plugin/framework/handling-data/terraform-concepts.mdx @@ -5,6 +5,9 @@ description: >- configuration to schemas, attributes, and blocks. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Terraform data concepts This page describes Terraform concepts as they relate to handling data within framework-based provider code. The [What is Terraform](/terraform/intro), [Terraform language](/terraform/language), and [Plugin Development](/terraform/plugin) documentation covers more general concepts behind Terraform's workflow, its configuration, and how it interacts with providers. diff --git a/website/docs/plugin/framework/handling-data/types/bool.mdx b/website/docs/plugin/framework/handling-data/types/bool.mdx index d771afd10..981ebe449 100644 --- a/website/docs/plugin/framework/handling-data/types/bool.mdx +++ b/website/docs/plugin/framework/handling-data/types/bool.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement boolean value types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Bool types Bool types store a boolean true or false value. diff --git a/website/docs/plugin/framework/handling-data/types/custom.mdx b/website/docs/plugin/framework/handling-data/types/custom.mdx index ad47f3372..188bab2f5 100644 --- a/website/docs/plugin/framework/handling-data/types/custom.mdx +++ b/website/docs/plugin/framework/handling-data/types/custom.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement custom types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Custom types Use existing custom types or develop custom types to consistently define behaviors for a kind of value across schemas. Custom types are supported on top of any framework-defined type. diff --git a/website/docs/plugin/framework/handling-data/types/dynamic.mdx b/website/docs/plugin/framework/handling-data/types/dynamic.mdx index cd0194eef..4cb5c1315 100644 --- a/website/docs/plugin/framework/handling-data/types/dynamic.mdx +++ b/website/docs/plugin/framework/handling-data/types/dynamic.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement dynamic types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Dynamic types diff --git a/website/docs/plugin/framework/handling-data/types/float32.mdx b/website/docs/plugin/framework/handling-data/types/float32.mdx index bc3bdc8e2..8f9ad0d6f 100644 --- a/website/docs/plugin/framework/handling-data/types/float32.mdx +++ b/website/docs/plugin/framework/handling-data/types/float32.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float32 types diff --git a/website/docs/plugin/framework/handling-data/types/float64.mdx b/website/docs/plugin/framework/handling-data/types/float64.mdx index 6fc389679..08289c73d 100644 --- a/website/docs/plugin/framework/handling-data/types/float64.mdx +++ b/website/docs/plugin/framework/handling-data/types/float64.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Float64 types diff --git a/website/docs/plugin/framework/handling-data/types/index.mdx b/website/docs/plugin/framework/handling-data/types/index.mdx index 4e5f9103a..40ad90abc 100644 --- a/website/docs/plugin/framework/handling-data/types/index.mdx +++ b/website/docs/plugin/framework/handling-data/types/index.mdx @@ -6,6 +6,9 @@ description: >- types based off of the built-in attribute types. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Data types Types are value storage and access mechanism for resource, data source, or provider [schema](/terraform/plugin/framework/handling-data/schemas) data. Every attribute and block has an associated type, which describes the kind of data. These types fully support Terraform's [type system concepts](/terraform/plugin/framework/handling-data/terraform-concepts) that cannot be represented in Go built-in types, such as `*string`. Framework types can be extended by implementing [custom types](/terraform/plugin/framework/handling-data/types/custom) in provider code or shared libraries to provide specific use case functionality. diff --git a/website/docs/plugin/framework/handling-data/types/int32.mdx b/website/docs/plugin/framework/handling-data/types/int32.mdx index 4cc69ca3f..3bed7f6d5 100644 --- a/website/docs/plugin/framework/handling-data/types/int32.mdx +++ b/website/docs/plugin/framework/handling-data/types/int32.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int32 types diff --git a/website/docs/plugin/framework/handling-data/types/int64.mdx b/website/docs/plugin/framework/handling-data/types/int64.mdx index 5d4f99da8..f9c35d2c4 100644 --- a/website/docs/plugin/framework/handling-data/types/int64.mdx +++ b/website/docs/plugin/framework/handling-data/types/int64.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Int64 types diff --git a/website/docs/plugin/framework/handling-data/types/list.mdx b/website/docs/plugin/framework/handling-data/types/list.mdx index 252f1df5e..ff9d2f402 100644 --- a/website/docs/plugin/framework/handling-data/types/list.mdx +++ b/website/docs/plugin/framework/handling-data/types/list.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # List types List types store an ordered collection of single element type. diff --git a/website/docs/plugin/framework/handling-data/types/map.mdx b/website/docs/plugin/framework/handling-data/types/map.mdx index b6be68b9d..32d7edef2 100644 --- a/website/docs/plugin/framework/handling-data/types/map.mdx +++ b/website/docs/plugin/framework/handling-data/types/map.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Map type Map types store an ordered collection of single element type. diff --git a/website/docs/plugin/framework/handling-data/types/number.mdx b/website/docs/plugin/framework/handling-data/types/number.mdx index 99c7315db..f8e3a1ff5 100644 --- a/website/docs/plugin/framework/handling-data/types/number.mdx +++ b/website/docs/plugin/framework/handling-data/types/number.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Number types diff --git a/website/docs/plugin/framework/handling-data/types/object.mdx b/website/docs/plugin/framework/handling-data/types/object.mdx index b6ae89545..7e0bc9037 100644 --- a/website/docs/plugin/framework/handling-data/types/object.mdx +++ b/website/docs/plugin/framework/handling-data/types/object.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement object value types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Object types Object types store a mapping of explicit attribute names to value types. Objects must declare all attribute values, even when null or unknown, unless the entire object is null or unknown. diff --git a/website/docs/plugin/framework/handling-data/types/set.mdx b/website/docs/plugin/framework/handling-data/types/set.mdx index 73e0576cb..fac4f4826 100644 --- a/website/docs/plugin/framework/handling-data/types/set.mdx +++ b/website/docs/plugin/framework/handling-data/types/set.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement set value types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Set types Set types store an ordered collection of single element type. diff --git a/website/docs/plugin/framework/handling-data/types/string.mdx b/website/docs/plugin/framework/handling-data/types/string.mdx index c4644153d..6a3ae5333 100644 --- a/website/docs/plugin/framework/handling-data/types/string.mdx +++ b/website/docs/plugin/framework/handling-data/types/string.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement string value types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # String types String types store a collection of UTF-8 encoded bytes. diff --git a/website/docs/plugin/framework/handling-data/types/tuple.mdx b/website/docs/plugin/framework/handling-data/types/tuple.mdx index 1276559a9..feb696693 100644 --- a/website/docs/plugin/framework/handling-data/types/tuple.mdx +++ b/website/docs/plugin/framework/handling-data/types/tuple.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement tuple value types with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Tuple types diff --git a/website/docs/plugin/framework/handling-data/writing-state.mdx b/website/docs/plugin/framework/handling-data/writing-state.mdx index 200ef6501..31921c515 100644 --- a/website/docs/plugin/framework/handling-data/writing-state.mdx +++ b/website/docs/plugin/framework/handling-data/writing-state.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Writing state One of the primary jobs of a Terraform provider is to manage the provider's diff --git a/website/docs/plugin/framework/index.mdx b/website/docs/plugin/framework/index.mdx index 59379ac0c..fa481feae 100644 --- a/website/docs/plugin/framework/index.mdx +++ b/website/docs/plugin/framework/index.mdx @@ -5,6 +5,9 @@ description: >- providers. Learn how the plugin framework works with Terraform core. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Terraform plugin framework The plugin framework is HashiCorp’s recommended way develop Terraform Plugins on [protocol version 6](/terraform/plugin/terraform-plugin-protocol#protocol-version-6) or [protocol version 5](/terraform/plugin/terraform-plugin-protocol#protocol-version-5). diff --git a/website/docs/plugin/framework/internals/index.mdx b/website/docs/plugin/framework/internals/index.mdx index 3212e2b71..a6cbcc3f7 100644 --- a/website/docs/plugin/framework/internals/index.mdx +++ b/website/docs/plugin/framework/internals/index.mdx @@ -5,6 +5,9 @@ description: >- Learn about the internal implementation details of the framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Framework internals The following information describes some internals of the Terraform Plugin Framework in order to provide a more in-depth view of specific aspects of Framework behaviour. diff --git a/website/docs/plugin/framework/internals/rpcs.mdx b/website/docs/plugin/framework/internals/rpcs.mdx index 4e12a18fe..a041489ce 100644 --- a/website/docs/plugin/framework/internals/rpcs.mdx +++ b/website/docs/plugin/framework/internals/rpcs.mdx @@ -4,6 +4,9 @@ description: >- Learn how Terraform uses RPCs to support provider functionality. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # RPCs and framework functionality The correlation between the Terraform command, the RPCs that are issued and the Terraform plugin framework methods that are called is as follows: diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx index 4042f31d2..7fdae9dd4 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/attribute-schema.mdx @@ -5,6 +5,9 @@ description: >- the terraform-plugin-mux Go library. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute schema Attributes define how users can configure values for your Terraform provider, resources, and data sources. Refer to diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx index 1f4ee4bcc..1da120a15 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks-computed.mdx @@ -5,6 +5,9 @@ description: >- validators in the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating blocks with computed fields Some providers, resources, and data sources include repeatable nested blocks in their attributes. Some blocks contain diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx index 353029dee..0b3a281de 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/blocks.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating blocks Some providers, resources, and data sources include repeatable nested blocks in their attributes. These nested blocks diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx index f358d8d62..adf349c28 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/default-values.mdx @@ -5,6 +5,9 @@ description: >- attribute plan modifier in the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute default values Default values support is only available in the Framework for resources. Handle default values for data source attributes within the [data source `Read` method](/terraform/plugin/framework/data-sources#read-method) and default values for provider attributes within the [provider `Configure` method](/terraform/plugin/framework/providers#configure-method). diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx index 6efe29b7a..33459851b 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/fields.mdx @@ -5,6 +5,9 @@ description: >- fields from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute fields A subset of attribute fields, such as required, optional, computed, or sensitive, define attribute behavior as boolean flags. Refer to diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx index 1a119eff6..4a4976be6 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/force-new.mdx @@ -5,6 +5,9 @@ description: >- modifiers in the framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute ForceNew triggers In Terraform, sometimes a resource must be replaced when the value of an attribute changes. In SDKv2, this is diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx index 86d633fa7..a29bcb47c 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/types.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate attribute type from SDKv2 to the plugin Framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute types An attribute either contains a primitive type, such as an integer or a string, or contains other attributes. Attributes diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx index 8c33f4c01..168607566 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-custom.mdx @@ -6,6 +6,9 @@ description: >- check attribute values for required syntax, types, and acceptable values. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating attribute custom validators You can write custom validations that give users feedback about required syntax, types, and acceptable values in your diff --git a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx index 33c32dfd4..cb015260f 100644 --- a/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx +++ b/website/docs/plugin/framework/migrating/attributes-blocks/validators-predefined.mdx @@ -7,6 +7,9 @@ description: >- and acceptable values. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating predefined attribute validators Attribute validators ensure that attributes do or do not contain specific values. You can use predefined validators for diff --git a/website/docs/plugin/framework/migrating/benefits.mdx b/website/docs/plugin/framework/migrating/benefits.mdx index 847b2c73f..362c271ad 100644 --- a/website/docs/plugin/framework/migrating/benefits.mdx +++ b/website/docs/plugin/framework/migrating/benefits.mdx @@ -6,6 +6,9 @@ description: >- over the previous SDKv2. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Benefits of migrating to the plugin framework We recommend using the plugin framework to develop your provider because it offers significant benefits in comparison to SDKv2. We designed the framework with feedback from thousands of existing providers, so the framework significantly improves upon the functionality available in SDKv2. diff --git a/website/docs/plugin/framework/migrating/data-sources/index.mdx b/website/docs/plugin/framework/migrating/data-sources/index.mdx index eaa78e446..747549c9a 100644 --- a/website/docs/plugin/framework/migrating/data-sources/index.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/index.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate a data source from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating data sources Data sources let Terraform reference external data. Unlike resources, Terraform does not create, update, or delete diff --git a/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx b/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx index 7b0105316..8cd1d2d55 100644 --- a/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx +++ b/website/docs/plugin/framework/migrating/data-sources/timeouts.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate timeouts from SDKv2 to the framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating timeouts The Framework can be used in conjunction with the [terraform-plugin-framework-timeouts](https://github.com/hashicorp/terraform-plugin-framework-timeouts) module in order to allow defining timeouts in configuration and have them be available in `Read` functions. diff --git a/website/docs/plugin/framework/migrating/index.mdx b/website/docs/plugin/framework/migrating/index.mdx index f13455039..d440a5931 100644 --- a/website/docs/plugin/framework/migrating/index.mdx +++ b/website/docs/plugin/framework/migrating/index.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate your provider from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Overview This guide helps you migrate a Terraform provider from SDKv2 to the plugin Framework. We recommend migrating because the Framework has abstractions that make it easier to use, and it represents the future of Terraform plugin development. Refer to [Plugin Framework Benefits](/terraform/plugin/framework-benefits) for higher level details about how the framework makes provider development easier and [Feature Comparison](/terraform/plugin/framework/migrating/benefits) for a detailed functionality comparison between the SDKv2 and the framework. diff --git a/website/docs/plugin/framework/migrating/mux.mdx b/website/docs/plugin/framework/migrating/mux.mdx index e0e57c4b7..6bac42693 100644 --- a/website/docs/plugin/framework/migrating/mux.mdx +++ b/website/docs/plugin/framework/migrating/mux.mdx @@ -5,6 +5,9 @@ description: >- the terraform-plugin-mux Go library. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Muxing Muxing enables multiple underlying provider implementations to exist within the same logical provider server via the [terraform-plugin-mux Go module](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-mux). Each underlying provider implementation serves different managed resources and data sources. Refer to the [Combining and Translating documentation](/terraform/plugin/mux) for full details about muxing configuration. diff --git a/website/docs/plugin/framework/migrating/providers/index.mdx b/website/docs/plugin/framework/migrating/providers/index.mdx index 6bc14ba9a..70d023623 100644 --- a/website/docs/plugin/framework/migrating/providers/index.mdx +++ b/website/docs/plugin/framework/migrating/providers/index.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating providers Providers are Terraform plugins that define resources and data sources for practitioners to use. You serve your diff --git a/website/docs/plugin/framework/migrating/resources/crud.mdx b/website/docs/plugin/framework/migrating/resources/crud.mdx index 645c0aae3..0b685b982 100644 --- a/website/docs/plugin/framework/migrating/resources/crud.mdx +++ b/website/docs/plugin/framework/migrating/resources/crud.mdx @@ -5,6 +5,9 @@ description: >- functions from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # CRUD functions In Terraform, a resource represents a single instance of a given resource type. They modify a specific resource in the diff --git a/website/docs/plugin/framework/migrating/resources/import.mdx b/website/docs/plugin/framework/migrating/resources/import.mdx index c7ebb97aa..cfae5ea13 100644 --- a/website/docs/plugin/framework/migrating/resources/import.mdx +++ b/website/docs/plugin/framework/migrating/resources/import.mdx @@ -6,6 +6,9 @@ description: >- their Terraform projects. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Resource import Practitioners can use the [`terraform import` command](/terraform/cli/commands/import) to let Terraform diff --git a/website/docs/plugin/framework/migrating/resources/index.mdx b/website/docs/plugin/framework/migrating/resources/index.mdx index 73d383a3a..337b63598 100644 --- a/website/docs/plugin/framework/migrating/resources/index.mdx +++ b/website/docs/plugin/framework/migrating/resources/index.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate resources from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating resources Resources are an abstraction that allow Terraform to manage infrastructure objects by defining create, read, update, diff --git a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx index af8241279..5822be101 100644 --- a/website/docs/plugin/framework/migrating/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/migrating/resources/plan-modification.mdx @@ -5,6 +5,9 @@ description: >- plan modifiers in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Plan modification Your provider can modify the Terraform plan to match the expected end state. This can include replacing unknown values diff --git a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx index 7b5c05b33..fef82a9d0 100644 --- a/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/migrating/resources/state-upgrade.mdx @@ -6,6 +6,9 @@ description: >- old schema configurations. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # State upgraders When you update a resource's implementation in your provider, some changes may not be compatible with old versions. You diff --git a/website/docs/plugin/framework/migrating/resources/timeouts.mdx b/website/docs/plugin/framework/migrating/resources/timeouts.mdx index a7a8d76c2..6b4ee3604 100644 --- a/website/docs/plugin/framework/migrating/resources/timeouts.mdx +++ b/website/docs/plugin/framework/migrating/resources/timeouts.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate timeouts from SDKv2 to the framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Timeouts The Framework can be used in conjunction with the [terraform-plugin-framework-timeouts](https://github.com/hashicorp/terraform-plugin-framework-timeouts) module in order to allow defining timeouts in configuration and have them be available in CRUD functions. diff --git a/website/docs/plugin/framework/migrating/schema/index.mdx b/website/docs/plugin/framework/migrating/schema/index.mdx index 5afd215f2..f9ffdb47f 100644 --- a/website/docs/plugin/framework/migrating/schema/index.mdx +++ b/website/docs/plugin/framework/migrating/schema/index.mdx @@ -4,6 +4,9 @@ description: >- Learn how to migrate schema from SDKv2 to the plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Migrating schema Providers, resources, and data sources all use schema to define their attributes and behavior. Schemas specify the diff --git a/website/docs/plugin/framework/migrating/testing.mdx b/website/docs/plugin/framework/migrating/testing.mdx index 79eb6ea74..3a7ae48ef 100644 --- a/website/docs/plugin/framework/migrating/testing.mdx +++ b/website/docs/plugin/framework/migrating/testing.mdx @@ -5,6 +5,9 @@ description: >- Framework does not affect provider behavior. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Testing During migration, you should [write tests](#testing-migration) to verify that the behaviour of your provider has not diff --git a/website/docs/plugin/framework/provider-servers.mdx b/website/docs/plugin/framework/provider-servers.mdx index a8d07bd5e..93c34eaac 100644 --- a/website/docs/plugin/framework/provider-servers.mdx +++ b/website/docs/plugin/framework/provider-servers.mdx @@ -6,6 +6,9 @@ description: >- APIs. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Provider servers Before a [provider](/terraform/plugin/framework/providers) can be used with Terraform, it must implement a [gRPC server](https://grpc.io) that supports Terraform-specific connection and handshake handling on startup. The server must then implement the [Terraform Plugin Protocol](/terraform/plugin/how-terraform-works#terraform-plugin-protocol). diff --git a/website/docs/plugin/framework/providers/index.mdx b/website/docs/plugin/framework/providers/index.mdx index 131047f55..b65e257b2 100644 --- a/website/docs/plugin/framework/providers/index.mdx +++ b/website/docs/plugin/framework/providers/index.mdx @@ -6,6 +6,9 @@ description: >- interact with APIs. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Providers Providers are Terraform plugins that define [resources](/terraform/plugin/framework/resources) and [data sources](/terraform/plugin/framework/data-sources) for practitioners to use. Providers are wrapped by a [provider server](/terraform/plugin/framework/provider-servers) for interacting with Terraform. diff --git a/website/docs/plugin/framework/providers/validate-configuration.mdx b/website/docs/plugin/framework/providers/validate-configuration.mdx index f59330f1c..7d5a49fe3 100644 --- a/website/docs/plugin/framework/providers/validate-configuration.mdx +++ b/website/docs/plugin/framework/providers/validate-configuration.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Validate provider configuration [Providers](/terraform/plugin/framework/providers) support validating an entire practitioner configuration in either declarative or imperative logic. Feedback, such as required syntax or acceptable combinations of values, is returned via [diagnostics](/terraform/plugin/framework/diagnostics). diff --git a/website/docs/plugin/framework/resources/configure.mdx b/website/docs/plugin/framework/resources/configure.mdx index 93dced950..412fd05b7 100644 --- a/website/docs/plugin/framework/resources/configure.mdx +++ b/website/docs/plugin/framework/resources/configure.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Configure resources [Resources](/terraform/plugin/framework/resources) may require provider-level data or remote system clients to operate correctly. The framework supports the ability to configure this data and/or clients once within the provider, then pass that information to resources by adding the `Configure` method. diff --git a/website/docs/plugin/framework/resources/create.mdx b/website/docs/plugin/framework/resources/create.mdx index db9ec0914..77951cd49 100644 --- a/website/docs/plugin/framework/resources/create.mdx +++ b/website/docs/plugin/framework/resources/create.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement resource creation in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Create resources Creation is part of the basic Terraform lifecycle for managing resources. During the [`terraform apply` command](/terraform/cli/commands/apply), Terraform calls the provider [`ApplyResourceChange`](/terraform/plugin/framework/internals/rpcs#applyresourcechange-rpc) RPC, in which the framework calls the [`resource.Resource` interface `Create` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Create). The request contains Terraform configuration and plan data. The response expects the applied Terraform state data, including any computed values. The data is defined by the [schema](/terraform/plugin/framework/data-handling/schemas) of the resource. diff --git a/website/docs/plugin/framework/resources/default.mdx b/website/docs/plugin/framework/resources/default.mdx index 5254d5c5b..cc697d138 100644 --- a/website/docs/plugin/framework/resources/default.mdx +++ b/website/docs/plugin/framework/resources/default.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Default values After [validation](/terraform/plugin/framework/validation) and before applying configuration changes, Terraform generates a plan that describes the expected values and behaviors of those changes. Resources can then tailor the plan to set default values on computed resource attributes that are null in the configuration. diff --git a/website/docs/plugin/framework/resources/delete.mdx b/website/docs/plugin/framework/resources/delete.mdx index 37c706e7f..d46cdd6b4 100644 --- a/website/docs/plugin/framework/resources/delete.mdx +++ b/website/docs/plugin/framework/resources/delete.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement resource deletion in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Delete resources Deletion is part of the basic Terraform lifecycle for managing resources. During the [`terraform apply` command](/terraform/cli/commands/apply), Terraform calls the provider [`ApplyResourceChange`](/terraform/plugin/framework/internals/rpcs#applyresourcechange-rpc) RPC, in which the framework calls the [`resource.Resource` interface `Delete` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Delete). The request contains Terraform prior state data. The response is only for returning diagnostics. The data is defined by the [schema](/terraform/plugin/framework/schemas) of the resource. diff --git a/website/docs/plugin/framework/resources/import.mdx b/website/docs/plugin/framework/resources/import.mdx index f8202686d..137391b4f 100644 --- a/website/docs/plugin/framework/resources/import.mdx +++ b/website/docs/plugin/framework/resources/import.mdx @@ -4,6 +4,9 @@ description: >- Learn how to support resource import using the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Resource import Practitioners can use the [`terraform import` command](/terraform/cli/commands/import) to let Terraform begin managing existing infrastructure resources. Resources can implement the `ImportState` method, which must either specify enough Terraform state for the `Read` method to refresh [`resource.Resource`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource) or return an error. diff --git a/website/docs/plugin/framework/resources/index.mdx b/website/docs/plugin/framework/resources/index.mdx index 6aa9000c8..fee87055f 100644 --- a/website/docs/plugin/framework/resources/index.mdx +++ b/website/docs/plugin/framework/resources/index.mdx @@ -5,6 +5,9 @@ description: >- allow Terraform to manage infrastructure objects. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Resources [Resources](/terraform/language/resources) are an abstraction that allow Terraform to manage infrastructure objects, such as a compute instance, an access policy, or disk. Terraform assumes that every resource: diff --git a/website/docs/plugin/framework/resources/plan-modification.mdx b/website/docs/plugin/framework/resources/plan-modification.mdx index bcab40d8d..97eff9ce1 100644 --- a/website/docs/plugin/framework/resources/plan-modification.mdx +++ b/website/docs/plugin/framework/resources/plan-modification.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Plan modification After [validation](/terraform/plugin/framework/validation) and before applying configuration changes, Terraform generates a plan that describes the expected values and behaviors of those changes. Resources can then tailor the plan to match the expected end state, prevent errant in-place updates, or return any [diagnostics](/terraform/plugin/framework/diagnostics). diff --git a/website/docs/plugin/framework/resources/private-state.mdx b/website/docs/plugin/framework/resources/private-state.mdx index 244b1a9b9..f813b74fa 100644 --- a/website/docs/plugin/framework/resources/private-state.mdx +++ b/website/docs/plugin/framework/resources/private-state.mdx @@ -5,6 +5,9 @@ description: >- Private state is provider-only data storage for resources. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Private state management Resource private state is provider maintained data that is stored in Terraform state alongside the schema-defined data. Private state is never accessed or exposed by Terraform plans, however providers can use this data storage for advanced use cases. diff --git a/website/docs/plugin/framework/resources/read.mdx b/website/docs/plugin/framework/resources/read.mdx index dc0161ae9..567986190 100644 --- a/website/docs/plugin/framework/resources/read.mdx +++ b/website/docs/plugin/framework/resources/read.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement resource read in the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Read resources Read (refresh) is part of the basic Terraform lifecycle for managing resources. During the [`terraform apply`](/terraform/cli/commands/apply), [`terraform plan`](/terraform/cli/commands/plan), and [`terraform refresh`](/terraform/cli/commands/refresh) commands, Terraform calls the provider [`ReadResource`](/terraform/plugin/framework/internals/rpcs#readresource-rpc) RPC, in which the framework calls the [`resource.Resource` interface `Read` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Read). The `Read` method is also executed after [resource import](/terraform/plugin/framework/resources/import). The request contains Terraform prior state data. The response contains the refreshed state data. The data is defined by the [schema](/terraform/plugin/framework/schemas) of the resource. diff --git a/website/docs/plugin/framework/resources/state-move.mdx b/website/docs/plugin/framework/resources/state-move.mdx index c97dc57cd..d7f8f58d8 100644 --- a/website/docs/plugin/framework/resources/state-move.mdx +++ b/website/docs/plugin/framework/resources/state-move.mdx @@ -5,6 +5,9 @@ description: >- the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # State move diff --git a/website/docs/plugin/framework/resources/state-upgrade.mdx b/website/docs/plugin/framework/resources/state-upgrade.mdx index 31fd5c678..bf34e7f46 100644 --- a/website/docs/plugin/framework/resources/state-upgrade.mdx +++ b/website/docs/plugin/framework/resources/state-upgrade.mdx @@ -5,6 +5,9 @@ description: >- one version of your Terraform framework provider to another. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # State upgrade A resource schema captures the structure and types of the resource [state](/terraform/language/state). Any state data that does not conform to the resource schema will generate errors or may not be persisted properly. Over time, it may be necessary for resources to make breaking changes to their schemas, such as changing an attribute type. Terraform supports versioning of these resource schemas and the current version is saved into the Terraform state. When the provider advertises a newer schema version, Terraform will call back to the provider to attempt to upgrade from the saved schema version to the one advertised. This operation is performed prior to planning, but with a configured provider. diff --git a/website/docs/plugin/framework/resources/timeouts.mdx b/website/docs/plugin/framework/resources/timeouts.mdx index db14bae0c..db22cd7f4 100644 --- a/website/docs/plugin/framework/resources/timeouts.mdx +++ b/website/docs/plugin/framework/resources/timeouts.mdx @@ -4,6 +4,9 @@ description: >- Learn how to implement timeouts with the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Timeouts The reality of cloud infrastructure is that it typically takes time to perform operations such as booting operating systems, discovering services, and replicating state across network edges. As the provider developer you should take known delays in resource APIs into account in the CRUD functions of the resource. Terraform supports configurable timeouts to assist in these situations. diff --git a/website/docs/plugin/framework/resources/update.mdx b/website/docs/plugin/framework/resources/update.mdx index a892b4161..3bf3f4de4 100644 --- a/website/docs/plugin/framework/resources/update.mdx +++ b/website/docs/plugin/framework/resources/update.mdx @@ -5,6 +5,9 @@ description: >- plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Update resources In-place update is part of the basic Terraform lifecycle for managing resources. During the [`terraform apply` command](/terraform/cli/commands/apply), Terraform calls the provider [`ApplyResourceChange`](/terraform/plugin/framework/internals/rpcs#applyresourcechange-rpc) RPC, in which the framework calls the [`resource.Resource` interface `Update` method](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/resource#Resource.Update). The request contains Terraform prior state, configuration, and plan data. The response contains updated state data. The data is defined by the [schema](/terraform/plugin/framework/schemas) of the resource. diff --git a/website/docs/plugin/framework/resources/validate-configuration.mdx b/website/docs/plugin/framework/resources/validate-configuration.mdx index 4a1d93661..27ab2dc17 100644 --- a/website/docs/plugin/framework/resources/validate-configuration.mdx +++ b/website/docs/plugin/framework/resources/validate-configuration.mdx @@ -5,6 +5,9 @@ description: >- framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Validate configuration [Resources](/terraform/plugin/framework/resources) support validating an entire practitioner configuration in either declarative or imperative logic. Feedback, such as required syntax or acceptable combinations of values, is returned via [diagnostics](/terraform/plugin/framework/diagnostics). diff --git a/website/docs/plugin/framework/resources/write-only-arguments.mdx b/website/docs/plugin/framework/resources/write-only-arguments.mdx index 58a3da1e0..c993a5755 100644 --- a/website/docs/plugin/framework/resources/write-only-arguments.mdx +++ b/website/docs/plugin/framework/resources/write-only-arguments.mdx @@ -4,6 +4,9 @@ description: >- How to implement write-only arguments with the provider development framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Write-only Arguments Write-only arguments are managed resource attributes that are configured by practitioners but are not persisted to the Terraform plan or state artifacts. Write-only arguments are supported in Terraform 1.11 and later. diff --git a/website/docs/plugin/framework/validation.mdx b/website/docs/plugin/framework/validation.mdx index fedfbafb9..2daf5bd0b 100644 --- a/website/docs/plugin/framework/validation.mdx +++ b/website/docs/plugin/framework/validation.mdx @@ -4,6 +4,9 @@ description: >- Learn how to validate configuration values using the Terraform plugin framework. --- +> [!IMPORTANT] +> **Documentation Update:** Product documentation previously located in `/website` has moved to the [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs) repository, where all product documentation is now centralized. Please make contributions directly to `web-unified-docs`, since changes to `/website` in this repository will not appear on developer.hashicorp.com. + # Validation The framework can return [diagnostics](/terraform/plugin/framework/diagnostics) feedback for values in provider, resource, and data source configurations or [errors](/terraform/plugin/framework/functions/errors) feedback for values in function parameters. This allows you to write validations that give users feedback about required syntax, types, and acceptable values. From c3ce36e0e2f0819e4b44c9c88b6911af6ab0f89b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:21:59 -0500 Subject: [PATCH 23/42] build(deps): Bump github.com/google/go-cmp from 0.6.0 to 0.7.0 (#1100) Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/google/go-cmp/releases) - [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: github.com/google/go-cmp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 207c0347e..ed148f3eb 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 toolchain go1.22.7 require ( - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/hashicorp/terraform-plugin-go v0.26.0 github.com/hashicorp/terraform-plugin-log v0.9.0 ) diff --git a/go.sum b/go.sum index 5a586522d..c5bf7b0e6 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= From a2ae84f250fbe4649e0a80afbc4f67eac9cb32a0 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:21:28 +0100 Subject: [PATCH 24/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1108) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index ff447cff9..37a620cdd 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -88,7 +88,7 @@ jobs: - run: go mod download - run: go test -coverprofile=coverage.out ./... - run: go tool cover -html=coverage.out -o coverage.html - - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: go-${{ matrix.go-version }}-coverage path: coverage.html From 2f5a4aacafddf659cb010a397165e05ec66e0e4b Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Tue, 18 Mar 2025 09:03:05 -0400 Subject: [PATCH 25/42] chore: Update minimum Go version in module (#1114) * chore: Update minimum Go version in module * add changelogs --- .../unreleased/NOTES-20250317-153311.yaml | 7 ++++++ .../NOTES-20250317-153311.yaml | 7 ++++++ .copywrite.hcl | 2 +- .github/workflows/ci-go.yml | 2 +- README.md | 2 +- go.mod | 10 ++++---- go.sum | 12 +++++----- tools/go.mod | 14 +++++------ tools/go.sum | 24 +++++++++---------- 9 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 .changes/unreleased/NOTES-20250317-153311.yaml create mode 100644 .changes/unreleased/upcoming-stable/NOTES-20250317-153311.yaml diff --git a/.changes/unreleased/NOTES-20250317-153311.yaml b/.changes/unreleased/NOTES-20250317-153311.yaml new file mode 100644 index 000000000..032cbf254 --- /dev/null +++ b/.changes/unreleased/NOTES-20250317-153311.yaml @@ -0,0 +1,7 @@ +kind: NOTES +body: 'all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). + It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) + before upgrading. Any consumers building on earlier Go versions may experience errors.' +time: 2025-03-17T15:33:11.058742-04:00 +custom: + Issue: "1114" diff --git a/.changes/unreleased/upcoming-stable/NOTES-20250317-153311.yaml b/.changes/unreleased/upcoming-stable/NOTES-20250317-153311.yaml new file mode 100644 index 000000000..032cbf254 --- /dev/null +++ b/.changes/unreleased/upcoming-stable/NOTES-20250317-153311.yaml @@ -0,0 +1,7 @@ +kind: NOTES +body: 'all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). + It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) + before upgrading. Any consumers building on earlier Go versions may experience errors.' +time: 2025-03-17T15:33:11.058742-04:00 +custom: + Issue: "1114" diff --git a/.copywrite.hcl b/.copywrite.hcl index 12b18fcf4..0494a4fe5 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -6,7 +6,7 @@ project { header_ignore = [ # changie tooling configuration and CHANGELOG entries (prose) - ".changes/unreleased/*.yaml", + ".changes/unreleased/**", ".changie.yaml", # GitHub issue template configuration diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 37a620cdd..5aad2f144 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: [ '1.23', '1.22' ] + go-version: [ '1.24', '1.23' ] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 diff --git a/README.md b/README.md index 294ba5ead..0b2041e25 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Providers built with this framework are compatible with Terraform version v0.12 This project follows the [support policy](https://golang.org/doc/devel/release.html#policy) of Go as its support policy. The two latest major releases of Go are supported by the project. -Currently, that means Go **1.22** or later must be used when including this project as a dependency. +Currently, that means Go **1.23** or later must be used when including this project as a dependency. ## Contributing diff --git a/go.mod b/go.mod index ed148f3eb..76694c5ec 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/hashicorp/terraform-plugin-framework -go 1.22.0 +go 1.23.0 -toolchain go1.22.7 +toolchain go1.23.7 require ( github.com/google/go-cmp v0.7.0 @@ -25,9 +25,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.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/grpc v1.69.4 // indirect google.golang.org/protobuf v1.36.3 // indirect diff --git a/go.sum b/go.sum index c5bf7b0e6..1180f5320 100644 --- a/go.sum +++ b/go.sum @@ -64,18 +64,18 @@ go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4Jjx go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= diff --git a/tools/go.mod b/tools/go.mod index 7a3a2905f..5c4a1c97d 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,6 +1,6 @@ module tools -go 1.22.7 +go 1.23.7 require github.com/hashicorp/copywrite v0.21.0 @@ -48,14 +48,14 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/thanhpk/randstr v1.0.4 // indirect go.mongodb.org/mongo-driver v1.10.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.37.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tools/go.sum b/tools/go.sum index 6da31e8ee..797116e3d 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -374,8 +374,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= @@ -413,8 +413,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -431,8 +431,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -479,16 +479,16 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -500,8 +500,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 88524807bad5d550195af9914d05654b86e9d23f Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Tue, 18 Mar 2025 16:05:31 -0400 Subject: [PATCH 26/42] Resource Identity: Initial implementation + Schemas (#1107) * new attribute additions (breaks build for existing internal schema attributes) * update internal testing/testschema * implement in datasource/schema * implement in ephemeral/schema * implement in provider/metaschema * implement in provider/schema * implement in resource/schema * add to final internal/testing/testschema * get resource identity schema implementation * protov6 + tests * fix up package docs + build specific TODO comments * spellcheck! * update go dep * update go sum --- datasource/schema/bool_attribute.go | 12 + datasource/schema/bool_attribute_test.go | 52 + datasource/schema/dynamic_attribute.go | 12 + datasource/schema/dynamic_attribute_test.go | 52 + datasource/schema/float32_attribute.go | 12 + datasource/schema/float32_attribute_test.go | 52 + datasource/schema/float64_attribute.go | 12 + datasource/schema/float64_attribute_test.go | 52 + datasource/schema/int32_attribute.go | 12 + datasource/schema/int32_attribute_test.go | 52 + datasource/schema/int64_attribute.go | 12 + datasource/schema/int64_attribute_test.go | 52 + datasource/schema/list_attribute.go | 12 + datasource/schema/list_attribute_test.go | 52 + datasource/schema/list_nested_attribute.go | 12 + .../schema/list_nested_attribute_test.go | 52 + datasource/schema/map_attribute.go | 12 + datasource/schema/map_attribute_test.go | 52 + datasource/schema/map_nested_attribute.go | 12 + .../schema/map_nested_attribute_test.go | 52 + datasource/schema/number_attribute.go | 12 + datasource/schema/number_attribute_test.go | 52 + datasource/schema/object_attribute.go | 12 + datasource/schema/object_attribute_test.go | 52 + datasource/schema/set_attribute.go | 12 + datasource/schema/set_attribute_test.go | 52 + datasource/schema/set_nested_attribute.go | 12 + .../schema/set_nested_attribute_test.go | 52 + datasource/schema/single_nested_attribute.go | 12 + .../schema/single_nested_attribute_test.go | 52 + datasource/schema/string_attribute.go | 12 + datasource/schema/string_attribute_test.go | 52 + ephemeral/schema/bool_attribute.go | 12 + ephemeral/schema/bool_attribute_test.go | 52 + ephemeral/schema/dynamic_attribute.go | 12 + ephemeral/schema/dynamic_attribute_test.go | 52 + ephemeral/schema/float32_attribute.go | 12 + ephemeral/schema/float32_attribute_test.go | 52 + ephemeral/schema/float64_attribute.go | 12 + ephemeral/schema/float64_attribute_test.go | 52 + ephemeral/schema/int32_attribute.go | 12 + ephemeral/schema/int32_attribute_test.go | 52 + ephemeral/schema/int64_attribute.go | 12 + ephemeral/schema/int64_attribute_test.go | 52 + ephemeral/schema/list_attribute.go | 12 + ephemeral/schema/list_attribute_test.go | 52 + ephemeral/schema/list_nested_attribute.go | 12 + .../schema/list_nested_attribute_test.go | 52 + ephemeral/schema/map_attribute.go | 12 + ephemeral/schema/map_attribute_test.go | 52 + ephemeral/schema/map_nested_attribute.go | 12 + ephemeral/schema/map_nested_attribute_test.go | 52 + ephemeral/schema/number_attribute.go | 12 + ephemeral/schema/number_attribute_test.go | 52 + ephemeral/schema/object_attribute.go | 12 + ephemeral/schema/object_attribute_test.go | 52 + ephemeral/schema/set_attribute.go | 12 + ephemeral/schema/set_attribute_test.go | 52 + ephemeral/schema/set_nested_attribute.go | 12 + ephemeral/schema/set_nested_attribute_test.go | 52 + ephemeral/schema/single_nested_attribute.go | 12 + .../schema/single_nested_attribute_test.go | 52 + ephemeral/schema/string_attribute.go | 12 + ephemeral/schema/string_attribute_test.go | 52 + go.mod | 10 +- go.sum | 42 +- .../fromproto5/getresourceidentityschemas.go | 23 + .../getresourceidentityschemas_test.go | 44 + .../fromproto6/getresourceidentityschemas.go | 23 + .../getresourceidentityschemas_test.go | 44 + internal/fwschema/attribute.go | 18 + internal/fwserver/server.go | 45 + .../server_getresourceidentityschemas.go | 34 + .../server_getresourceidentityschemas_test.go | 229 +++++ .../server_getresourceidentityschemas.go | 27 + .../server_getresourceidentityschemas_test.go | 218 +++++ .../server_upgraderesourceidentity.go | 15 + .../server_getresourceidentityschemas.go | 27 + .../server_getresourceidentityschemas_test.go | 218 +++++ .../server_upgraderesourceidentity.go | 15 + .../testprovider/resourcewithidentity.go | 30 + internal/testing/testschema/attribute.go | 12 + .../testschema/attributewithbooldefault.go | 12 + .../attributewithboolplanmodifiers.go | 12 + .../testschema/attributewithboolvalidators.go | 12 + .../testschema/attributewithdynamicdefault.go | 12 + .../attributewithdynamicplanmodifiers.go | 12 + .../attributewithdynamicvalidators.go | 12 + .../testschema/attributewithfloat32default.go | 12 + .../attributewithfloat32planmodifiers.go | 12 + .../attributewithfloat32validators.go | 12 + .../testschema/attributewithfloat64default.go | 12 + .../attributewithfloat64planmodifiers.go | 12 + .../attributewithfloat64validators.go | 12 + .../testschema/attributewithint32default.go | 12 + .../attributewithint32planmodifiers.go | 12 + .../attributewithint32validators.go | 12 + .../testschema/attributewithint64default.go | 12 + .../attributewithint64planmodifiers.go | 12 + .../attributewithint64validators.go | 12 + .../testschema/attributewithlistdefault.go | 12 + .../attributewithlistplanmodifiers.go | 12 + .../testschema/attributewithlistvalidators.go | 12 + .../testschema/attributewithmapdefault.go | 12 + .../attributewithmapplanmodifiers.go | 12 + .../testschema/attributewithmapvalidators.go | 12 + .../testschema/attributewithnumberdefault.go | 12 + .../attributewithnumberplanmodifiers.go | 12 + .../attributewithnumbervalidators.go | 12 + .../testschema/attributewithobjectdefault.go | 12 + .../attributewithobjectplanmodifiers.go | 12 + .../attributewithobjectvalidators.go | 12 + .../testschema/attributewithsetdefault.go | 12 + .../attributewithsetplanmodifiers.go | 12 + .../testschema/attributewithsetvalidators.go | 12 + .../testschema/attributewithstringdefault.go | 12 + .../attributewithstringplanmodifiers.go | 12 + .../attributewithstringvalidators.go | 12 + .../testing/testschema/nested_attribute.go | 12 + .../nested_attribute_with_list_default.go | 12 + ...sted_attribute_with_list_plan_modifiers.go | 12 + .../nested_attribute_with_map_default.go | 12 + ...ested_attribute_with_map_plan_modifiers.go | 12 + .../nested_attribute_with_object_default.go | 12 + ...ed_attribute_with_object_plan_modifiers.go | 12 + .../nested_attribute_with_set_default.go | 12 + ...ested_attribute_with_set_plan_modifiers.go | 12 + .../toproto5/getresourceidentityschemas.go | 42 + .../getresourceidentityschemas_test.go | 367 +++++++ internal/toproto5/identity_schema.go | 52 + .../toproto5/identity_schema_attribute.go | 43 + .../identity_schema_attribute_test.go | 257 +++++ internal/toproto5/identity_schema_test.go | 138 +++ .../toproto6/getresourceidentityschemas.go | 42 + .../getresourceidentityschemas_test.go | 367 +++++++ internal/toproto6/identity_schema.go | 52 + .../toproto6/identity_schema_attribute.go | 43 + .../identity_schema_attribute_test.go | 257 +++++ internal/toproto6/identity_schema_test.go | 138 +++ provider/metaschema/bool_attribute.go | 12 + provider/metaschema/bool_attribute_test.go | 52 + provider/metaschema/float64_attribute.go | 12 + provider/metaschema/float64_attribute_test.go | 52 + provider/metaschema/int64_attribute.go | 12 + provider/metaschema/int64_attribute_test.go | 52 + provider/metaschema/list_attribute.go | 12 + provider/metaschema/list_attribute_test.go | 52 + provider/metaschema/list_nested_attribute.go | 12 + .../metaschema/list_nested_attribute_test.go | 52 + provider/metaschema/map_attribute.go | 12 + provider/metaschema/map_attribute_test.go | 52 + provider/metaschema/map_nested_attribute.go | 12 + .../metaschema/map_nested_attribute_test.go | 52 + provider/metaschema/number_attribute.go | 12 + provider/metaschema/number_attribute_test.go | 52 + provider/metaschema/object_attribute.go | 12 + provider/metaschema/object_attribute_test.go | 52 + provider/metaschema/set_attribute.go | 12 + provider/metaschema/set_attribute_test.go | 52 + provider/metaschema/set_nested_attribute.go | 12 + .../metaschema/set_nested_attribute_test.go | 52 + .../metaschema/single_nested_attribute.go | 12 + .../single_nested_attribute_test.go | 52 + provider/metaschema/string_attribute.go | 12 + provider/metaschema/string_attribute_test.go | 52 + provider/schema/bool_attribute.go | 12 + provider/schema/bool_attribute_test.go | 52 + provider/schema/dynamic_attribute.go | 12 + provider/schema/dynamic_attribute_test.go | 52 + provider/schema/float32_attribute.go | 12 + provider/schema/float32_attribute_test.go | 52 + provider/schema/float64_attribute.go | 12 + provider/schema/float64_attribute_test.go | 52 + provider/schema/int32_attribute.go | 12 + provider/schema/int32_attribute_test.go | 52 + provider/schema/int64_attribute.go | 12 + provider/schema/int64_attribute_test.go | 52 + provider/schema/list_attribute.go | 12 + provider/schema/list_attribute_test.go | 52 + provider/schema/list_nested_attribute.go | 12 + provider/schema/list_nested_attribute_test.go | 52 + provider/schema/map_attribute.go | 12 + provider/schema/map_attribute_test.go | 52 + provider/schema/map_nested_attribute.go | 12 + provider/schema/map_nested_attribute_test.go | 52 + provider/schema/number_attribute.go | 12 + provider/schema/number_attribute_test.go | 52 + provider/schema/object_attribute.go | 12 + provider/schema/object_attribute_test.go | 52 + provider/schema/set_attribute.go | 12 + provider/schema/set_attribute_test.go | 52 + provider/schema/set_nested_attribute.go | 12 + provider/schema/set_nested_attribute_test.go | 52 + provider/schema/single_nested_attribute.go | 12 + .../schema/single_nested_attribute_test.go | 52 + provider/schema/string_attribute.go | 12 + provider/schema/string_attribute_test.go | 52 + resource/identity_schema.go | 27 + resource/identityschema/attribute.go | 33 + resource/identityschema/bool_attribute.go | 127 +++ .../identityschema/bool_attribute_test.go | 431 +++++++++ resource/identityschema/doc.go | 9 + resource/identityschema/float32_attribute.go | 130 +++ .../identityschema/float32_attribute_test.go | 431 +++++++++ resource/identityschema/float64_attribute.go | 130 +++ .../identityschema/float64_attribute_test.go | 431 +++++++++ resource/identityschema/int32_attribute.go | 130 +++ .../identityschema/int32_attribute_test.go | 431 +++++++++ resource/identityschema/int64_attribute.go | 130 +++ .../identityschema/int64_attribute_test.go | 431 +++++++++ resource/identityschema/list_attribute.go | 169 ++++ .../identityschema/list_attribute_test.go | 497 ++++++++++ resource/identityschema/number_attribute.go | 131 +++ .../identityschema/number_attribute_test.go | 431 +++++++++ resource/identityschema/schema.go | 145 +++ resource/identityschema/schema_test.go | 915 ++++++++++++++++++ resource/identityschema/string_attribute.go | 127 +++ .../identityschema/string_attribute_test.go | 431 +++++++++ resource/resource.go | 10 + resource/schema/bool_attribute.go | 12 + resource/schema/bool_attribute_test.go | 52 + resource/schema/dynamic_attribute.go | 12 + resource/schema/dynamic_attribute_test.go | 52 + resource/schema/float32_attribute.go | 12 + resource/schema/float32_attribute_test.go | 52 + resource/schema/float64_attribute.go | 12 + resource/schema/float64_attribute_test.go | 52 + resource/schema/int32_attribute.go | 12 + resource/schema/int32_attribute_test.go | 52 + resource/schema/int64_attribute.go | 12 + resource/schema/int64_attribute_test.go | 52 + resource/schema/list_attribute.go | 12 + resource/schema/list_attribute_test.go | 52 + resource/schema/list_nested_attribute.go | 12 + resource/schema/list_nested_attribute_test.go | 52 + resource/schema/map_attribute.go | 12 + resource/schema/map_attribute_test.go | 52 + resource/schema/map_nested_attribute.go | 12 + resource/schema/map_nested_attribute_test.go | 52 + resource/schema/number_attribute.go | 12 + resource/schema/number_attribute_test.go | 52 + resource/schema/object_attribute.go | 12 + resource/schema/object_attribute_test.go | 52 + resource/schema/set_attribute.go | 12 + resource/schema/set_attribute_test.go | 52 + resource/schema/set_nested_attribute.go | 12 + resource/schema/set_nested_attribute_test.go | 52 + resource/schema/single_nested_attribute.go | 12 + .../schema/single_nested_attribute_test.go | 52 + resource/schema/string_attribute.go | 12 + resource/schema/string_attribute_test.go | 52 + 251 files changed, 14042 insertions(+), 25 deletions(-) create mode 100644 internal/fromproto5/getresourceidentityschemas.go create mode 100644 internal/fromproto5/getresourceidentityschemas_test.go create mode 100644 internal/fromproto6/getresourceidentityschemas.go create mode 100644 internal/fromproto6/getresourceidentityschemas_test.go create mode 100644 internal/fwserver/server_getresourceidentityschemas.go create mode 100644 internal/fwserver/server_getresourceidentityschemas_test.go create mode 100644 internal/proto5server/server_getresourceidentityschemas.go create mode 100644 internal/proto5server/server_getresourceidentityschemas_test.go create mode 100644 internal/proto5server/server_upgraderesourceidentity.go create mode 100644 internal/proto6server/server_getresourceidentityschemas.go create mode 100644 internal/proto6server/server_getresourceidentityschemas_test.go create mode 100644 internal/proto6server/server_upgraderesourceidentity.go create mode 100644 internal/testing/testprovider/resourcewithidentity.go create mode 100644 internal/toproto5/getresourceidentityschemas.go create mode 100644 internal/toproto5/getresourceidentityschemas_test.go create mode 100644 internal/toproto5/identity_schema.go create mode 100644 internal/toproto5/identity_schema_attribute.go create mode 100644 internal/toproto5/identity_schema_attribute_test.go create mode 100644 internal/toproto5/identity_schema_test.go create mode 100644 internal/toproto6/getresourceidentityschemas.go create mode 100644 internal/toproto6/getresourceidentityschemas_test.go create mode 100644 internal/toproto6/identity_schema.go create mode 100644 internal/toproto6/identity_schema_attribute.go create mode 100644 internal/toproto6/identity_schema_attribute_test.go create mode 100644 internal/toproto6/identity_schema_test.go create mode 100644 resource/identity_schema.go create mode 100644 resource/identityschema/attribute.go create mode 100644 resource/identityschema/bool_attribute.go create mode 100644 resource/identityschema/bool_attribute_test.go create mode 100644 resource/identityschema/doc.go create mode 100644 resource/identityschema/float32_attribute.go create mode 100644 resource/identityschema/float32_attribute_test.go create mode 100644 resource/identityschema/float64_attribute.go create mode 100644 resource/identityschema/float64_attribute_test.go create mode 100644 resource/identityschema/int32_attribute.go create mode 100644 resource/identityschema/int32_attribute_test.go create mode 100644 resource/identityschema/int64_attribute.go create mode 100644 resource/identityschema/int64_attribute_test.go create mode 100644 resource/identityschema/list_attribute.go create mode 100644 resource/identityschema/list_attribute_test.go create mode 100644 resource/identityschema/number_attribute.go create mode 100644 resource/identityschema/number_attribute_test.go create mode 100644 resource/identityschema/schema.go create mode 100644 resource/identityschema/schema_test.go create mode 100644 resource/identityschema/string_attribute.go create mode 100644 resource/identityschema/string_attribute_test.go diff --git a/datasource/schema/bool_attribute.go b/datasource/schema/bool_attribute.go index 1d984a8db..5830d6c29 100644 --- a/datasource/schema/bool_attribute.go +++ b/datasource/schema/bool_attribute.go @@ -191,3 +191,15 @@ func (a BoolAttribute) IsWriteOnly() bool { func (a BoolAttribute) IsSensitive() bool { return a.Sensitive } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsOptionalForImport() bool { + return false +} diff --git a/datasource/schema/bool_attribute_test.go b/datasource/schema/bool_attribute_test.go index ea22a21fc..e2fa2d2a6 100644 --- a/datasource/schema/bool_attribute_test.go +++ b/datasource/schema/bool_attribute_test.go @@ -428,3 +428,55 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/dynamic_attribute.go b/datasource/schema/dynamic_attribute.go index 4659cadf6..2d489fe83 100644 --- a/datasource/schema/dynamic_attribute.go +++ b/datasource/schema/dynamic_attribute.go @@ -188,6 +188,18 @@ func (a DynamicAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsOptionalForImport() bool { + return false +} + // DynamicValidators returns the Validators field value. func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { return a.Validators diff --git a/datasource/schema/dynamic_attribute_test.go b/datasource/schema/dynamic_attribute_test.go index 7166ae9cb..05cd9e340 100644 --- a/datasource/schema/dynamic_attribute_test.go +++ b/datasource/schema/dynamic_attribute_test.go @@ -428,3 +428,55 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { }) } } + +func TestDynamicAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestDynamicAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/float32_attribute.go b/datasource/schema/float32_attribute.go index d2510f5c3..c7c09f6c2 100644 --- a/datasource/schema/float32_attribute.go +++ b/datasource/schema/float32_attribute.go @@ -194,3 +194,15 @@ func (a Float32Attribute) IsSensitive() bool { func (a Float32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/datasource/schema/float32_attribute_test.go b/datasource/schema/float32_attribute_test.go index 3149803e0..152ef32c0 100644 --- a/datasource/schema/float32_attribute_test.go +++ b/datasource/schema/float32_attribute_test.go @@ -428,3 +428,55 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/float64_attribute.go b/datasource/schema/float64_attribute.go index 1d893dd33..b688653e3 100644 --- a/datasource/schema/float64_attribute.go +++ b/datasource/schema/float64_attribute.go @@ -194,3 +194,15 @@ func (a Float64Attribute) IsSensitive() bool { func (a Float64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/datasource/schema/float64_attribute_test.go b/datasource/schema/float64_attribute_test.go index c73193387..a99d9154c 100644 --- a/datasource/schema/float64_attribute_test.go +++ b/datasource/schema/float64_attribute_test.go @@ -428,3 +428,55 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/int32_attribute.go b/datasource/schema/int32_attribute.go index d06a9b8ce..44c2b631f 100644 --- a/datasource/schema/int32_attribute.go +++ b/datasource/schema/int32_attribute.go @@ -194,3 +194,15 @@ func (a Int32Attribute) IsSensitive() bool { func (a Int32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/datasource/schema/int32_attribute_test.go b/datasource/schema/int32_attribute_test.go index 15404c9e8..2e38dc919 100644 --- a/datasource/schema/int32_attribute_test.go +++ b/datasource/schema/int32_attribute_test.go @@ -428,3 +428,55 @@ func TestInt32AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/int64_attribute.go b/datasource/schema/int64_attribute.go index 85bd4a445..c5dc49dc7 100644 --- a/datasource/schema/int64_attribute.go +++ b/datasource/schema/int64_attribute.go @@ -194,3 +194,15 @@ func (a Int64Attribute) IsSensitive() bool { func (a Int64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/datasource/schema/int64_attribute_test.go b/datasource/schema/int64_attribute_test.go index ed0544c62..c692c2b3f 100644 --- a/datasource/schema/int64_attribute_test.go +++ b/datasource/schema/int64_attribute_test.go @@ -428,3 +428,55 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/list_attribute.go b/datasource/schema/list_attribute.go index 2e5140602..d581d6638 100644 --- a/datasource/schema/list_attribute.go +++ b/datasource/schema/list_attribute.go @@ -213,6 +213,18 @@ func (a ListAttribute) ListValidators() []validator.List { return a.Validators } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/datasource/schema/list_attribute_test.go b/datasource/schema/list_attribute_test.go index c38c41e2f..d28a3732d 100644 --- a/datasource/schema/list_attribute_test.go +++ b/datasource/schema/list_attribute_test.go @@ -524,3 +524,55 @@ func TestListAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/list_nested_attribute.go b/datasource/schema/list_nested_attribute.go index 922320627..f2b88a87c 100644 --- a/datasource/schema/list_nested_attribute.go +++ b/datasource/schema/list_nested_attribute.go @@ -236,6 +236,18 @@ func (a ListNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsOptionalForImport() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/datasource/schema/list_nested_attribute_test.go b/datasource/schema/list_nested_attribute_test.go index b7ea5be20..70725f0eb 100644 --- a/datasource/schema/list_nested_attribute_test.go +++ b/datasource/schema/list_nested_attribute_test.go @@ -689,3 +689,55 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/map_attribute.go b/datasource/schema/map_attribute.go index 3d6c57680..164133370 100644 --- a/datasource/schema/map_attribute.go +++ b/datasource/schema/map_attribute.go @@ -211,6 +211,18 @@ func (a MapAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/datasource/schema/map_attribute_test.go b/datasource/schema/map_attribute_test.go index af361a636..f72a8b496 100644 --- a/datasource/schema/map_attribute_test.go +++ b/datasource/schema/map_attribute_test.go @@ -524,3 +524,55 @@ func TestMapAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/map_nested_attribute.go b/datasource/schema/map_nested_attribute.go index 9bf1bb957..30c0c2240 100644 --- a/datasource/schema/map_nested_attribute.go +++ b/datasource/schema/map_nested_attribute.go @@ -236,6 +236,18 @@ func (a MapNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/datasource/schema/map_nested_attribute_test.go b/datasource/schema/map_nested_attribute_test.go index d691cda82..e5db0a5c2 100644 --- a/datasource/schema/map_nested_attribute_test.go +++ b/datasource/schema/map_nested_attribute_test.go @@ -689,3 +689,55 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/number_attribute.go b/datasource/schema/number_attribute.go index c21f74a15..fc12b09dd 100644 --- a/datasource/schema/number_attribute.go +++ b/datasource/schema/number_attribute.go @@ -191,6 +191,18 @@ func (a NumberAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsOptionalForImport() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/datasource/schema/number_attribute_test.go b/datasource/schema/number_attribute_test.go index c30b711eb..87c10c323 100644 --- a/datasource/schema/number_attribute_test.go +++ b/datasource/schema/number_attribute_test.go @@ -428,3 +428,55 @@ func TestNumberAttributeNumberValidators(t *testing.T) { }) } } + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/object_attribute.go b/datasource/schema/object_attribute.go index a004329ac..21586932a 100644 --- a/datasource/schema/object_attribute.go +++ b/datasource/schema/object_attribute.go @@ -210,6 +210,18 @@ func (a ObjectAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/datasource/schema/object_attribute_test.go b/datasource/schema/object_attribute_test.go index d5c45ee64..a4b30d2fe 100644 --- a/datasource/schema/object_attribute_test.go +++ b/datasource/schema/object_attribute_test.go @@ -557,3 +557,55 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { }) } } + +func TestObjectAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestObjectAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/set_attribute.go b/datasource/schema/set_attribute.go index 859b0558d..638c21345 100644 --- a/datasource/schema/set_attribute.go +++ b/datasource/schema/set_attribute.go @@ -206,6 +206,18 @@ func (a SetAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/datasource/schema/set_attribute_test.go b/datasource/schema/set_attribute_test.go index 02fcb830c..29f2effe1 100644 --- a/datasource/schema/set_attribute_test.go +++ b/datasource/schema/set_attribute_test.go @@ -524,3 +524,55 @@ func TestSetAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/set_nested_attribute.go b/datasource/schema/set_nested_attribute.go index 26b14e449..4247933f2 100644 --- a/datasource/schema/set_nested_attribute.go +++ b/datasource/schema/set_nested_attribute.go @@ -231,6 +231,18 @@ func (a SetNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/datasource/schema/set_nested_attribute_test.go b/datasource/schema/set_nested_attribute_test.go index 1a50cac03..083eba851 100644 --- a/datasource/schema/set_nested_attribute_test.go +++ b/datasource/schema/set_nested_attribute_test.go @@ -689,3 +689,55 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/single_nested_attribute.go b/datasource/schema/single_nested_attribute.go index b6f4e7f32..838c9e333 100644 --- a/datasource/schema/single_nested_attribute.go +++ b/datasource/schema/single_nested_attribute.go @@ -245,6 +245,18 @@ func (a SingleNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/datasource/schema/single_nested_attribute_test.go b/datasource/schema/single_nested_attribute_test.go index 0a7a0b66f..73a007390 100644 --- a/datasource/schema/single_nested_attribute_test.go +++ b/datasource/schema/single_nested_attribute_test.go @@ -570,3 +570,55 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSingleNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/datasource/schema/string_attribute.go b/datasource/schema/string_attribute.go index 95534fece..9a61de9b2 100644 --- a/datasource/schema/string_attribute.go +++ b/datasource/schema/string_attribute.go @@ -187,6 +187,18 @@ func (a StringAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsOptionalForImport() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/datasource/schema/string_attribute_test.go b/datasource/schema/string_attribute_test.go index 09ea194f2..6ccf2a9bf 100644 --- a/datasource/schema/string_attribute_test.go +++ b/datasource/schema/string_attribute_test.go @@ -428,3 +428,55 @@ func TestStringAttributeStringValidators(t *testing.T) { }) } } + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/bool_attribute.go b/ephemeral/schema/bool_attribute.go index 56790dee2..f8ebd5ef7 100644 --- a/ephemeral/schema/bool_attribute.go +++ b/ephemeral/schema/bool_attribute.go @@ -190,3 +190,15 @@ func (a BoolAttribute) IsSensitive() bool { func (a BoolAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/bool_attribute_test.go b/ephemeral/schema/bool_attribute_test.go index 30cc54aa2..7a9b27f63 100644 --- a/ephemeral/schema/bool_attribute_test.go +++ b/ephemeral/schema/bool_attribute_test.go @@ -428,3 +428,55 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/dynamic_attribute.go b/ephemeral/schema/dynamic_attribute.go index 9cd22e70a..339775fe6 100644 --- a/ephemeral/schema/dynamic_attribute.go +++ b/ephemeral/schema/dynamic_attribute.go @@ -191,3 +191,15 @@ func (a DynamicAttribute) IsWriteOnly() bool { func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { return a.Validators } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/dynamic_attribute_test.go b/ephemeral/schema/dynamic_attribute_test.go index 4d01e95a0..48fc4da08 100644 --- a/ephemeral/schema/dynamic_attribute_test.go +++ b/ephemeral/schema/dynamic_attribute_test.go @@ -427,3 +427,55 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { }) } } + +func TestDynamicAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestDynamicAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/float32_attribute.go b/ephemeral/schema/float32_attribute.go index 46af5bae2..733502fef 100644 --- a/ephemeral/schema/float32_attribute.go +++ b/ephemeral/schema/float32_attribute.go @@ -193,3 +193,15 @@ func (a Float32Attribute) IsSensitive() bool { func (a Float32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/float32_attribute_test.go b/ephemeral/schema/float32_attribute_test.go index 1617d12af..90f9afe99 100644 --- a/ephemeral/schema/float32_attribute_test.go +++ b/ephemeral/schema/float32_attribute_test.go @@ -428,3 +428,55 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/float64_attribute.go b/ephemeral/schema/float64_attribute.go index f4699e210..9c72720f1 100644 --- a/ephemeral/schema/float64_attribute.go +++ b/ephemeral/schema/float64_attribute.go @@ -193,3 +193,15 @@ func (a Float64Attribute) IsSensitive() bool { func (a Float64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/float64_attribute_test.go b/ephemeral/schema/float64_attribute_test.go index 4bfb2ebd0..29b240f7c 100644 --- a/ephemeral/schema/float64_attribute_test.go +++ b/ephemeral/schema/float64_attribute_test.go @@ -428,3 +428,55 @@ func TestFloat54AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/int32_attribute.go b/ephemeral/schema/int32_attribute.go index 76f6e0194..c493b08c3 100644 --- a/ephemeral/schema/int32_attribute.go +++ b/ephemeral/schema/int32_attribute.go @@ -193,3 +193,15 @@ func (a Int32Attribute) IsSensitive() bool { func (a Int32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/int32_attribute_test.go b/ephemeral/schema/int32_attribute_test.go index b4d3cc6dc..3e88ea4d6 100644 --- a/ephemeral/schema/int32_attribute_test.go +++ b/ephemeral/schema/int32_attribute_test.go @@ -428,3 +428,55 @@ func TestInt2AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/int64_attribute.go b/ephemeral/schema/int64_attribute.go index 27cb2dd23..c356ac65b 100644 --- a/ephemeral/schema/int64_attribute.go +++ b/ephemeral/schema/int64_attribute.go @@ -193,3 +193,15 @@ func (a Int64Attribute) IsSensitive() bool { func (a Int64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/ephemeral/schema/int64_attribute_test.go b/ephemeral/schema/int64_attribute_test.go index 6b7c1849f..ef637140f 100644 --- a/ephemeral/schema/int64_attribute_test.go +++ b/ephemeral/schema/int64_attribute_test.go @@ -428,3 +428,55 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/list_attribute.go b/ephemeral/schema/list_attribute.go index 258757f99..246e77a78 100644 --- a/ephemeral/schema/list_attribute.go +++ b/ephemeral/schema/list_attribute.go @@ -206,6 +206,18 @@ func (a ListAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsOptionalForImport() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListAttribute) ListValidators() []validator.List { return a.Validators diff --git a/ephemeral/schema/list_attribute_test.go b/ephemeral/schema/list_attribute_test.go index cdbc9feb6..9ee0e5c85 100644 --- a/ephemeral/schema/list_attribute_test.go +++ b/ephemeral/schema/list_attribute_test.go @@ -523,3 +523,55 @@ func TestListAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/list_nested_attribute.go b/ephemeral/schema/list_nested_attribute.go index a4478fb48..a70b6835f 100644 --- a/ephemeral/schema/list_nested_attribute.go +++ b/ephemeral/schema/list_nested_attribute.go @@ -235,6 +235,18 @@ func (a ListNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsOptionalForImport() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/ephemeral/schema/list_nested_attribute_test.go b/ephemeral/schema/list_nested_attribute_test.go index 914af418c..daba7e113 100644 --- a/ephemeral/schema/list_nested_attribute_test.go +++ b/ephemeral/schema/list_nested_attribute_test.go @@ -688,3 +688,55 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/map_attribute.go b/ephemeral/schema/map_attribute.go index 0741747a4..86480e01f 100644 --- a/ephemeral/schema/map_attribute.go +++ b/ephemeral/schema/map_attribute.go @@ -210,6 +210,18 @@ func (a MapAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/ephemeral/schema/map_attribute_test.go b/ephemeral/schema/map_attribute_test.go index f750b5f5d..ed2348551 100644 --- a/ephemeral/schema/map_attribute_test.go +++ b/ephemeral/schema/map_attribute_test.go @@ -523,3 +523,55 @@ func TestMapAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/map_nested_attribute.go b/ephemeral/schema/map_nested_attribute.go index de057e106..6e8df3dde 100644 --- a/ephemeral/schema/map_nested_attribute.go +++ b/ephemeral/schema/map_nested_attribute.go @@ -235,6 +235,18 @@ func (a MapNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/ephemeral/schema/map_nested_attribute_test.go b/ephemeral/schema/map_nested_attribute_test.go index 37c2f1aa2..2bbaa15d5 100644 --- a/ephemeral/schema/map_nested_attribute_test.go +++ b/ephemeral/schema/map_nested_attribute_test.go @@ -688,3 +688,55 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/number_attribute.go b/ephemeral/schema/number_attribute.go index 17d557398..d56a4ddb1 100644 --- a/ephemeral/schema/number_attribute.go +++ b/ephemeral/schema/number_attribute.go @@ -190,6 +190,18 @@ func (a NumberAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsOptionalForImport() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/ephemeral/schema/number_attribute_test.go b/ephemeral/schema/number_attribute_test.go index a15142afa..2b07635a6 100644 --- a/ephemeral/schema/number_attribute_test.go +++ b/ephemeral/schema/number_attribute_test.go @@ -428,3 +428,55 @@ func TestNumberAttributeNumberValidators(t *testing.T) { }) } } + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/object_attribute.go b/ephemeral/schema/object_attribute.go index fa808e4ba..cab38b7e8 100644 --- a/ephemeral/schema/object_attribute.go +++ b/ephemeral/schema/object_attribute.go @@ -208,6 +208,18 @@ func (a ObjectAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/ephemeral/schema/object_attribute_test.go b/ephemeral/schema/object_attribute_test.go index f49e4afca..cff690c6e 100644 --- a/ephemeral/schema/object_attribute_test.go +++ b/ephemeral/schema/object_attribute_test.go @@ -557,3 +557,55 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { }) } } + +func TestObjectAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestObjectAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/set_attribute.go b/ephemeral/schema/set_attribute.go index 7ecd08ffd..963253f91 100644 --- a/ephemeral/schema/set_attribute.go +++ b/ephemeral/schema/set_attribute.go @@ -205,6 +205,18 @@ func (a SetAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/ephemeral/schema/set_attribute_test.go b/ephemeral/schema/set_attribute_test.go index d3c158fda..af56ab903 100644 --- a/ephemeral/schema/set_attribute_test.go +++ b/ephemeral/schema/set_attribute_test.go @@ -523,3 +523,55 @@ func TestSetAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/set_nested_attribute.go b/ephemeral/schema/set_nested_attribute.go index 658dc0df7..0e1dc40af 100644 --- a/ephemeral/schema/set_nested_attribute.go +++ b/ephemeral/schema/set_nested_attribute.go @@ -230,6 +230,18 @@ func (a SetNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/ephemeral/schema/set_nested_attribute_test.go b/ephemeral/schema/set_nested_attribute_test.go index 3abd59839..a049ade74 100644 --- a/ephemeral/schema/set_nested_attribute_test.go +++ b/ephemeral/schema/set_nested_attribute_test.go @@ -689,3 +689,55 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/single_nested_attribute.go b/ephemeral/schema/single_nested_attribute.go index 167b8c136..23bc8cf11 100644 --- a/ephemeral/schema/single_nested_attribute.go +++ b/ephemeral/schema/single_nested_attribute.go @@ -244,6 +244,18 @@ func (a SingleNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/ephemeral/schema/single_nested_attribute_test.go b/ephemeral/schema/single_nested_attribute_test.go index 8f4e4e783..f97ba872b 100644 --- a/ephemeral/schema/single_nested_attribute_test.go +++ b/ephemeral/schema/single_nested_attribute_test.go @@ -569,3 +569,55 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSingleNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/ephemeral/schema/string_attribute.go b/ephemeral/schema/string_attribute.go index 3cfefe97c..ec8f8692f 100644 --- a/ephemeral/schema/string_attribute.go +++ b/ephemeral/schema/string_attribute.go @@ -186,6 +186,18 @@ func (a StringAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsOptionalForImport() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/ephemeral/schema/string_attribute_test.go b/ephemeral/schema/string_attribute_test.go index 9f8532f34..95a34c24c 100644 --- a/ephemeral/schema/string_attribute_test.go +++ b/ephemeral/schema/string_attribute_test.go @@ -427,3 +427,55 @@ func TestStringAttributeStringValidators(t *testing.T) { }) } } + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/go.mod b/go.mod index 76694c5ec..3687dc3cd 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.7 require ( github.com/google/go-cmp v0.7.0 - github.com/hashicorp/terraform-plugin-go v0.26.0 + github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250314120210-d406d3409aef github.com/hashicorp/terraform-plugin-log v0.9.0 ) @@ -14,7 +14,7 @@ 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.2 // indirect + github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/terraform-registry-address v0.2.4 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect @@ -28,7 +28,7 @@ require ( golang.org/x/net v0.37.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect - google.golang.org/grpc v1.69.4 // indirect - google.golang.org/protobuf v1.36.3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.71.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect ) diff --git a/go.sum b/go.sum index 1180f5320..975600260 100644 --- a/go.sum +++ b/go.sum @@ -17,12 +17,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= -github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= 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.26.0 h1:cuIzCv4qwigug3OS7iKhpGAbZTiypAfFQmw8aE65O2M= -github.com/hashicorp/terraform-plugin-go v0.26.0/go.mod h1:+CXjuLDiFgqR+GcrM5a2E2Kal5t5q2jb0E3D57tTdNY= +github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250314120210-d406d3409aef h1:RtNtj/RAsw/ef2bpeMlCDo+HsREcQVyJ+20AzikH3kw= +github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250314120210-d406d3409aef/go.mod h1:MfDwS/KnIy2QzCwdRtuqIjZ23gpYa9Vm+Z8cFpx8qtU= 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.4 h1:JXu/zHB2Ymg/TGVCRu10XqNa4Sh2bWcqCNyKWjnCPJA= @@ -54,16 +54,18 @@ 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= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -76,12 +78,12 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/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= diff --git a/internal/fromproto5/getresourceidentityschemas.go b/internal/fromproto5/getresourceidentityschemas.go new file mode 100644 index 000000000..a5ca3b16c --- /dev/null +++ b/internal/fromproto5/getresourceidentityschemas.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// GetResourceIdentitySchemasRequest returns the *fwserver.GetResourceIdentitySchemasRequest +// equivalent of a *tfprotov5.GetResourceIdentitySchemasRequest. +func GetResourceIdentitySchemasRequest(ctx context.Context, proto5 *tfprotov5.GetResourceIdentitySchemasRequest) *fwserver.GetResourceIdentitySchemasRequest { + if proto5 == nil { + return nil + } + + fw := &fwserver.GetResourceIdentitySchemasRequest{} + + return fw +} diff --git a/internal/fromproto5/getresourceidentityschemas_test.go b/internal/fromproto5/getresourceidentityschemas_test.go new file mode 100644 index 000000000..e3143c24d --- /dev/null +++ b/internal/fromproto5/getresourceidentityschemas_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +func TestGetResourceIdentitySchemasRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input *tfprotov5.GetResourceIdentitySchemasRequest + expected *fwserver.GetResourceIdentitySchemasRequest + }{ + "nil": { + input: nil, + expected: nil, + }, + "empty": { + input: &tfprotov5.GetResourceIdentitySchemasRequest{}, + expected: &fwserver.GetResourceIdentitySchemasRequest{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := fromproto5.GetResourceIdentitySchemasRequest(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/fromproto6/getresourceidentityschemas.go b/internal/fromproto6/getresourceidentityschemas.go new file mode 100644 index 000000000..448c5b56a --- /dev/null +++ b/internal/fromproto6/getresourceidentityschemas.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// GetResourceIdentitySchemasRequest returns the *fwserver.GetResourceIdentitySchemasRequest +// equivalent of a *tfprotov6.GetResourceIdentitySchemasRequest. +func GetResourceIdentitySchemasRequest(ctx context.Context, proto6 *tfprotov6.GetResourceIdentitySchemasRequest) *fwserver.GetResourceIdentitySchemasRequest { + if proto6 == nil { + return nil + } + + fw := &fwserver.GetResourceIdentitySchemasRequest{} + + return fw +} diff --git a/internal/fromproto6/getresourceidentityschemas_test.go b/internal/fromproto6/getresourceidentityschemas_test.go new file mode 100644 index 000000000..22ab51200 --- /dev/null +++ b/internal/fromproto6/getresourceidentityschemas_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +func TestGetResourceIdentitySchemasRequest(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input *tfprotov6.GetResourceIdentitySchemasRequest + expected *fwserver.GetResourceIdentitySchemasRequest + }{ + "nil": { + input: nil, + expected: nil, + }, + "empty": { + input: &tfprotov6.GetResourceIdentitySchemasRequest{}, + expected: &fwserver.GetResourceIdentitySchemasRequest{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := fromproto6.GetResourceIdentitySchemasRequest(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/fwschema/attribute.go b/internal/fwschema/attribute.go index 6c440b319..b9fe31129 100644 --- a/internal/fwschema/attribute.go +++ b/internal/fwschema/attribute.go @@ -71,6 +71,16 @@ type Attribute interface { // // Write-only attributes are a managed-resource schema concept only. IsWriteOnly() bool + + // IsOptionalForImport should return true if the identity attribute is optional to be set by + // the practitioner when importing by identity. This is named differently than OptionalForImport + // to prevent a conflict with the relevant field name. + IsOptionalForImport() bool + + // IsRequiredForImport should return true if the identity attribute must be set by + // the practitioner when importing by identity. This is named differently than RequiredForImport + // to prevent a conflict with the relevant field name. + IsRequiredForImport() bool } // AttributesEqual is a helper function to perform equality testing on two @@ -113,5 +123,13 @@ func AttributesEqual(a, b Attribute) bool { return false } + if a.IsOptionalForImport() != b.IsOptionalForImport() { + return false + } + + if a.IsRequiredForImport() != b.IsRequiredForImport() { + return false + } + return true } diff --git a/internal/fwserver/server.go b/internal/fwserver/server.go index b6f2bc85e..22723f9ff 100644 --- a/internal/fwserver/server.go +++ b/internal/fwserver/server.go @@ -689,3 +689,48 @@ func (s *Server) ResourceSchemas(ctx context.Context) (map[string]fwschema.Schem return resourceSchemas, diags } + +// ResourceIdentitySchemas returns a map of Resource Identity Schemas for the +// GetResourceIdentitySchemas RPC without caching since not all schemas are guaranteed to +// be necessary for later provider operations. The schema implementations are +// also validated. +func (s *Server) ResourceIdentitySchemas(ctx context.Context) (map[string]fwschema.Schema, diag.Diagnostics) { + resourceIdentitySchemas := make(map[string]fwschema.Schema) + + resourceFuncs, diags := s.ResourceFuncs(ctx) + + for typeName, resourceFunc := range resourceFuncs { + r := resourceFunc() + + rWithIdentity, ok := r.(resource.ResourceWithIdentity) + if !ok { + // Resource identity support is optional, so we can skip resources that don't implement it. + continue + } + + identitySchemaReq := resource.IdentitySchemaRequest{} + identitySchemaResp := resource.IdentitySchemaResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined Resource IdentitySchema method", map[string]interface{}{logging.KeyResourceType: typeName}) + rWithIdentity.IdentitySchema(ctx, identitySchemaReq, &identitySchemaResp) + logging.FrameworkTrace(ctx, "Called provider defined Resource IdentitySchema method", map[string]interface{}{logging.KeyResourceType: typeName}) + + diags.Append(identitySchemaResp.Diagnostics...) + + if identitySchemaResp.Diagnostics.HasError() { + continue + } + + validateDiags := identitySchemaResp.IdentitySchema.ValidateImplementation(ctx) + + diags.Append(validateDiags...) + + if validateDiags.HasError() { + continue + } + + resourceIdentitySchemas[typeName] = identitySchemaResp.IdentitySchema + } + + return resourceIdentitySchemas, diags +} diff --git a/internal/fwserver/server_getresourceidentityschemas.go b/internal/fwserver/server_getresourceidentityschemas.go new file mode 100644 index 000000000..511c6bb05 --- /dev/null +++ b/internal/fwserver/server_getresourceidentityschemas.go @@ -0,0 +1,34 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// GetResourceIdentitySchemasRequest is the framework server request for the +// GetResourceIdentitySchemas RPC. +type GetResourceIdentitySchemasRequest struct{} + +// GetResourceIdentitySchemasResponse is the framework server response for the +// GetResourceIdentitySchemas RPC. +type GetResourceIdentitySchemasResponse struct { + IdentitySchemas map[string]fwschema.Schema + Diagnostics diag.Diagnostics +} + +// GetResourceIdentitySchemas implements the framework server GetResourceIdentitySchemas RPC. +func (s *Server) GetResourceIdentitySchemas(ctx context.Context, req *GetResourceIdentitySchemasRequest, resp *GetResourceIdentitySchemasResponse) { + identitySchemas, diags := s.ResourceIdentitySchemas(ctx) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + resp.IdentitySchemas = identitySchemas +} diff --git a/internal/fwserver/server_getresourceidentityschemas_test.go b/internal/fwserver/server_getresourceidentityschemas_test.go new file mode 100644 index 000000000..c780a7ff1 --- /dev/null +++ b/internal/fwserver/server_getresourceidentityschemas_test.go @@ -0,0 +1,229 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fwserver_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" +) + +func TestServerGetResourceIdentitySchemas(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + server *fwserver.Server + request *fwserver.GetResourceIdentitySchemasRequest + expectedResponse *fwserver.GetResourceIdentitySchemasResponse + }{ + "empty-provider": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + expectedResponse: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{}, + }, + }, + "resource-no-identity-schemas": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource1" + }, + } + }, + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource2" + }, + } + }, + } + }, + }, + }, + request: &fwserver.GetResourceIdentitySchemasRequest{}, + expectedResponse: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{}, + }, + }, + "resource-identity-schemas": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource1" + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource2" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test2": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource3" + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource4" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test4": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + } + }, + }, + }, + request: &fwserver.GetResourceIdentitySchemasRequest{}, + expectedResponse: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource2": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test2": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + "test_resource4": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test4": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + }, + "resource-identity-schemas-invalid-attribute-name": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource1" + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource2" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "$": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + func() resource.Resource { + return &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource3" + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource4" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test4": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + } + }, + }, + }, + request: &fwserver.GetResourceIdentitySchemasRequest{}, + expectedResponse: &fwserver.GetResourceIdentitySchemasResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute/Block Name", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"$\" at schema path \"$\" is an invalid attribute/block name. "+ + "Names must only contain lowercase alphanumeric characters (a-z, 0-9) and underscores (_).", + ), + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + response := &fwserver.GetResourceIdentitySchemasResponse{} + testCase.server.GetResourceIdentitySchemas(context.Background(), testCase.request, response) + + if diff := cmp.Diff(response, testCase.expectedResponse); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/proto5server/server_getresourceidentityschemas.go b/internal/proto5server/server_getresourceidentityschemas.go new file mode 100644 index 000000000..97ecb47ea --- /dev/null +++ b/internal/proto5server/server_getresourceidentityschemas.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// GetResourceIdentitySchemas satisfies the tfprotov5.ProviderServer interface. +func (s *Server) GetResourceIdentitySchemas(ctx context.Context, proto5Req *tfprotov5.GetResourceIdentitySchemasRequest) (*tfprotov5.GetResourceIdentitySchemasResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwReq := fromproto5.GetResourceIdentitySchemasRequest(ctx, proto5Req) + fwResp := &fwserver.GetResourceIdentitySchemasResponse{} + + s.FrameworkServer.GetResourceIdentitySchemas(ctx, fwReq, fwResp) + + return toproto5.GetResourceIdentitySchemasResponse(ctx, fwResp), nil +} diff --git a/internal/proto5server/server_getresourceidentityschemas_test.go b/internal/proto5server/server_getresourceidentityschemas_test.go new file mode 100644 index 000000000..e054e2e9b --- /dev/null +++ b/internal/proto5server/server_getresourceidentityschemas_test.go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "bytes" + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-log/tfsdklogtest" +) + +func TestServerGetResourceIdentitySchemas(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + server *Server + request *tfprotov5.GetResourceIdentitySchemasRequest + expectedError error + expectedResponse *tfprotov5.GetResourceIdentitySchemasResponse + }{ + "resource-identity-schemas": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource1" + }, + }, + IdentitySchemaMethod: func(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test1": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource2" + }, + }, + IdentitySchemaMethod: func(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test2": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.GetResourceIdentitySchemasRequest{}, + expectedResponse: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource1": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test1", + RequiredForImport: true, + Type: tftypes.String, + }, + }, + }, + "test_resource2": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test2", + RequiredForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.server.GetResourceIdentitySchemas(context.Background(), new(tfprotov5.GetResourceIdentitySchemasRequest)) + + if diff := cmp.Diff(testCase.expectedError, err); diff != "" { + t.Errorf("unexpected error difference: %s", diff) + } + + if diff := cmp.Diff(testCase.expectedResponse, got); diff != "" { + t.Errorf("unexpected response difference: %s", diff) + } + }) + } +} + +func TestServerGetResourceIdentitySchemas_logging(t *testing.T) { + t.Parallel() + + var output bytes.Buffer + + ctx := tfsdklogtest.RootLogger(context.Background(), &output) + ctx = logging.InitContext(ctx) + + testServer := &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(ctx context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "examplecloud_thing" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{} + }, + } + }, + } + }, + }, + }, + } + + _, err := testServer.GetResourceIdentitySchemas(ctx, new(tfprotov5.GetResourceIdentitySchemasRequest)) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + entries, err := tfsdklogtest.MultilineJSONDecode(&output) + + if err != nil { + t.Fatalf("unable to read multiple line JSON: %s", err) + } + + expectedEntries := []map[string]interface{}{ + { + "@level": "trace", + "@message": "Checking ResourceTypes lock", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Checking ProviderTypeName lock", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Calling provider defined Provider Metadata", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Called provider defined Provider Metadata", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Calling provider defined Provider Resources", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Called provider defined Provider Resources", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Found resource type", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + { + "@level": "trace", + "@message": "Calling provider defined Resource IdentitySchema method", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + { + "@level": "trace", + "@message": "Called provider defined Resource IdentitySchema method", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + } + + if diff := cmp.Diff(entries, expectedEntries); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} diff --git a/internal/proto5server/server_upgraderesourceidentity.go b/internal/proto5server/server_upgraderesourceidentity.go new file mode 100644 index 000000000..f5b1979f1 --- /dev/null +++ b/internal/proto5server/server_upgraderesourceidentity.go @@ -0,0 +1,15 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto5server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// UpgradeResourceIdentity satisfies the tfprotov5.ProviderServer interface. +func (s *Server) UpgradeResourceIdentity(ctx context.Context, proto5Req *tfprotov5.UpgradeResourceIdentityRequest) (*tfprotov5.UpgradeResourceIdentityResponse, error) { + panic("unimplemented") // TODO:ResourceIdentity: implement +} diff --git a/internal/proto6server/server_getresourceidentityschemas.go b/internal/proto6server/server_getresourceidentityschemas.go new file mode 100644 index 000000000..5258d024c --- /dev/null +++ b/internal/proto6server/server_getresourceidentityschemas.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// GetResourceIdentitySchemas satisfies the tfprotov6.ProviderServer interface. +func (s *Server) GetResourceIdentitySchemas(ctx context.Context, proto6Req *tfprotov6.GetResourceIdentitySchemasRequest) (*tfprotov6.GetResourceIdentitySchemasResponse, error) { + ctx = s.registerContext(ctx) + ctx = logging.InitContext(ctx) + + fwReq := fromproto6.GetResourceIdentitySchemasRequest(ctx, proto6Req) + fwResp := &fwserver.GetResourceIdentitySchemasResponse{} + + s.FrameworkServer.GetResourceIdentitySchemas(ctx, fwReq, fwResp) + + return toproto6.GetResourceIdentitySchemasResponse(ctx, fwResp), nil +} diff --git a/internal/proto6server/server_getresourceidentityschemas_test.go b/internal/proto6server/server_getresourceidentityschemas_test.go new file mode 100644 index 000000000..d5d7ddfca --- /dev/null +++ b/internal/proto6server/server_getresourceidentityschemas_test.go @@ -0,0 +1,218 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "bytes" + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/logging" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-log/tfsdklogtest" +) + +func TestServerGetResourceIdentitySchemas(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + server *Server + request *tfprotov6.GetResourceIdentitySchemasRequest + expectedError error + expectedResponse *tfprotov6.GetResourceIdentitySchemasResponse + }{ + "resource-identity-schemas": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource1" + }, + }, + IdentitySchemaMethod: func(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test1": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource2" + }, + }, + IdentitySchemaMethod: func(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test2": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + } + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.GetResourceIdentitySchemasRequest{}, + expectedResponse: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource1": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test1", + RequiredForImport: true, + Type: tftypes.String, + }, + }, + }, + "test_resource2": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test2", + RequiredForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.server.GetResourceIdentitySchemas(context.Background(), new(tfprotov6.GetResourceIdentitySchemasRequest)) + + if diff := cmp.Diff(testCase.expectedError, err); diff != "" { + t.Errorf("unexpected error difference: %s", diff) + } + + if diff := cmp.Diff(testCase.expectedResponse, got); diff != "" { + t.Errorf("unexpected response difference: %s", diff) + } + }) + } +} + +func TestServerGetResourceIdentitySchemas_logging(t *testing.T) { + t.Parallel() + + var output bytes.Buffer + + ctx := tfsdklogtest.RootLogger(context.Background(), &output) + ctx = logging.InitContext(ctx) + + testServer := &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(ctx context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + MetadataMethod: func(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "examplecloud_thing" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = identityschema.Schema{} + }, + } + }, + } + }, + }, + }, + } + + _, err := testServer.GetResourceIdentitySchemas(ctx, new(tfprotov6.GetResourceIdentitySchemasRequest)) + + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + entries, err := tfsdklogtest.MultilineJSONDecode(&output) + + if err != nil { + t.Fatalf("unable to read multiple line JSON: %s", err) + } + + expectedEntries := []map[string]interface{}{ + { + "@level": "trace", + "@message": "Checking ResourceTypes lock", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Checking ProviderTypeName lock", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Calling provider defined Provider Metadata", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Called provider defined Provider Metadata", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Calling provider defined Provider Resources", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Called provider defined Provider Resources", + "@module": "sdk.framework", + }, + { + "@level": "trace", + "@message": "Found resource type", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + { + "@level": "trace", + "@message": "Calling provider defined Resource IdentitySchema method", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + { + "@level": "trace", + "@message": "Called provider defined Resource IdentitySchema method", + "@module": "sdk.framework", + "tf_resource_type": "examplecloud_thing", + }, + } + + if diff := cmp.Diff(entries, expectedEntries); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} diff --git a/internal/proto6server/server_upgraderesourceidentity.go b/internal/proto6server/server_upgraderesourceidentity.go new file mode 100644 index 000000000..042efce8e --- /dev/null +++ b/internal/proto6server/server_upgraderesourceidentity.go @@ -0,0 +1,15 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package proto6server + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// UpgradeResourceIdentity satisfies the tfprotov6.ProviderServer interface. +func (s *Server) UpgradeResourceIdentity(ctx context.Context, proto6Req *tfprotov6.UpgradeResourceIdentityRequest) (*tfprotov6.UpgradeResourceIdentityResponse, error) { + panic("unimplemented") // TODO:ResourceIdentity: implement +} diff --git a/internal/testing/testprovider/resourcewithidentity.go b/internal/testing/testprovider/resourcewithidentity.go new file mode 100644 index 000000000..514b979d0 --- /dev/null +++ b/internal/testing/testprovider/resourcewithidentity.go @@ -0,0 +1,30 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testprovider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.Resource = &ResourceWithIdentity{} +var _ resource.ResourceWithIdentity = &ResourceWithIdentity{} + +// Declarative resource.ResourceWithIdentity for unit testing. +type ResourceWithIdentity struct { + *Resource + + // ResourceWithIdentity interface methods + IdentitySchemaMethod func(context.Context, resource.IdentitySchemaRequest, *resource.IdentitySchemaResponse) +} + +// IdentitySchema implements resource.ResourceWithIdentity. +func (p *ResourceWithIdentity) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + if p.IdentitySchemaMethod == nil { + return + } + + p.IdentitySchemaMethod(ctx, req, resp) +} diff --git a/internal/testing/testschema/attribute.go b/internal/testing/testschema/attribute.go index 979db68eb..3f4e7470a 100644 --- a/internal/testing/testschema/attribute.go +++ b/internal/testing/testschema/attribute.go @@ -21,6 +21,8 @@ type Attribute struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -84,3 +86,13 @@ func (a Attribute) IsSensitive() bool { func (a Attribute) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a Attribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a Attribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithbooldefault.go b/internal/testing/testschema/attributewithbooldefault.go index 66edc07e5..e1ca0627a 100644 --- a/internal/testing/testschema/attributewithbooldefault.go +++ b/internal/testing/testschema/attributewithbooldefault.go @@ -23,6 +23,8 @@ type AttributeWithBoolDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Bool } @@ -91,3 +93,13 @@ func (a AttributeWithBoolDefaultValue) IsSensitive() bool { func (a AttributeWithBoolDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithboolplanmodifiers.go b/internal/testing/testschema/attributewithboolplanmodifiers.go index 397f18186..812697fb2 100644 --- a/internal/testing/testschema/attributewithboolplanmodifiers.go +++ b/internal/testing/testschema/attributewithboolplanmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithBoolPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Bool } @@ -92,3 +94,13 @@ func (a AttributeWithBoolPlanModifiers) IsSensitive() bool { func (a AttributeWithBoolPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithboolvalidators.go b/internal/testing/testschema/attributewithboolvalidators.go index 044c25cf4..5da143524 100644 --- a/internal/testing/testschema/attributewithboolvalidators.go +++ b/internal/testing/testschema/attributewithboolvalidators.go @@ -24,6 +24,8 @@ type AttributeWithBoolValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Bool } @@ -92,3 +94,13 @@ func (a AttributeWithBoolValidators) IsSensitive() bool { func (a AttributeWithBoolValidators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithBoolValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithdynamicdefault.go b/internal/testing/testschema/attributewithdynamicdefault.go index b562132a8..ae3690aa0 100644 --- a/internal/testing/testschema/attributewithdynamicdefault.go +++ b/internal/testing/testschema/attributewithdynamicdefault.go @@ -23,6 +23,8 @@ type AttributeWithDynamicDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Dynamic } @@ -91,3 +93,13 @@ func (a AttributeWithDynamicDefaultValue) IsSensitive() bool { func (a AttributeWithDynamicDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithdynamicplanmodifiers.go b/internal/testing/testschema/attributewithdynamicplanmodifiers.go index 74a80587a..f510ba12c 100644 --- a/internal/testing/testschema/attributewithdynamicplanmodifiers.go +++ b/internal/testing/testschema/attributewithdynamicplanmodifiers.go @@ -23,6 +23,8 @@ type AttributeWithDynamicPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Dynamic } @@ -91,3 +93,13 @@ func (a AttributeWithDynamicPlanModifiers) DynamicPlanModifiers() []planmodifier func (a AttributeWithDynamicPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithdynamicvalidators.go b/internal/testing/testschema/attributewithdynamicvalidators.go index e4ef1024e..cca5e5fc6 100644 --- a/internal/testing/testschema/attributewithdynamicvalidators.go +++ b/internal/testing/testschema/attributewithdynamicvalidators.go @@ -24,6 +24,8 @@ type AttributeWithDynamicValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Dynamic } @@ -92,3 +94,13 @@ func (a AttributeWithDynamicValidators) DynamicValidators() []validator.Dynamic func (a AttributeWithDynamicValidators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithDynamicValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat32default.go b/internal/testing/testschema/attributewithfloat32default.go index c3aeb627d..6798180f1 100644 --- a/internal/testing/testschema/attributewithfloat32default.go +++ b/internal/testing/testschema/attributewithfloat32default.go @@ -23,6 +23,8 @@ type AttributeWithFloat32DefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Float32 } @@ -91,3 +93,13 @@ func (a AttributeWithFloat32DefaultValue) IsSensitive() bool { func (a AttributeWithFloat32DefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32DefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32DefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat32planmodifiers.go b/internal/testing/testschema/attributewithfloat32planmodifiers.go index f93d87229..d01c9ca8d 100644 --- a/internal/testing/testschema/attributewithfloat32planmodifiers.go +++ b/internal/testing/testschema/attributewithfloat32planmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithFloat32PlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Float32 } @@ -92,3 +94,13 @@ func (a AttributeWithFloat32PlanModifiers) IsSensitive() bool { func (a AttributeWithFloat32PlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32PlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32PlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat32validators.go b/internal/testing/testschema/attributewithfloat32validators.go index 7fb02a5ad..fbf460685 100644 --- a/internal/testing/testschema/attributewithfloat32validators.go +++ b/internal/testing/testschema/attributewithfloat32validators.go @@ -24,6 +24,8 @@ type AttributeWithFloat32Validators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Float32 } @@ -92,3 +94,13 @@ func (a AttributeWithFloat32Validators) IsSensitive() bool { func (a AttributeWithFloat32Validators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32Validators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat32Validators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat64default.go b/internal/testing/testschema/attributewithfloat64default.go index 484ec37d9..9a9cca0a6 100644 --- a/internal/testing/testschema/attributewithfloat64default.go +++ b/internal/testing/testschema/attributewithfloat64default.go @@ -23,6 +23,8 @@ type AttributeWithFloat64DefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Float64 } @@ -91,3 +93,13 @@ func (a AttributeWithFloat64DefaultValue) IsSensitive() bool { func (a AttributeWithFloat64DefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64DefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64DefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat64planmodifiers.go b/internal/testing/testschema/attributewithfloat64planmodifiers.go index ba04291da..fe9a0077e 100644 --- a/internal/testing/testschema/attributewithfloat64planmodifiers.go +++ b/internal/testing/testschema/attributewithfloat64planmodifiers.go @@ -23,6 +23,8 @@ type AttributeWithFloat64PlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Float64 } @@ -91,3 +93,13 @@ func (a AttributeWithFloat64PlanModifiers) IsSensitive() bool { func (a AttributeWithFloat64PlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64PlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64PlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithfloat64validators.go b/internal/testing/testschema/attributewithfloat64validators.go index 02ef17704..309dbd1bc 100644 --- a/internal/testing/testschema/attributewithfloat64validators.go +++ b/internal/testing/testschema/attributewithfloat64validators.go @@ -23,6 +23,8 @@ type AttributeWithFloat64Validators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Float64 } @@ -91,3 +93,13 @@ func (a AttributeWithFloat64Validators) IsSensitive() bool { func (a AttributeWithFloat64Validators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64Validators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithFloat64Validators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint32default.go b/internal/testing/testschema/attributewithint32default.go index f332bae41..e9d6a8e9b 100644 --- a/internal/testing/testschema/attributewithint32default.go +++ b/internal/testing/testschema/attributewithint32default.go @@ -23,6 +23,8 @@ type AttributeWithInt32DefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Int32 } @@ -91,3 +93,13 @@ func (a AttributeWithInt32DefaultValue) IsSensitive() bool { func (a AttributeWithInt32DefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32DefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32DefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint32planmodifiers.go b/internal/testing/testschema/attributewithint32planmodifiers.go index 7f131df58..4cef7d2cd 100644 --- a/internal/testing/testschema/attributewithint32planmodifiers.go +++ b/internal/testing/testschema/attributewithint32planmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithInt32PlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Int32 } @@ -92,3 +94,13 @@ func (a AttributeWithInt32PlanModifiers) IsSensitive() bool { func (a AttributeWithInt32PlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32PlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32PlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint32validators.go b/internal/testing/testschema/attributewithint32validators.go index 8a4546e9e..62366de90 100644 --- a/internal/testing/testschema/attributewithint32validators.go +++ b/internal/testing/testschema/attributewithint32validators.go @@ -24,6 +24,8 @@ type AttributeWithInt32Validators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Int32 } @@ -92,3 +94,13 @@ func (a AttributeWithInt32Validators) IsSensitive() bool { func (a AttributeWithInt32Validators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32Validators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt32Validators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint64default.go b/internal/testing/testschema/attributewithint64default.go index 574a88a58..bb3f12f00 100644 --- a/internal/testing/testschema/attributewithint64default.go +++ b/internal/testing/testschema/attributewithint64default.go @@ -23,6 +23,8 @@ type AttributeWithInt64DefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Int64 } @@ -91,3 +93,13 @@ func (a AttributeWithInt64DefaultValue) IsSensitive() bool { func (a AttributeWithInt64DefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64DefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64DefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint64planmodifiers.go b/internal/testing/testschema/attributewithint64planmodifiers.go index e21a43251..ef315b3ae 100644 --- a/internal/testing/testschema/attributewithint64planmodifiers.go +++ b/internal/testing/testschema/attributewithint64planmodifiers.go @@ -23,6 +23,8 @@ type AttributeWithInt64PlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Int64 } @@ -91,3 +93,13 @@ func (a AttributeWithInt64PlanModifiers) IsSensitive() bool { func (a AttributeWithInt64PlanModifiers) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64PlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64PlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithint64validators.go b/internal/testing/testschema/attributewithint64validators.go index c4e23e166..5ccb40aa7 100644 --- a/internal/testing/testschema/attributewithint64validators.go +++ b/internal/testing/testschema/attributewithint64validators.go @@ -23,6 +23,8 @@ type AttributeWithInt64Validators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Int64 } @@ -91,3 +93,13 @@ func (a AttributeWithInt64Validators) IsSensitive() bool { func (a AttributeWithInt64Validators) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64Validators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithInt64Validators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithlistdefault.go b/internal/testing/testschema/attributewithlistdefault.go index ff23c5215..3676ca7ba 100644 --- a/internal/testing/testschema/attributewithlistdefault.go +++ b/internal/testing/testschema/attributewithlistdefault.go @@ -24,6 +24,8 @@ type AttributeWithListDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.List } @@ -94,3 +96,13 @@ func (a AttributeWithListDefaultValue) IsSensitive() bool { func (a AttributeWithListDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithlistplanmodifiers.go b/internal/testing/testschema/attributewithlistplanmodifiers.go index fbb50c332..eafa1093b 100644 --- a/internal/testing/testschema/attributewithlistplanmodifiers.go +++ b/internal/testing/testschema/attributewithlistplanmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithListPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.List } @@ -94,3 +96,13 @@ func (a AttributeWithListPlanModifiers) IsWriteOnly() bool { func (a AttributeWithListPlanModifiers) ListPlanModifiers() []planmodifier.List { return a.PlanModifiers } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithlistvalidators.go b/internal/testing/testschema/attributewithlistvalidators.go index fefa2eb02..36182c4a0 100644 --- a/internal/testing/testschema/attributewithlistvalidators.go +++ b/internal/testing/testschema/attributewithlistvalidators.go @@ -24,6 +24,8 @@ type AttributeWithListValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.List } @@ -94,3 +96,13 @@ func (a AttributeWithListValidators) IsWriteOnly() bool { func (a AttributeWithListValidators) ListValidators() []validator.List { return a.Validators } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithListValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithmapdefault.go b/internal/testing/testschema/attributewithmapdefault.go index 2b223cd1d..0ca52972c 100644 --- a/internal/testing/testschema/attributewithmapdefault.go +++ b/internal/testing/testschema/attributewithmapdefault.go @@ -24,6 +24,8 @@ type AttributeWithMapDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Map } @@ -94,3 +96,13 @@ func (a AttributeWithMapDefaultValue) IsSensitive() bool { func (a AttributeWithMapDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithmapplanmodifiers.go b/internal/testing/testschema/attributewithmapplanmodifiers.go index 06c7b2fe4..b57d597ce 100644 --- a/internal/testing/testschema/attributewithmapplanmodifiers.go +++ b/internal/testing/testschema/attributewithmapplanmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithMapPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Map } @@ -94,3 +96,13 @@ func (a AttributeWithMapPlanModifiers) IsWriteOnly() bool { func (a AttributeWithMapPlanModifiers) MapPlanModifiers() []planmodifier.Map { return a.PlanModifiers } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithmapvalidators.go b/internal/testing/testschema/attributewithmapvalidators.go index 5e3a9dac8..8b2ec6b14 100644 --- a/internal/testing/testschema/attributewithmapvalidators.go +++ b/internal/testing/testschema/attributewithmapvalidators.go @@ -24,6 +24,8 @@ type AttributeWithMapValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Map } @@ -90,6 +92,16 @@ func (a AttributeWithMapValidators) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithMapValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} + // MapValidators satisfies the fwxschema.AttributeWithMapValidators interface. func (a AttributeWithMapValidators) MapValidators() []validator.Map { return a.Validators diff --git a/internal/testing/testschema/attributewithnumberdefault.go b/internal/testing/testschema/attributewithnumberdefault.go index effa79507..bdb16149d 100644 --- a/internal/testing/testschema/attributewithnumberdefault.go +++ b/internal/testing/testschema/attributewithnumberdefault.go @@ -23,6 +23,8 @@ type AttributeWithNumberDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Number } @@ -91,3 +93,13 @@ func (a AttributeWithNumberDefaultValue) IsSensitive() bool { func (a AttributeWithNumberDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithnumberplanmodifiers.go b/internal/testing/testschema/attributewithnumberplanmodifiers.go index cf9299778..db6bf3d46 100644 --- a/internal/testing/testschema/attributewithnumberplanmodifiers.go +++ b/internal/testing/testschema/attributewithnumberplanmodifiers.go @@ -23,6 +23,8 @@ type AttributeWithNumberPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Number } @@ -87,6 +89,16 @@ func (a AttributeWithNumberPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // NumberPlanModifiers satisfies the fwxschema.AttributeWithNumberPlanModifiers interface. func (a AttributeWithNumberPlanModifiers) NumberPlanModifiers() []planmodifier.Number { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithnumbervalidators.go b/internal/testing/testschema/attributewithnumbervalidators.go index af1869d38..d5ba9bc7c 100644 --- a/internal/testing/testschema/attributewithnumbervalidators.go +++ b/internal/testing/testschema/attributewithnumbervalidators.go @@ -23,6 +23,8 @@ type AttributeWithNumberValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Number } @@ -87,6 +89,16 @@ func (a AttributeWithNumberValidators) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithNumberValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} + // NumberValidators satisfies the fwxschema.AttributeWithNumberValidators interface. func (a AttributeWithNumberValidators) NumberValidators() []validator.Number { return a.Validators diff --git a/internal/testing/testschema/attributewithobjectdefault.go b/internal/testing/testschema/attributewithobjectdefault.go index ed25e0a90..c8ae9e047 100644 --- a/internal/testing/testschema/attributewithobjectdefault.go +++ b/internal/testing/testschema/attributewithobjectdefault.go @@ -24,6 +24,8 @@ type AttributeWithObjectDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Object } @@ -94,3 +96,13 @@ func (a AttributeWithObjectDefaultValue) IsSensitive() bool { func (a AttributeWithObjectDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithobjectplanmodifiers.go b/internal/testing/testschema/attributewithobjectplanmodifiers.go index e4ec023bd..625746b47 100644 --- a/internal/testing/testschema/attributewithobjectplanmodifiers.go +++ b/internal/testing/testschema/attributewithobjectplanmodifiers.go @@ -24,6 +24,8 @@ type AttributeWithObjectPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Object } @@ -90,6 +92,16 @@ func (a AttributeWithObjectPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ObjectPlanModifiers satisfies the fwxschema.AttributeWithObjectPlanModifiers interface. func (a AttributeWithObjectPlanModifiers) ObjectPlanModifiers() []planmodifier.Object { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithobjectvalidators.go b/internal/testing/testschema/attributewithobjectvalidators.go index 534c47cf6..03bab115f 100644 --- a/internal/testing/testschema/attributewithobjectvalidators.go +++ b/internal/testing/testschema/attributewithobjectvalidators.go @@ -24,6 +24,8 @@ type AttributeWithObjectValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Object } @@ -90,6 +92,16 @@ func (a AttributeWithObjectValidators) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithObjectValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ObjectValidators satisfies the fwxschema.AttributeWithObjectValidators interface. func (a AttributeWithObjectValidators) ObjectValidators() []validator.Object { return a.Validators diff --git a/internal/testing/testschema/attributewithsetdefault.go b/internal/testing/testschema/attributewithsetdefault.go index f770f956c..d5d557cec 100644 --- a/internal/testing/testschema/attributewithsetdefault.go +++ b/internal/testing/testschema/attributewithsetdefault.go @@ -24,6 +24,8 @@ type AttributeWithSetDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.Set } @@ -94,3 +96,13 @@ func (a AttributeWithSetDefaultValue) IsSensitive() bool { func (a AttributeWithSetDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithsetplanmodifiers.go b/internal/testing/testschema/attributewithsetplanmodifiers.go index a36f5adc4..685709121 100644 --- a/internal/testing/testschema/attributewithsetplanmodifiers.go +++ b/internal/testing/testschema/attributewithsetplanmodifiers.go @@ -25,6 +25,8 @@ type AttributeWithSetPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.Set } @@ -91,6 +93,16 @@ func (a AttributeWithSetPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // SetPlanModifiers satisfies the fwxschema.AttributeWithSetPlanModifiers interface. func (a AttributeWithSetPlanModifiers) SetPlanModifiers() []planmodifier.Set { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithsetvalidators.go b/internal/testing/testschema/attributewithsetvalidators.go index 32bc5256f..ed18808cc 100644 --- a/internal/testing/testschema/attributewithsetvalidators.go +++ b/internal/testing/testschema/attributewithsetvalidators.go @@ -24,6 +24,8 @@ type AttributeWithSetValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.Set } @@ -90,6 +92,16 @@ func (a AttributeWithSetValidators) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithSetValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} + // SetValidators satisfies the fwxschema.AttributeWithSetValidators interface. func (a AttributeWithSetValidators) SetValidators() []validator.Set { return a.Validators diff --git a/internal/testing/testschema/attributewithstringdefault.go b/internal/testing/testschema/attributewithstringdefault.go index 01e1e6f4e..28131c39a 100644 --- a/internal/testing/testschema/attributewithstringdefault.go +++ b/internal/testing/testschema/attributewithstringdefault.go @@ -23,6 +23,8 @@ type AttributeWithStringDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Default defaults.String } @@ -91,3 +93,13 @@ func (a AttributeWithStringDefaultValue) IsSensitive() bool { func (a AttributeWithStringDefaultValue) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/attributewithstringplanmodifiers.go b/internal/testing/testschema/attributewithstringplanmodifiers.go index d1b5af8d5..f88a93cf7 100644 --- a/internal/testing/testschema/attributewithstringplanmodifiers.go +++ b/internal/testing/testschema/attributewithstringplanmodifiers.go @@ -23,6 +23,8 @@ type AttributeWithStringPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool PlanModifiers []planmodifier.String } @@ -87,6 +89,16 @@ func (a AttributeWithStringPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // StringPlanModifiers satisfies the fwxschema.AttributeWithStringPlanModifiers interface. func (a AttributeWithStringPlanModifiers) StringPlanModifiers() []planmodifier.String { return a.PlanModifiers diff --git a/internal/testing/testschema/attributewithstringvalidators.go b/internal/testing/testschema/attributewithstringvalidators.go index e1ddf7e59..21efefe3d 100644 --- a/internal/testing/testschema/attributewithstringvalidators.go +++ b/internal/testing/testschema/attributewithstringvalidators.go @@ -23,6 +23,8 @@ type AttributeWithStringValidators struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Validators []validator.String } @@ -87,6 +89,16 @@ func (a AttributeWithStringValidators) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringValidators) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a AttributeWithStringValidators) IsOptionalForImport() bool { + return a.OptionalForImport +} + // StringValidators satisfies the fwxschema.AttributeWithStringValidators interface. func (a AttributeWithStringValidators) StringValidators() []validator.String { return a.Validators diff --git a/internal/testing/testschema/nested_attribute.go b/internal/testing/testschema/nested_attribute.go index f37301359..2a638bcb4 100644 --- a/internal/testing/testschema/nested_attribute.go +++ b/internal/testing/testschema/nested_attribute.go @@ -28,6 +28,8 @@ type NestedAttribute struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -163,3 +165,13 @@ func (a NestedAttribute) IsSensitive() bool { func (a NestedAttribute) IsWriteOnly() bool { return a.WriteOnly } + +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/internal/testing/testschema/nested_attribute_with_list_default.go b/internal/testing/testschema/nested_attribute_with_list_default.go index a1b70cd87..2621e9d39 100644 --- a/internal/testing/testschema/nested_attribute_with_list_default.go +++ b/internal/testing/testschema/nested_attribute_with_list_default.go @@ -28,6 +28,8 @@ type NestedAttributeWithListDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithListDefaultValue) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ListDefaultValue satisfies the fwschema.AttributeWithListDefaultValue interface. func (a NestedAttributeWithListDefaultValue) ListDefaultValue() defaults.List { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go index 2904c21ee..e754a7a6d 100644 --- a/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_list_plan_modifiers.go @@ -28,6 +28,8 @@ type NestedAttributeWithListPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithListPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithListPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ListPlanModifiers satisfies the fwxschema.AttributeWithListPlanModifiers interface. func (a NestedAttributeWithListPlanModifiers) ListPlanModifiers() []planmodifier.List { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_map_default.go b/internal/testing/testschema/nested_attribute_with_map_default.go index 6eac9d3ff..be51d0a36 100644 --- a/internal/testing/testschema/nested_attribute_with_map_default.go +++ b/internal/testing/testschema/nested_attribute_with_map_default.go @@ -28,6 +28,8 @@ type NestedAttributeWithMapDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithMapDefaultValue) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} + // MapDefaultValue satisfies the fwschema.AttributeWithMapDefaultValue interface. func (a NestedAttributeWithMapDefaultValue) MapDefaultValue() defaults.Map { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go index 35f2a0732..855ef49df 100644 --- a/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_map_plan_modifiers.go @@ -28,6 +28,8 @@ type NestedAttributeWithMapPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithMapPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithMapPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // MapPlanModifiers satisfies the fwxschema.AttributeWithMapPlanModifiers interface. func (a NestedAttributeWithMapPlanModifiers) MapPlanModifiers() []planmodifier.Map { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_object_default.go b/internal/testing/testschema/nested_attribute_with_object_default.go index e8579c2ac..5d83fb9d9 100644 --- a/internal/testing/testschema/nested_attribute_with_object_default.go +++ b/internal/testing/testschema/nested_attribute_with_object_default.go @@ -29,6 +29,8 @@ type NestedAttributeWithObjectDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -111,6 +113,16 @@ func (a NestedAttributeWithObjectDefaultValue) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ObjectDefaultValue satisfies the fwschema.AttributeWithListDefaultValue interface. func (a NestedAttributeWithObjectDefaultValue) ObjectDefaultValue() defaults.Object { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go index 2d86af989..5f70c1fda 100644 --- a/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_object_plan_modifiers.go @@ -27,6 +27,8 @@ type NestedAttributeWithObjectPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -105,6 +107,16 @@ func (a NestedAttributeWithObjectPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithObjectPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // ObjectPlanModifiers satisfies the fwxschema.AttributeWithObjectPlanModifiers interface. func (a NestedAttributeWithObjectPlanModifiers) ObjectPlanModifiers() []planmodifier.Object { return a.PlanModifiers diff --git a/internal/testing/testschema/nested_attribute_with_set_default.go b/internal/testing/testschema/nested_attribute_with_set_default.go index 3a80c28a7..16fc98d1c 100644 --- a/internal/testing/testschema/nested_attribute_with_set_default.go +++ b/internal/testing/testschema/nested_attribute_with_set_default.go @@ -28,6 +28,8 @@ type NestedAttributeWithSetDefaultValue struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithSetDefaultValue) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetDefaultValue) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetDefaultValue) IsOptionalForImport() bool { + return a.OptionalForImport +} + // MapDefaultValue satisfies the fwschema.AttributeWithMapDefaultValue interface. func (a NestedAttributeWithSetDefaultValue) SetDefaultValue() defaults.Set { return a.Default diff --git a/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go b/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go index 1d886e70e..a92acd392 100644 --- a/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go +++ b/internal/testing/testschema/nested_attribute_with_set_plan_modifiers.go @@ -28,6 +28,8 @@ type NestedAttributeWithSetPlanModifiers struct { Required bool Sensitive bool WriteOnly bool + RequiredForImport bool + OptionalForImport bool Type attr.Type } @@ -108,6 +110,16 @@ func (a NestedAttributeWithSetPlanModifiers) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetPlanModifiers) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport satisfies the fwschema.Attribute interface. +func (a NestedAttributeWithSetPlanModifiers) IsOptionalForImport() bool { + return a.OptionalForImport +} + // SetPlanModifiers satisfies the fwxschema.AttributeWithSetPlanModifiers interface. func (a NestedAttributeWithSetPlanModifiers) SetPlanModifiers() []planmodifier.Set { return a.PlanModifiers diff --git a/internal/toproto5/getresourceidentityschemas.go b/internal/toproto5/getresourceidentityschemas.go new file mode 100644 index 000000000..4080a1710 --- /dev/null +++ b/internal/toproto5/getresourceidentityschemas.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// GetResourceIdentitySchemasResponse returns the *tfprotov5.GetResourceIdentitySchemasResponse +// equivalent of a *fwserver.GetResourceIdentitySchemasResponse. +func GetResourceIdentitySchemasResponse(ctx context.Context, fw *fwserver.GetResourceIdentitySchemasResponse) *tfprotov5.GetResourceIdentitySchemasResponse { + if fw == nil { + return nil + } + + protov5 := &tfprotov5.GetResourceIdentitySchemasResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + IdentitySchemas: make(map[string]*tfprotov5.ResourceIdentitySchema, len(fw.IdentitySchemas)), + } + + var err error + + for resourceType, identitySchema := range fw.IdentitySchemas { + protov5.IdentitySchemas[resourceType], err = IdentitySchema(ctx, identitySchema) + + if err != nil { + protov5.Diagnostics = append(protov5.Diagnostics, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Error converting resource identity schema", + Detail: "The identity schema for the resource \"" + resourceType + "\" couldn't be converted into a usable type. This is always a problem with the provider. Please report the following to the provider developer:\n\n" + err.Error(), + }) + + return protov5 + } + } + + return protov5 +} diff --git a/internal/toproto5/getresourceidentityschemas_test.go b/internal/toproto5/getresourceidentityschemas_test.go new file mode 100644 index 000000000..0dff87452 --- /dev/null +++ b/internal/toproto5/getresourceidentityschemas_test.go @@ -0,0 +1,367 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestGetResourceIdentitySchemasResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input *fwserver.GetResourceIdentitySchemasResponse + expected *tfprotov5.GetResourceIdentitySchemasResponse + }{ + "nil": { + input: nil, + expected: nil, + }, + "resource-identity-identity-multiple-resources": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource_1": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + "test_resource_2": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource_1": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + RequiredForImport: true, + Name: "test_attribute", + Type: tftypes.Bool, + }, + }, + }, + "test_resource_2": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + RequiredForImport: true, + Name: "test_attribute", + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-optionalforimport": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + OptionalForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + OptionalForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-requiredforimport": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + Type: tftypes.Bool, + RequiredForImport: true, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-bool": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-float32": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Float32Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-float64": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Float64Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-int32": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Int32Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-int64": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Int64Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-list-string": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.StringType, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-number": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.NumberAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-string": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.String, + }, + }, + }, + }, + }, + }, + "resource-identity-version": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Version: 123, + }, + }, + }, + expected: &tfprotov5.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov5.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{}, + Version: 123, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := toproto5.GetResourceIdentitySchemasResponse(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/toproto5/identity_schema.go b/internal/toproto5/identity_schema.go new file mode 100644 index 000000000..003dbf155 --- /dev/null +++ b/internal/toproto5/identity_schema.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + "sort" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// IdentitySchema returns the *tfprotov5.ResourceIdentitySchema equivalent of a Schema. +func IdentitySchema(ctx context.Context, s fwschema.Schema) (*tfprotov5.ResourceIdentitySchema, error) { + if s == nil { + return nil, nil + } + + result := &tfprotov5.ResourceIdentitySchema{ + Version: s.GetVersion(), + } + + attrs := make([]*tfprotov5.ResourceIdentitySchemaAttribute, 0) + + for name, attr := range s.GetAttributes() { + a, err := IdentitySchemaAttribute(ctx, name, tftypes.NewAttributePath().WithAttributeName(name), attr) + + if err != nil { + return nil, err + } + + attrs = append(attrs, a) + } + + sort.Slice(attrs, func(i, j int) bool { + if attrs[i] == nil { + return true + } + + if attrs[j] == nil { + return false + } + + return attrs[i].Name < attrs[j].Name + }) + + result.IdentityAttributes = attrs + + return result, nil +} diff --git a/internal/toproto5/identity_schema_attribute.go b/internal/toproto5/identity_schema_attribute.go new file mode 100644 index 000000000..01714f7a3 --- /dev/null +++ b/internal/toproto5/identity_schema_attribute.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// IdentitySchemaAttribute returns the *tfprotov5.ResourceIdentitySchemaAttribute equivalent of an +// Attribute. Errors will be tftypes.AttributePathErrors based on `path`. `name` is the name of the attribute. +func IdentitySchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePath, a fwschema.Attribute) (*tfprotov5.ResourceIdentitySchemaAttribute, error) { + if _, ok := a.(fwschema.NestedAttribute); ok { + return nil, path.NewErrorf("identity schemas and protocol version 5 don't support NestedAttribute") + } + + if a.GetType() == nil { + return nil, path.NewErrorf("must have Type set") + } + + if !a.IsRequiredForImport() && !a.IsOptionalForImport() { + return nil, path.NewErrorf("must have RequiredForImport or OptionalForImport set") + } + + identitySchemaAttribute := &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: name, + RequiredForImport: a.IsRequiredForImport(), + OptionalForImport: a.IsOptionalForImport(), + Type: a.GetType().TerraformType(ctx), + + // Unlike other schema attributes, identity attributes only have a single description field which + // is assumed to be markdown. Both a.GetDescription() and a.GetMarkdownDescription() will return + // the same string, so we just chose one here. + Description: a.GetDescription(), + } + + return identitySchemaAttribute, nil +} diff --git a/internal/toproto5/identity_schema_attribute_test.go b/internal/toproto5/identity_schema_attribute_test.go new file mode 100644 index 000000000..28704ba5d --- /dev/null +++ b/internal/toproto5/identity_schema_attribute_test.go @@ -0,0 +1,257 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestIdentitySchemaAttribute(t *testing.T) { + t.Parallel() + + type testCase struct { + name string + attr fwschema.Attribute + path *tftypes.AttributePath + expected *tfprotov5.ResourceIdentitySchemaAttribute + expectedErr string + } + + tests := map[string]testCase{ + "description": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + Description: "A string attribute", + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + Description: "A string attribute", + }, + }, + "attr-string": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + "attr-bool": { + name: "bool", + attr: testschema.Attribute{ + Type: types.BoolType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "bool", + Type: tftypes.Bool, + RequiredForImport: true, + }, + }, + "attr-number": { + name: "number", + attr: testschema.Attribute{ + Type: types.NumberType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "number", + Type: tftypes.Number, + RequiredForImport: true, + }, + }, + "attr-list": { + name: "list", + attr: testschema.Attribute{ + Type: types.ListType{ElemType: types.NumberType}, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "list", + Type: tftypes.List{ElementType: tftypes.Number}, + RequiredForImport: true, + }, + }, + "requiredforimport": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + "optionalforimport": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + OptionalForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov5.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + OptionalForImport: true, + }, + }, + "nested-attr-single-error": { + name: "single_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeSingle, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas and protocol version 5 don't support NestedAttribute", + }, + "nested-attr-list-error": { + name: "list_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeList, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas and protocol version 5 don't support NestedAttribute", + }, + "nested-attr-map-error": { + name: "map_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeMap, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas and protocol version 5 don't support NestedAttribute", + }, + "nested-attr-set-error": { + name: "set_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeSet, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas and protocol version 5 don't support NestedAttribute", + }, + "attr-unset": { + name: "whoops", + attr: testschema.Attribute{ + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "must have Type set", + }, + "missing-requiredforimport-and-optionalforimport": { + name: "whoops", + attr: testschema.Attribute{ + Type: types.StringType, + }, + path: tftypes.NewAttributePath(), + expectedErr: "must have RequiredForImport or OptionalForImport set", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := toproto5.IdentitySchemaAttribute(context.Background(), tc.name, tc.path, tc.attr) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } + }) + } +} diff --git a/internal/toproto5/identity_schema_test.go b/internal/toproto5/identity_schema_test.go new file mode 100644 index 000000000..8388c4c16 --- /dev/null +++ b/internal/toproto5/identity_schema_test.go @@ -0,0 +1,138 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestIdentitySchema(t *testing.T) { + t.Parallel() + + type testCase struct { + input fwschema.Schema + expected *tfprotov5.ResourceIdentitySchema + expectedErr string + } + + tests := map[string]testCase{ + "nil": { + input: nil, + expected: nil, + }, + "empty-val": { + input: testschema.Schema{}, + expected: &tfprotov5.ResourceIdentitySchema{ + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{}, + Version: 0, + }, + }, + "basic-attrs": { + input: testschema.Schema{ + Version: 1, + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + "number": testschema.Attribute{ + Type: types.NumberType, + OptionalForImport: true, + }, + "bool": testschema.Attribute{ + Type: types.BoolType, + OptionalForImport: true, + }, + }, + }, + expected: &tfprotov5.ResourceIdentitySchema{ + Version: 1, + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + OptionalForImport: true, + }, + { + Name: "number", + Type: tftypes.Number, + OptionalForImport: true, + }, + { + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + }, + }, + "complex-attrs": { + input: testschema.Schema{ + Version: 2, + Attributes: map[string]fwschema.Attribute{ + "list_of_string": testschema.Attribute{ + Type: types.ListType{ElemType: types.StringType}, + RequiredForImport: true, + }, + "list_of_bool": testschema.Attribute{ + Type: types.ListType{ElemType: types.BoolType}, + RequiredForImport: true, + }, + }, + }, + expected: &tfprotov5.ResourceIdentitySchema{ + Version: 2, + IdentityAttributes: []*tfprotov5.ResourceIdentitySchemaAttribute{ + { + Name: "list_of_bool", + Type: tftypes.List{ElementType: tftypes.Bool}, + RequiredForImport: true, + }, + { + Name: "list_of_string", + Type: tftypes.List{ElementType: tftypes.String}, + RequiredForImport: true, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := toproto5.IdentitySchema(context.Background(), tc.input) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } + }) + } +} diff --git a/internal/toproto6/getresourceidentityschemas.go b/internal/toproto6/getresourceidentityschemas.go new file mode 100644 index 000000000..eb5d406e2 --- /dev/null +++ b/internal/toproto6/getresourceidentityschemas.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// GetResourceIdentitySchemasResponse returns the *tfprotov6.GetResourceIdentitySchemasResponse +// equivalent of a *fwserver.GetResourceIdentitySchemasResponse. +func GetResourceIdentitySchemasResponse(ctx context.Context, fw *fwserver.GetResourceIdentitySchemasResponse) *tfprotov6.GetResourceIdentitySchemasResponse { + if fw == nil { + return nil + } + + protov6 := &tfprotov6.GetResourceIdentitySchemasResponse{ + Diagnostics: Diagnostics(ctx, fw.Diagnostics), + IdentitySchemas: make(map[string]*tfprotov6.ResourceIdentitySchema, len(fw.IdentitySchemas)), + } + + var err error + + for resourceType, identitySchema := range fw.IdentitySchemas { + protov6.IdentitySchemas[resourceType], err = IdentitySchema(ctx, identitySchema) + + if err != nil { + protov6.Diagnostics = append(protov6.Diagnostics, &tfprotov6.Diagnostic{ + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Error converting resource identity schema", + Detail: "The identity schema for the resource \"" + resourceType + "\" couldn't be converted into a usable type. This is always a problem with the provider. Please report the following to the provider developer:\n\n" + err.Error(), + }) + + return protov6 + } + } + + return protov6 +} diff --git a/internal/toproto6/getresourceidentityschemas_test.go b/internal/toproto6/getresourceidentityschemas_test.go new file mode 100644 index 000000000..86670bdf4 --- /dev/null +++ b/internal/toproto6/getresourceidentityschemas_test.go @@ -0,0 +1,367 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestGetResourceIdentitySchemasResponse(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + input *fwserver.GetResourceIdentitySchemasResponse + expected *tfprotov6.GetResourceIdentitySchemasResponse + }{ + "nil": { + input: nil, + expected: nil, + }, + "resource-identity-identity-multiple-resources": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource_1": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + "test_resource_2": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource_1": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + RequiredForImport: true, + Name: "test_attribute", + Type: tftypes.Bool, + }, + }, + }, + "test_resource_2": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + RequiredForImport: true, + Name: "test_attribute", + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-optionalforimport": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + OptionalForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + OptionalForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-requiredforimport": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + Type: tftypes.Bool, + RequiredForImport: true, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-bool": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Bool, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-float32": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Float32Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-float64": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Float64Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-int32": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Int32Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-int64": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.Int64Attribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-list-string": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.StringType, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.List{ + ElementType: tftypes.String, + }, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-number": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.NumberAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.Number, + }, + }, + }, + }, + }, + }, + "resource-identity-attribute-type-string": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "test_attribute", + RequiredForImport: true, + Type: tftypes.String, + }, + }, + }, + }, + }, + }, + "resource-identity-version": { + input: &fwserver.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]fwschema.Schema{ + "test_resource": identityschema.Schema{ + Version: 123, + }, + }, + }, + expected: &tfprotov6.GetResourceIdentitySchemasResponse{ + IdentitySchemas: map[string]*tfprotov6.ResourceIdentitySchema{ + "test_resource": { + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{}, + Version: 123, + }, + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := toproto6.GetResourceIdentitySchemasResponse(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/toproto6/identity_schema.go b/internal/toproto6/identity_schema.go new file mode 100644 index 000000000..3c6455800 --- /dev/null +++ b/internal/toproto6/identity_schema.go @@ -0,0 +1,52 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + "sort" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// IdentitySchema returns the *tfprotov6.ResourceIdentitySchema equivalent of a Schema. +func IdentitySchema(ctx context.Context, s fwschema.Schema) (*tfprotov6.ResourceIdentitySchema, error) { + if s == nil { + return nil, nil + } + + result := &tfprotov6.ResourceIdentitySchema{ + Version: s.GetVersion(), + } + + attrs := make([]*tfprotov6.ResourceIdentitySchemaAttribute, 0) + + for name, attr := range s.GetAttributes() { + a, err := IdentitySchemaAttribute(ctx, name, tftypes.NewAttributePath().WithAttributeName(name), attr) + + if err != nil { + return nil, err + } + + attrs = append(attrs, a) + } + + sort.Slice(attrs, func(i, j int) bool { + if attrs[i] == nil { + return true + } + + if attrs[j] == nil { + return false + } + + return attrs[i].Name < attrs[j].Name + }) + + result.IdentityAttributes = attrs + + return result, nil +} diff --git a/internal/toproto6/identity_schema_attribute.go b/internal/toproto6/identity_schema_attribute.go new file mode 100644 index 000000000..5fe815de1 --- /dev/null +++ b/internal/toproto6/identity_schema_attribute.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// IdentitySchemaAttribute returns the *tfprotov6.ResourceIdentitySchemaAttribute equivalent of an +// Attribute. Errors will be tftypes.AttributePathErrors based on `path`. `name` is the name of the attribute. +func IdentitySchemaAttribute(ctx context.Context, name string, path *tftypes.AttributePath, a fwschema.Attribute) (*tfprotov6.ResourceIdentitySchemaAttribute, error) { + if _, ok := a.(fwschema.NestedAttribute); ok { + return nil, path.NewErrorf("identity schemas don't support NestedAttribute") + } + + if a.GetType() == nil { + return nil, path.NewErrorf("must have Type set") + } + + if !a.IsRequiredForImport() && !a.IsOptionalForImport() { + return nil, path.NewErrorf("must have RequiredForImport or OptionalForImport set") + } + + identitySchemaAttribute := &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: name, + RequiredForImport: a.IsRequiredForImport(), + OptionalForImport: a.IsOptionalForImport(), + Type: a.GetType().TerraformType(ctx), + + // Unlike other schema attributes, identity attributes only have a single description field which + // is assumed to be markdown. Both a.GetDescription() and a.GetMarkdownDescription() will return + // the same string, so we just chose one here. + Description: a.GetDescription(), + } + + return identitySchemaAttribute, nil +} diff --git a/internal/toproto6/identity_schema_attribute_test.go b/internal/toproto6/identity_schema_attribute_test.go new file mode 100644 index 000000000..45fa0d1a1 --- /dev/null +++ b/internal/toproto6/identity_schema_attribute_test.go @@ -0,0 +1,257 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestIdentitySchemaAttribute(t *testing.T) { + t.Parallel() + + type testCase struct { + name string + attr fwschema.Attribute + path *tftypes.AttributePath + expected *tfprotov6.ResourceIdentitySchemaAttribute + expectedErr string + } + + tests := map[string]testCase{ + "description": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + Description: "A string attribute", + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + Description: "A string attribute", + }, + }, + "attr-string": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + "attr-bool": { + name: "bool", + attr: testschema.Attribute{ + Type: types.BoolType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "bool", + Type: tftypes.Bool, + RequiredForImport: true, + }, + }, + "attr-number": { + name: "number", + attr: testschema.Attribute{ + Type: types.NumberType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "number", + Type: tftypes.Number, + RequiredForImport: true, + }, + }, + "attr-list": { + name: "list", + attr: testschema.Attribute{ + Type: types.ListType{ElemType: types.NumberType}, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "list", + Type: tftypes.List{ElementType: tftypes.Number}, + RequiredForImport: true, + }, + }, + "requiredforimport": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + "optionalforimport": { + name: "string", + attr: testschema.Attribute{ + Type: types.StringType, + OptionalForImport: true, + }, + path: tftypes.NewAttributePath(), + expected: &tfprotov6.ResourceIdentitySchemaAttribute{ + Name: "string", + Type: tftypes.String, + OptionalForImport: true, + }, + }, + "nested-attr-single-error": { + name: "single_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeSingle, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas don't support NestedAttribute", + }, + "nested-attr-list-error": { + name: "list_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeList, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas don't support NestedAttribute", + }, + "nested-attr-map-error": { + name: "map_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeMap, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas don't support NestedAttribute", + }, + "nested-attr-set-error": { + name: "set_nested", + attr: testschema.NestedAttribute{ + NestedObject: testschema.NestedAttributeObject{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + Optional: true, + }, + "computed": testschema.Attribute{ + Type: types.NumberType, + Computed: true, + Sensitive: true, + }, + }, + }, + NestingMode: fwschema.NestingModeSet, + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "identity schemas don't support NestedAttribute", + }, + "attr-unset": { + name: "whoops", + attr: testschema.Attribute{ + Optional: true, + }, + path: tftypes.NewAttributePath(), + expectedErr: "must have Type set", + }, + "missing-requiredforimport-and-optionalforimport": { + name: "whoops", + attr: testschema.Attribute{ + Type: types.StringType, + }, + path: tftypes.NewAttributePath(), + expectedErr: "must have RequiredForImport or OptionalForImport set", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := toproto6.IdentitySchemaAttribute(context.Background(), tc.name, tc.path, tc.attr) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } + }) + } +} diff --git a/internal/toproto6/identity_schema_test.go b/internal/toproto6/identity_schema_test.go new file mode 100644 index 000000000..f8ebd1728 --- /dev/null +++ b/internal/toproto6/identity_schema_test.go @@ -0,0 +1,138 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestIdentitySchema(t *testing.T) { + t.Parallel() + + type testCase struct { + input fwschema.Schema + expected *tfprotov6.ResourceIdentitySchema + expectedErr string + } + + tests := map[string]testCase{ + "nil": { + input: nil, + expected: nil, + }, + "empty-val": { + input: testschema.Schema{}, + expected: &tfprotov6.ResourceIdentitySchema{ + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{}, + Version: 0, + }, + }, + "basic-attrs": { + input: testschema.Schema{ + Version: 1, + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + "number": testschema.Attribute{ + Type: types.NumberType, + OptionalForImport: true, + }, + "bool": testschema.Attribute{ + Type: types.BoolType, + OptionalForImport: true, + }, + }, + }, + expected: &tfprotov6.ResourceIdentitySchema{ + Version: 1, + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "bool", + Type: tftypes.Bool, + OptionalForImport: true, + }, + { + Name: "number", + Type: tftypes.Number, + OptionalForImport: true, + }, + { + Name: "string", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + }, + }, + "complex-attrs": { + input: testschema.Schema{ + Version: 2, + Attributes: map[string]fwschema.Attribute{ + "list_of_string": testschema.Attribute{ + Type: types.ListType{ElemType: types.StringType}, + RequiredForImport: true, + }, + "list_of_bool": testschema.Attribute{ + Type: types.ListType{ElemType: types.BoolType}, + RequiredForImport: true, + }, + }, + }, + expected: &tfprotov6.ResourceIdentitySchema{ + Version: 2, + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "list_of_bool", + Type: tftypes.List{ElementType: tftypes.Bool}, + RequiredForImport: true, + }, + { + Name: "list_of_string", + Type: tftypes.List{ElementType: tftypes.String}, + RequiredForImport: true, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := toproto6.IdentitySchema(context.Background(), tc.input) + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected diff (+wanted, -got): %s", diff) + return + } + }) + } +} diff --git a/provider/metaschema/bool_attribute.go b/provider/metaschema/bool_attribute.go index 374296341..d02fcc801 100644 --- a/provider/metaschema/bool_attribute.go +++ b/provider/metaschema/bool_attribute.go @@ -124,3 +124,15 @@ func (a BoolAttribute) IsSensitive() bool { func (a BoolAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/bool_attribute_test.go b/provider/metaschema/bool_attribute_test.go index f679fc1a5..caeefaeb1 100644 --- a/provider/metaschema/bool_attribute_test.go +++ b/provider/metaschema/bool_attribute_test.go @@ -377,3 +377,55 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/float64_attribute.go b/provider/metaschema/float64_attribute.go index ac2b79b0a..96c634fbf 100644 --- a/provider/metaschema/float64_attribute.go +++ b/provider/metaschema/float64_attribute.go @@ -127,3 +127,15 @@ func (a Float64Attribute) IsSensitive() bool { func (a Float64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/float64_attribute_test.go b/provider/metaschema/float64_attribute_test.go index 8a73c9225..0ee1b3d42 100644 --- a/provider/metaschema/float64_attribute_test.go +++ b/provider/metaschema/float64_attribute_test.go @@ -377,3 +377,55 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/int64_attribute.go b/provider/metaschema/int64_attribute.go index aeccd7030..7490bd7b5 100644 --- a/provider/metaschema/int64_attribute.go +++ b/provider/metaschema/int64_attribute.go @@ -127,3 +127,15 @@ func (a Int64Attribute) IsSensitive() bool { func (a Int64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/int64_attribute_test.go b/provider/metaschema/int64_attribute_test.go index 69439d021..ab7a5bdf6 100644 --- a/provider/metaschema/int64_attribute_test.go +++ b/provider/metaschema/int64_attribute_test.go @@ -377,3 +377,55 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/list_attribute.go b/provider/metaschema/list_attribute.go index 187d9c47c..ea663a8a9 100644 --- a/provider/metaschema/list_attribute.go +++ b/provider/metaschema/list_attribute.go @@ -141,6 +141,18 @@ func (a ListAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/list_attribute_test.go b/provider/metaschema/list_attribute_test.go index 93f213ce0..66f5e6b66 100644 --- a/provider/metaschema/list_attribute_test.go +++ b/provider/metaschema/list_attribute_test.go @@ -451,3 +451,55 @@ func TestListAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/list_nested_attribute.go b/provider/metaschema/list_nested_attribute.go index 0fa1b8221..ccc3293d6 100644 --- a/provider/metaschema/list_nested_attribute.go +++ b/provider/metaschema/list_nested_attribute.go @@ -166,3 +166,15 @@ func (a ListNestedAttribute) IsSensitive() bool { func (a ListNestedAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/list_nested_attribute_test.go b/provider/metaschema/list_nested_attribute_test.go index 1c3576a30..fe587af0d 100644 --- a/provider/metaschema/list_nested_attribute_test.go +++ b/provider/metaschema/list_nested_attribute_test.go @@ -551,3 +551,55 @@ func TestListNestedAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestListNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ListNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/map_attribute.go b/provider/metaschema/map_attribute.go index 9103231ff..99d90e767 100644 --- a/provider/metaschema/map_attribute.go +++ b/provider/metaschema/map_attribute.go @@ -144,6 +144,18 @@ func (a MapAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/map_attribute_test.go b/provider/metaschema/map_attribute_test.go index 7d137ef1d..11ec28d01 100644 --- a/provider/metaschema/map_attribute_test.go +++ b/provider/metaschema/map_attribute_test.go @@ -451,3 +451,55 @@ func TestMapAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/map_nested_attribute.go b/provider/metaschema/map_nested_attribute.go index 587c56c0a..79029c833 100644 --- a/provider/metaschema/map_nested_attribute.go +++ b/provider/metaschema/map_nested_attribute.go @@ -166,3 +166,15 @@ func (a MapNestedAttribute) IsSensitive() bool { func (a MapNestedAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/map_nested_attribute_test.go b/provider/metaschema/map_nested_attribute_test.go index 99c5ae558..b2cc9a219 100644 --- a/provider/metaschema/map_nested_attribute_test.go +++ b/provider/metaschema/map_nested_attribute_test.go @@ -551,3 +551,55 @@ func TestMapNestedAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestMapNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.MapNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/number_attribute.go b/provider/metaschema/number_attribute.go index 511e7000a..3e345a7fb 100644 --- a/provider/metaschema/number_attribute.go +++ b/provider/metaschema/number_attribute.go @@ -128,3 +128,15 @@ func (a NumberAttribute) IsSensitive() bool { func (a NumberAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/number_attribute_test.go b/provider/metaschema/number_attribute_test.go index 676db8e1b..53a094126 100644 --- a/provider/metaschema/number_attribute_test.go +++ b/provider/metaschema/number_attribute_test.go @@ -377,3 +377,55 @@ func TestNumberAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/object_attribute.go b/provider/metaschema/object_attribute.go index aabe40d4c..7c325a238 100644 --- a/provider/metaschema/object_attribute.go +++ b/provider/metaschema/object_attribute.go @@ -143,6 +143,18 @@ func (a ObjectAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/object_attribute_test.go b/provider/metaschema/object_attribute_test.go index 6b3bb4479..7b47aa5c9 100644 --- a/provider/metaschema/object_attribute_test.go +++ b/provider/metaschema/object_attribute_test.go @@ -459,3 +459,55 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { }) } } + +func TestObjectAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ObjectAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestObjectAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.ObjectAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/set_attribute.go b/provider/metaschema/set_attribute.go index f7d3e4112..5878c6ec8 100644 --- a/provider/metaschema/set_attribute.go +++ b/provider/metaschema/set_attribute.go @@ -139,6 +139,18 @@ func (a SetAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC diff --git a/provider/metaschema/set_attribute_test.go b/provider/metaschema/set_attribute_test.go index 975b3b719..94bf6346b 100644 --- a/provider/metaschema/set_attribute_test.go +++ b/provider/metaschema/set_attribute_test.go @@ -451,3 +451,55 @@ func TestSetAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/set_nested_attribute.go b/provider/metaschema/set_nested_attribute.go index a3c6fbf9e..102ede616 100644 --- a/provider/metaschema/set_nested_attribute.go +++ b/provider/metaschema/set_nested_attribute.go @@ -161,3 +161,15 @@ func (a SetNestedAttribute) IsSensitive() bool { func (a SetNestedAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/set_nested_attribute_test.go b/provider/metaschema/set_nested_attribute_test.go index 225a936f9..5e7c46f66 100644 --- a/provider/metaschema/set_nested_attribute_test.go +++ b/provider/metaschema/set_nested_attribute_test.go @@ -551,3 +551,55 @@ func TestSetNestedAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestSetNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SetNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/single_nested_attribute.go b/provider/metaschema/single_nested_attribute.go index 160fb1c80..51b7c73b6 100644 --- a/provider/metaschema/single_nested_attribute.go +++ b/provider/metaschema/single_nested_attribute.go @@ -181,3 +181,15 @@ func (a SingleNestedAttribute) IsSensitive() bool { func (a SingleNestedAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/single_nested_attribute_test.go b/provider/metaschema/single_nested_attribute_test.go index d590cbba8..82b57349f 100644 --- a/provider/metaschema/single_nested_attribute_test.go +++ b/provider/metaschema/single_nested_attribute_test.go @@ -515,3 +515,55 @@ func TestSingleNestedAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SingleNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSingleNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.SingleNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/metaschema/string_attribute.go b/provider/metaschema/string_attribute.go index fe25c5014..eb056e2d1 100644 --- a/provider/metaschema/string_attribute.go +++ b/provider/metaschema/string_attribute.go @@ -124,3 +124,15 @@ func (a StringAttribute) IsSensitive() bool { func (a StringAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/metaschema/string_attribute_test.go b/provider/metaschema/string_attribute_test.go index c360462a1..2d12a2f58 100644 --- a/provider/metaschema/string_attribute_test.go +++ b/provider/metaschema/string_attribute_test.go @@ -377,3 +377,55 @@ func TestStringAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: metaschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute metaschema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: metaschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/bool_attribute.go b/provider/schema/bool_attribute.go index 0502821ca..9f4f37138 100644 --- a/provider/schema/bool_attribute.go +++ b/provider/schema/bool_attribute.go @@ -185,3 +185,15 @@ func (a BoolAttribute) IsSensitive() bool { func (a BoolAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/bool_attribute_test.go b/provider/schema/bool_attribute_test.go index 3e134f214..07291287b 100644 --- a/provider/schema/bool_attribute_test.go +++ b/provider/schema/bool_attribute_test.go @@ -422,3 +422,55 @@ func TestBoolAttributeIsWriteOnly(t *testing.T) { }) } } + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/dynamic_attribute.go b/provider/schema/dynamic_attribute.go index 4b31279f8..86da9aef2 100644 --- a/provider/schema/dynamic_attribute.go +++ b/provider/schema/dynamic_attribute.go @@ -182,3 +182,15 @@ func (a DynamicAttribute) DynamicValidators() []validator.Dynamic { func (a DynamicAttribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/dynamic_attribute_test.go b/provider/schema/dynamic_attribute_test.go index 66c7d855e..bd6ec94a7 100644 --- a/provider/schema/dynamic_attribute_test.go +++ b/provider/schema/dynamic_attribute_test.go @@ -422,3 +422,55 @@ func TestDynamicAttributeDynamicValidators(t *testing.T) { }) } } + +func TestDynamicAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestDynamicAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/float32_attribute.go b/provider/schema/float32_attribute.go index 8e62dc96d..bd124a33f 100644 --- a/provider/schema/float32_attribute.go +++ b/provider/schema/float32_attribute.go @@ -188,3 +188,15 @@ func (a Float32Attribute) IsSensitive() bool { func (a Float32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/float32_attribute_test.go b/provider/schema/float32_attribute_test.go index 70d13db04..2793464d7 100644 --- a/provider/schema/float32_attribute_test.go +++ b/provider/schema/float32_attribute_test.go @@ -422,3 +422,55 @@ func TestFloat32AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/float64_attribute.go b/provider/schema/float64_attribute.go index f0c0cc008..d7c328329 100644 --- a/provider/schema/float64_attribute.go +++ b/provider/schema/float64_attribute.go @@ -188,3 +188,15 @@ func (a Float64Attribute) IsSensitive() bool { func (a Float64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/float64_attribute_test.go b/provider/schema/float64_attribute_test.go index 8ff796cdb..01db35d43 100644 --- a/provider/schema/float64_attribute_test.go +++ b/provider/schema/float64_attribute_test.go @@ -422,3 +422,55 @@ func TestFloat64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/int32_attribute.go b/provider/schema/int32_attribute.go index 1f8c60c39..1c5bde95f 100644 --- a/provider/schema/int32_attribute.go +++ b/provider/schema/int32_attribute.go @@ -188,3 +188,15 @@ func (a Int32Attribute) IsSensitive() bool { func (a Int32Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/int32_attribute_test.go b/provider/schema/int32_attribute_test.go index b9fa878b7..f93d28ef0 100644 --- a/provider/schema/int32_attribute_test.go +++ b/provider/schema/int32_attribute_test.go @@ -422,3 +422,55 @@ func TestInt32AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/int64_attribute.go b/provider/schema/int64_attribute.go index 25243bbf1..bf334a6d3 100644 --- a/provider/schema/int64_attribute.go +++ b/provider/schema/int64_attribute.go @@ -188,3 +188,15 @@ func (a Int64Attribute) IsSensitive() bool { func (a Int64Attribute) IsWriteOnly() bool { return false } + +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsOptionalForImport() bool { + return false +} diff --git a/provider/schema/int64_attribute_test.go b/provider/schema/int64_attribute_test.go index c5ebec88a..c2bdc7d1d 100644 --- a/provider/schema/int64_attribute_test.go +++ b/provider/schema/int64_attribute_test.go @@ -422,3 +422,55 @@ func TestInt64AttributeIsWriteOnly(t *testing.T) { }) } } + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/list_attribute.go b/provider/schema/list_attribute.go index b85848b58..55f6f233c 100644 --- a/provider/schema/list_attribute.go +++ b/provider/schema/list_attribute.go @@ -202,6 +202,18 @@ func (a ListAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsOptionalForImport() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListAttribute) ListValidators() []validator.List { return a.Validators diff --git a/provider/schema/list_attribute_test.go b/provider/schema/list_attribute_test.go index d0ab91c58..bb8451841 100644 --- a/provider/schema/list_attribute_test.go +++ b/provider/schema/list_attribute_test.go @@ -518,3 +518,55 @@ func TestListAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/list_nested_attribute.go b/provider/schema/list_nested_attribute.go index 700299c05..e00e940b0 100644 --- a/provider/schema/list_nested_attribute.go +++ b/provider/schema/list_nested_attribute.go @@ -230,6 +230,18 @@ func (a ListNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsOptionalForImport() bool { + return false +} + // ListValidators returns the Validators field value. func (a ListNestedAttribute) ListValidators() []validator.List { return a.Validators diff --git a/provider/schema/list_nested_attribute_test.go b/provider/schema/list_nested_attribute_test.go index 1f3715c73..b6130073e 100644 --- a/provider/schema/list_nested_attribute_test.go +++ b/provider/schema/list_nested_attribute_test.go @@ -683,3 +683,55 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/map_attribute.go b/provider/schema/map_attribute.go index 82b5a05d7..28cc5f5b8 100644 --- a/provider/schema/map_attribute.go +++ b/provider/schema/map_attribute.go @@ -205,6 +205,18 @@ func (a MapAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/provider/schema/map_attribute_test.go b/provider/schema/map_attribute_test.go index f2e1e6a25..bf46b6c5a 100644 --- a/provider/schema/map_attribute_test.go +++ b/provider/schema/map_attribute_test.go @@ -518,3 +518,55 @@ func TestMapAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/map_nested_attribute.go b/provider/schema/map_nested_attribute.go index 14fb4092b..5b8246491 100644 --- a/provider/schema/map_nested_attribute.go +++ b/provider/schema/map_nested_attribute.go @@ -229,6 +229,18 @@ func (a MapNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsOptionalForImport() bool { + return false +} + // MapValidators returns the Validators field value. func (a MapNestedAttribute) MapValidators() []validator.Map { return a.Validators diff --git a/provider/schema/map_nested_attribute_test.go b/provider/schema/map_nested_attribute_test.go index bfbdb86c9..26812fc03 100644 --- a/provider/schema/map_nested_attribute_test.go +++ b/provider/schema/map_nested_attribute_test.go @@ -683,3 +683,55 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/number_attribute.go b/provider/schema/number_attribute.go index bb6ffc6d6..add021b1c 100644 --- a/provider/schema/number_attribute.go +++ b/provider/schema/number_attribute.go @@ -185,6 +185,18 @@ func (a NumberAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsOptionalForImport() bool { + return false +} + // NumberValidators returns the Validators field value. func (a NumberAttribute) NumberValidators() []validator.Number { return a.Validators diff --git a/provider/schema/number_attribute_test.go b/provider/schema/number_attribute_test.go index 80dba9dd1..daff176a5 100644 --- a/provider/schema/number_attribute_test.go +++ b/provider/schema/number_attribute_test.go @@ -422,3 +422,55 @@ func TestNumberAttributeNumberValidators(t *testing.T) { }) } } + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/object_attribute.go b/provider/schema/object_attribute.go index c5c81a1ba..0cb33f1fd 100644 --- a/provider/schema/object_attribute.go +++ b/provider/schema/object_attribute.go @@ -204,6 +204,18 @@ func (a ObjectAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a ObjectAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/provider/schema/object_attribute_test.go b/provider/schema/object_attribute_test.go index a53bc6bf5..2ae9d0eec 100644 --- a/provider/schema/object_attribute_test.go +++ b/provider/schema/object_attribute_test.go @@ -551,3 +551,55 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { }) } } + +func TestObjectAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestObjectAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/set_attribute.go b/provider/schema/set_attribute.go index eaf73344e..9d1a8f0c0 100644 --- a/provider/schema/set_attribute.go +++ b/provider/schema/set_attribute.go @@ -200,6 +200,18 @@ func (a SetAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/provider/schema/set_attribute_test.go b/provider/schema/set_attribute_test.go index bfa58193b..efa665df6 100644 --- a/provider/schema/set_attribute_test.go +++ b/provider/schema/set_attribute_test.go @@ -518,3 +518,55 @@ func TestSetAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/set_nested_attribute.go b/provider/schema/set_nested_attribute.go index a9dad64a7..f5d788e6f 100644 --- a/provider/schema/set_nested_attribute.go +++ b/provider/schema/set_nested_attribute.go @@ -225,6 +225,18 @@ func (a SetNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsOptionalForImport() bool { + return false +} + // SetValidators returns the Validators field value. func (a SetNestedAttribute) SetValidators() []validator.Set { return a.Validators diff --git a/provider/schema/set_nested_attribute_test.go b/provider/schema/set_nested_attribute_test.go index 3471f42e3..6dd173469 100644 --- a/provider/schema/set_nested_attribute_test.go +++ b/provider/schema/set_nested_attribute_test.go @@ -683,3 +683,55 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/single_nested_attribute.go b/provider/schema/single_nested_attribute.go index aded0988e..6c306d167 100644 --- a/provider/schema/single_nested_attribute.go +++ b/provider/schema/single_nested_attribute.go @@ -239,6 +239,18 @@ func (a SingleNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsOptionalForImport() bool { + return false +} + // ObjectValidators returns the Validators field value. func (a SingleNestedAttribute) ObjectValidators() []validator.Object { return a.Validators diff --git a/provider/schema/single_nested_attribute_test.go b/provider/schema/single_nested_attribute_test.go index cbb2f3aa6..a674a82a9 100644 --- a/provider/schema/single_nested_attribute_test.go +++ b/provider/schema/single_nested_attribute_test.go @@ -564,3 +564,55 @@ func TestSingleNestedAttributeObjectValidators(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSingleNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/provider/schema/string_attribute.go b/provider/schema/string_attribute.go index eda7a02c4..feb9e8039 100644 --- a/provider/schema/string_attribute.go +++ b/provider/schema/string_attribute.go @@ -181,6 +181,18 @@ func (a StringAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsOptionalForImport() bool { + return false +} + // StringValidators returns the Validators field value. func (a StringAttribute) StringValidators() []validator.String { return a.Validators diff --git a/provider/schema/string_attribute_test.go b/provider/schema/string_attribute_test.go index 35e9f03a8..366f84116 100644 --- a/provider/schema/string_attribute_test.go +++ b/provider/schema/string_attribute_test.go @@ -422,3 +422,55 @@ func TestStringAttributeStringValidators(t *testing.T) { }) } } + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identity_schema.go b/resource/identity_schema.go new file mode 100644 index 000000000..812173d20 --- /dev/null +++ b/resource/identity_schema.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package resource + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" +) + +// IdentitySchemaRequest represents a request for the Resource to return its identity schema. +// An instance of this request struct is supplied as an argument to the +// Resource type IdentitySchema method. +type IdentitySchemaRequest struct{} + +// IdentitySchemaResponse represents a response to a SchemaRequest. An instance of this +// response struct is supplied as an argument to the Resource type IdentitySchema +// method. +type IdentitySchemaResponse struct { + // IdentitySchema is the schema of the resource identity. + IdentitySchema identityschema.Schema + + // Diagnostics report errors or warnings related to retrieving the resource + // identity schema. An empty slice indicates success, with no warnings + // or errors generated. + Diagnostics diag.Diagnostics +} diff --git a/resource/identityschema/attribute.go b/resource/identityschema/attribute.go new file mode 100644 index 000000000..06ef93150 --- /dev/null +++ b/resource/identityschema/attribute.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +) + +// Attribute define a value field inside the Schema. Implementations in this +// package include: +// - BoolAttribute +// - Float32Attribute +// - Float64Attribute +// - Int32Attribute +// - Int64Attribute +// - ListAttribute +// - NumberAttribute +// - StringAttribute +// +// The available attribute types for a resource identity schema are intentionally +// limited. Nested attributes and blocks are not supported in identity schemas, +// as well as ListAttribute definitions can only have primitive element types of: +// - types.BoolType +// - types.Float32Type +// - types.Float64Type +// - types.Int32Type +// - types.Int64Type +// - types.NumberType +// - types.StringType +type Attribute interface { + fwschema.Attribute +} diff --git a/resource/identityschema/bool_attribute.go b/resource/identityschema/bool_attribute.go new file mode 100644 index 000000000..c17ae9549 --- /dev/null +++ b/resource/identityschema/bool_attribute.go @@ -0,0 +1,127 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = BoolAttribute{} +) + +// BoolAttribute represents a schema attribute that is a boolean. When +// retrieving the value for this attribute, use types.Bool as the value type +// unless the CustomType field is set. +// +// Terraform configurations configure this attribute using expressions that +// return a boolean or directly via the true/false keywords. +// +// example_attribute = true +type BoolAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.BoolType. When retrieving data, the basetypes.BoolValuable + // associated with this custom type must be used in place of types.Bool. + CustomType basetypes.BoolTypable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a BoolAttribute. +func (a BoolAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a BoolAttribute +// and all fields are equal. +func (a BoolAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(BoolAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a BoolAttribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a BoolAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a BoolAttribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.StringType or the CustomType field value if defined. +func (a BoolAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.BoolType +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a BoolAttribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a BoolAttribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a BoolAttribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a BoolAttribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a BoolAttribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a BoolAttribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a BoolAttribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/bool_attribute_test.go b/resource/identityschema/bool_attribute_test.go new file mode 100644 index 000000000..057cde795 --- /dev/null +++ b/resource/identityschema/bool_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.BoolAttribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.BoolType"), + }, + "ElementKeyInt": { + attribute: identityschema.BoolAttribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.BoolType"), + }, + "ElementKeyString": { + attribute: identityschema.BoolAttribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.BoolType"), + }, + "ElementKeyValue": { + attribute: identityschema.BoolAttribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.BoolType"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.BoolAttribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.BoolAttribute{}, + other: testschema.AttributeWithBoolValidators{}, + expected: false, + }, + "equal": { + attribute: identityschema.BoolAttribute{}, + other: identityschema.BoolAttribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected string + }{ + "no-description": { + attribute: identityschema.BoolAttribute{}, + expected: "", + }, + "description": { + attribute: identityschema.BoolAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.BoolAttribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.BoolAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected attr.Type + }{ + "base": { + attribute: identityschema.BoolAttribute{}, + expected: types.BoolType, + }, + "custom-type": { + attribute: identityschema.BoolAttribute{ + CustomType: testtypes.BoolType{}, + }, + expected: testtypes.BoolType{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-computed": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-optional": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-required": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.BoolAttribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.BoolAttribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.BoolAttribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/doc.go b/resource/identityschema/doc.go new file mode 100644 index 000000000..5e468a5cf --- /dev/null +++ b/resource/identityschema/doc.go @@ -0,0 +1,9 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package identityschema contains all available schema functionality for managed +// resource identity. +// +// Resource identity schemas define the structure and value types for identity state data. +// Schemas are implemented via the resource.ResourceWithIdentity type IdentitySchema method. +package identityschema diff --git a/resource/identityschema/float32_attribute.go b/resource/identityschema/float32_attribute.go new file mode 100644 index 000000000..ce741d32c --- /dev/null +++ b/resource/identityschema/float32_attribute.go @@ -0,0 +1,130 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = Float32Attribute{} +) + +// Float32Attribute represents a schema attribute that is a 32-bit floating +// point number. When retrieving the value for this attribute, use +// types.Float32 as the value type unless the CustomType field is set. +// +// Use Int32Attribute for 32-bit integer attributes or NumberAttribute for +// 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point value. +// +// example_attribute = 123.45 +type Float32Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Float32Type. When retrieving data, the basetypes.Float32Valuable + // associated with this custom type must be used in place of types.Float32. + CustomType basetypes.Float32Typable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Float32Attribute. +func (a Float32Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Float32Attribute +// and all fields are equal. +func (a Float32Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Float32Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a Float32Attribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a Float32Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a Float32Attribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.Float32Type or the CustomType field value if defined. +func (a Float32Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Float32Type +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a Float32Attribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a Float32Attribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a Float32Attribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a Float32Attribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a Float32Attribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a Float32Attribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a Float32Attribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/float32_attribute_test.go b/resource/identityschema/float32_attribute_test.go new file mode 100644 index 000000000..0f906f88d --- /dev/null +++ b/resource/identityschema/float32_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestFloat32AttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.Float32Attribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.Float32Type"), + }, + "ElementKeyInt": { + attribute: identityschema.Float32Attribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.Float32Type"), + }, + "ElementKeyString": { + attribute: identityschema.Float32Attribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.Float32Type"), + }, + "ElementKeyValue": { + attribute: identityschema.Float32Attribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.Float32Type"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.Float32Attribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.Float32Attribute{}, + other: testschema.AttributeWithFloat32Validators{}, + expected: false, + }, + "equal": { + attribute: identityschema.Float32Attribute{}, + other: identityschema.Float32Attribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected string + }{ + "no-description": { + attribute: identityschema.Float32Attribute{}, + expected: "", + }, + "description": { + attribute: identityschema.Float32Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.Float32Attribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.Float32Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected attr.Type + }{ + "base": { + attribute: identityschema.Float32Attribute{}, + expected: types.Float32Type, + }, + "custom-type": { + attribute: identityschema.Float32Attribute{ + CustomType: testtypes.Float32Type{}, + }, + expected: testtypes.Float32Type{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-computed": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-optional": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-required": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.Float32Attribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.Float32Attribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.Float32Attribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/float64_attribute.go b/resource/identityschema/float64_attribute.go new file mode 100644 index 000000000..a6e33fb3c --- /dev/null +++ b/resource/identityschema/float64_attribute.go @@ -0,0 +1,130 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = Float64Attribute{} +) + +// Float64Attribute represents a schema attribute that is a 64-bit floating +// point number. When retrieving the value for this attribute, use +// types.Float64 as the value type unless the CustomType field is set. +// +// Use Int64Attribute for 64-bit integer attributes or NumberAttribute for +// 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point value. +// +// example_attribute = 123.45 +type Float64Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Float64Type. When retrieving data, the basetypes.Float64Valuable + // associated with this custom type must be used in place of types.Float64. + CustomType basetypes.Float64Typable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Float64Attribute. +func (a Float64Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Float64Attribute +// and all fields are equal. +func (a Float64Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Float64Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a Float64Attribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a Float64Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a Float64Attribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.Float64Type or the CustomType field value if defined. +func (a Float64Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Float64Type +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a Float64Attribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a Float64Attribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a Float64Attribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a Float64Attribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a Float64Attribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a Float64Attribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a Float64Attribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/float64_attribute_test.go b/resource/identityschema/float64_attribute_test.go new file mode 100644 index 000000000..e5473e7b3 --- /dev/null +++ b/resource/identityschema/float64_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.Float64Attribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.Float64Type"), + }, + "ElementKeyInt": { + attribute: identityschema.Float64Attribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.Float64Type"), + }, + "ElementKeyString": { + attribute: identityschema.Float64Attribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.Float64Type"), + }, + "ElementKeyValue": { + attribute: identityschema.Float64Attribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.Float64Type"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.Float64Attribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.Float64Attribute{}, + other: testschema.AttributeWithFloat64Validators{}, + expected: false, + }, + "equal": { + attribute: identityschema.Float64Attribute{}, + other: identityschema.Float64Attribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected string + }{ + "no-description": { + attribute: identityschema.Float64Attribute{}, + expected: "", + }, + "description": { + attribute: identityschema.Float64Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.Float64Attribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.Float64Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected attr.Type + }{ + "base": { + attribute: identityschema.Float64Attribute{}, + expected: types.Float64Type, + }, + "custom-type": { + attribute: identityschema.Float64Attribute{ + CustomType: testtypes.Float64Type{}, + }, + expected: testtypes.Float64Type{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-computed": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-optional": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-required": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.Float64Attribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.Float64Attribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.Float64Attribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/int32_attribute.go b/resource/identityschema/int32_attribute.go new file mode 100644 index 000000000..a5ef7e819 --- /dev/null +++ b/resource/identityschema/int32_attribute.go @@ -0,0 +1,130 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = Int32Attribute{} +) + +// Int32Attribute represents a schema attribute that is a 32-bit integer. +// When retrieving the value for this attribute, use types.Int32 as the value +// type unless the CustomType field is set. +// +// Use Float32Attribute for 32-bit floating point number attributes or +// NumberAttribute for 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via an integer value. +// +// example_attribute = 123 +type Int32Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Int32Type. When retrieving data, the basetypes.Int32Valuable + // associated with this custom type must be used in place of types.Int32. + CustomType basetypes.Int32Typable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Int32Attribute. +func (a Int32Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Int32Attribute +// and all fields are equal. +func (a Int32Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Int32Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a Int32Attribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a Int32Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a Int32Attribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.Int32Type or the CustomType field value if defined. +func (a Int32Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Int32Type +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a Int32Attribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a Int32Attribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a Int32Attribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a Int32Attribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a Int32Attribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a Int32Attribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a Int32Attribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/int32_attribute_test.go b/resource/identityschema/int32_attribute_test.go new file mode 100644 index 000000000..7b68255c2 --- /dev/null +++ b/resource/identityschema/int32_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestInt32AttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.Int32Attribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.Int32Type"), + }, + "ElementKeyInt": { + attribute: identityschema.Int32Attribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.Int32Type"), + }, + "ElementKeyString": { + attribute: identityschema.Int32Attribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.Int32Type"), + }, + "ElementKeyValue": { + attribute: identityschema.Int32Attribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.Int32Type"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.Int32Attribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.Int32Attribute{}, + other: testschema.AttributeWithInt32Validators{}, + expected: false, + }, + "equal": { + attribute: identityschema.Int32Attribute{}, + other: identityschema.Int32Attribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected string + }{ + "no-description": { + attribute: identityschema.Int32Attribute{}, + expected: "", + }, + "description": { + attribute: identityschema.Int32Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.Int32Attribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.Int32Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected attr.Type + }{ + "base": { + attribute: identityschema.Int32Attribute{}, + expected: types.Int32Type, + }, + "custom-type": { + attribute: identityschema.Int32Attribute{ + CustomType: testtypes.Int32Type{}, + }, + expected: testtypes.Int32Type{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-computed": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-optional": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-required": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.Int32Attribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.Int32Attribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.Int32Attribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/int64_attribute.go b/resource/identityschema/int64_attribute.go new file mode 100644 index 000000000..332a1d1c1 --- /dev/null +++ b/resource/identityschema/int64_attribute.go @@ -0,0 +1,130 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = Int64Attribute{} +) + +// Int64Attribute represents a schema attribute that is a 64-bit integer. +// When retrieving the value for this attribute, use types.Int64 as the value +// type unless the CustomType field is set. +// +// Use Float64Attribute for 64-bit floating point number attributes or +// NumberAttribute for 512-bit generic number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via an integer value. +// +// example_attribute = 123 +type Int64Attribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.Int64Type. When retrieving data, the basetypes.Int64Valuable + // associated with this custom type must be used in place of types.Int64. + CustomType basetypes.Int64Typable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a Int64Attribute. +func (a Int64Attribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a Int64Attribute +// and all fields are equal. +func (a Int64Attribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(Int64Attribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a Int64Attribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a Int64Attribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a Int64Attribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.Int64Type or the CustomType field value if defined. +func (a Int64Attribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.Int64Type +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a Int64Attribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a Int64Attribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a Int64Attribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a Int64Attribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a Int64Attribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a Int64Attribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a Int64Attribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/int64_attribute_test.go b/resource/identityschema/int64_attribute_test.go new file mode 100644 index 000000000..2bba9c500 --- /dev/null +++ b/resource/identityschema/int64_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.Int64Attribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.Int64Type"), + }, + "ElementKeyInt": { + attribute: identityschema.Int64Attribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.Int64Type"), + }, + "ElementKeyString": { + attribute: identityschema.Int64Attribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.Int64Type"), + }, + "ElementKeyValue": { + attribute: identityschema.Int64Attribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.Int64Type"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.Int64Attribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.Int64Attribute{}, + other: testschema.AttributeWithInt64Validators{}, + expected: false, + }, + "equal": { + attribute: identityschema.Int64Attribute{}, + other: identityschema.Int64Attribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected string + }{ + "no-description": { + attribute: identityschema.Int64Attribute{}, + expected: "", + }, + "description": { + attribute: identityschema.Int64Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.Int64Attribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.Int64Attribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected attr.Type + }{ + "base": { + attribute: identityschema.Int64Attribute{}, + expected: types.Int64Type, + }, + "custom-type": { + attribute: identityschema.Int64Attribute{ + CustomType: testtypes.Int64Type{}, + }, + expected: testtypes.Int64Type{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-computed": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-optional": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-required": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.Int64Attribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.Int64Attribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.Int64Attribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/list_attribute.go b/resource/identityschema/list_attribute.go new file mode 100644 index 000000000..ef396c248 --- /dev/null +++ b/resource/identityschema/list_attribute.go @@ -0,0 +1,169 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = ListAttribute{} + _ fwschema.AttributeWithValidateImplementation = ListAttribute{} +) + +// ListAttribute represents a schema attribute that is a list with a single +// element type. When retrieving the value for this attribute, use types.List +// as the value type unless the CustomType field is set. The ElementType field +// must be set. +// +// In identity schemas, ListAttribute is only permitted to have a primitive ElementType, +// which are: +// - types.BoolType +// - types.Float32Type +// - types.Float64Type +// - types.Int32Type +// - types.Int64Type +// - types.NumberType +// - types.StringType +// +// Terraform configurations configure this attribute using expressions that +// return a list or directly via square brace syntax. +// +// # list of strings +// example_attribute = ["first", "second"] +type ListAttribute struct { + // ElementType is the type for all elements of the list. This field must be + // set. + // + // ElementType must be a primitive, which are: + // - types.BoolType + // - types.Float32Type + // - types.Float64Type + // - types.Int32Type + // - types.Int64Type + // - types.NumberType + // - types.StringType + ElementType attr.Type + + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.ListType. When retrieving data, the basetypes.ListValuable + // associated with this custom type must be used in place of types.List. + CustomType basetypes.ListTypable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep returns the result of stepping into a list +// index or an error. +func (a ListAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a ListAttribute +// and all fields are equal. +func (a ListAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(ListAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a ListAttribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a ListAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a ListAttribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.ListType or the CustomType field value if defined. +func (a ListAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.ListType{ + ElemType: a.ElementType, + } +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a ListAttribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a ListAttribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a ListAttribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a ListAttribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a ListAttribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a ListAttribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a ListAttribute) IsOptionalForImport() bool { + return a.OptionalForImport +} + +// ValidateImplementation contains logic for validating the +// provider-defined implementation of the attribute to prevent unexpected +// errors or panics. This logic runs during the GetResourceIdentitySchemas RPC and +// should never include false positives. +func (a ListAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { + if a.CustomType == nil && a.ElementType == nil { + resp.Diagnostics.Append(fwschema.AttributeMissingElementTypeDiag(req.Path)) + } + + // TODO:ResourceIdentity: Write validation + tests that ensure the element type only contains primitive elements (bool, string, number) +} diff --git a/resource/identityschema/list_attribute_test.go b/resource/identityschema/list_attribute_test.go new file mode 100644 index 000000000..45fc40c17 --- /dev/null +++ b/resource/identityschema/list_attribute_test.go @@ -0,0 +1,497 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestListAttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply step tftypes.AttributeName to ListType"), + }, + "ElementKeyInt": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + step: tftypes.ElementKeyInt(1), + expected: types.StringType, + expectedError: nil, + }, + "ElementKeyString": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply step tftypes.ElementKeyString to ListType"), + }, + "ElementKeyValue": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply step tftypes.ElementKeyValue to ListType"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + other: testschema.AttributeWithListValidators{}, + expected: false, + }, + "different-element-type": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + other: identityschema.ListAttribute{ElementType: types.BoolType}, + expected: false, + }, + "equal": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + other: identityschema.ListAttribute{ElementType: types.StringType}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected string + }{ + "no-description": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: "", + }, + "description": { + attribute: identityschema.ListAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.ListAttribute{ + ElementType: types.StringType, + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected attr.Type + }{ + "base": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: types.ListType{ElemType: types.StringType}, + }, + "custom-type": { + attribute: identityschema.ListAttribute{ + CustomType: testtypes.ListType{ListType: types.ListType{ElemType: types.StringType}}, + }, + expected: testtypes.ListType{ListType: types.ListType{ElemType: types.StringType}}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-computed": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-optional": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-required": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.ListAttribute{ + ElementType: types.StringType, + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.ListAttribute{ElementType: types.StringType}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.ListAttribute{ + ElementType: types.StringType, + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeValidateImplementation(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.ListAttribute + request fwschema.ValidateImplementationRequest + expected *fwschema.ValidateImplementationResponse + }{ + "elementtype": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.StringType, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-missing": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is missing the CustomType or ElementType field on a collection Attribute. "+ + "One of these fields is required to prevent other unexpected errors or panics.", + ), + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := &fwschema.ValidateImplementationResponse{} + testCase.attribute.ValidateImplementation(context.Background(), testCase.request, got) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/number_attribute.go b/resource/identityschema/number_attribute.go new file mode 100644 index 000000000..d1533477a --- /dev/null +++ b/resource/identityschema/number_attribute.go @@ -0,0 +1,131 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = NumberAttribute{} +) + +// NumberAttribute represents a schema attribute that is a generic number with +// up to 512 bits of floating point or integer precision. When retrieving the +// value for this attribute, use types.Number as the value type unless the +// CustomType field is set. +// +// Use Float64Attribute for 64-bit floating point number attributes or +// Int64Attribute for 64-bit integer number attributes. +// +// Terraform configurations configure this attribute using expressions that +// return a number or directly via a floating point or integer value. +// +// example_attribute = 123 +type NumberAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.NumberType. When retrieving data, the basetypes.NumberValuable + // associated with this custom type must be used in place of types.Number. + CustomType basetypes.NumberTypable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a NumberAttribute. +func (a NumberAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a NumberAttribute +// and all fields are equal. +func (a NumberAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(NumberAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a NumberAttribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a NumberAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a NumberAttribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.NumberType or the CustomType field value if defined. +func (a NumberAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.NumberType +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a NumberAttribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a NumberAttribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a NumberAttribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a NumberAttribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a NumberAttribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a NumberAttribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a NumberAttribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/number_attribute_test.go b/resource/identityschema/number_attribute_test.go new file mode 100644 index 000000000..9597643cd --- /dev/null +++ b/resource/identityschema/number_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestNumberAttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.NumberAttribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.NumberType"), + }, + "ElementKeyInt": { + attribute: identityschema.NumberAttribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.NumberType"), + }, + "ElementKeyString": { + attribute: identityschema.NumberAttribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.NumberType"), + }, + "ElementKeyValue": { + attribute: identityschema.NumberAttribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.NumberType"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.NumberAttribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.NumberAttribute{}, + other: testschema.AttributeWithNumberValidators{}, + expected: false, + }, + "equal": { + attribute: identityschema.NumberAttribute{}, + other: identityschema.NumberAttribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected string + }{ + "no-description": { + attribute: identityschema.NumberAttribute{}, + expected: "", + }, + "description": { + attribute: identityschema.NumberAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.NumberAttribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.NumberAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected attr.Type + }{ + "base": { + attribute: identityschema.NumberAttribute{}, + expected: types.NumberType, + }, + "custom-type": { + attribute: identityschema.NumberAttribute{ + CustomType: testtypes.NumberType{}, + }, + expected: testtypes.NumberType{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-computed": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-optional": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-required": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.NumberAttribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.NumberAttribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.NumberAttribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/schema.go b/resource/identityschema/schema.go new file mode 100644 index 000000000..6d68aff9a --- /dev/null +++ b/resource/identityschema/schema.go @@ -0,0 +1,145 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/path" +) + +// Schema must satify the fwschema.Schema interface. +var _ fwschema.Schema = Schema{} + +// Schema defines the structure and value types of resource identity data. This type +// is used as the resource.IdentitySchemaResponse type IdentitySchema field, which is +// implemented by the resource.ResourceWithIdentity type IdentitySchema method. +type Schema struct { + // Attributes is the mapping of underlying attribute names to attribute + // definitions. + // + // Names must only contain lowercase letters, numbers, and underscores. + Attributes map[string]Attribute + + // Version indicates the current version of the resource identity schema. Resource + // identity schema versioning enables identity state upgrades in conjunction with the + // [resource.ResourceWithUpgradeIdentity] interface. Versioning is only + // required if there is a breaking change involving existing identity state data, + // such as changing an attribute type in a manner that is incompatible with the Terraform type. + // + // Versions are conventionally only incremented by one each release. + Version int64 +} + +// ApplyTerraform5AttributePathStep applies the given AttributePathStep to the +// schema. +func (s Schema) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (any, error) { + return fwschema.SchemaApplyTerraform5AttributePathStep(s, step) +} + +// AttributeAtPath returns the Attribute at the passed path. If the path points +// to an element or attribute of a complex type, rather than to an Attribute, +// it will return an ErrPathInsideAtomicAttribute error. +func (s Schema) AttributeAtPath(ctx context.Context, p path.Path) (fwschema.Attribute, diag.Diagnostics) { + return fwschema.SchemaAttributeAtPath(ctx, s, p) +} + +// AttributeAtPath returns the Attribute at the passed path. If the path points +// to an element or attribute of a complex type, rather than to an Attribute, +// it will return an ErrPathInsideAtomicAttribute error. +func (s Schema) AttributeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (fwschema.Attribute, error) { + return fwschema.SchemaAttributeAtTerraformPath(ctx, s, p) +} + +// GetAttributes returns the Attributes field value. +func (s Schema) GetAttributes() map[string]fwschema.Attribute { + return schemaAttributes(s.Attributes) +} + +// GetBlocks returns an empty map as it's not relevant for identity schemas. +func (s Schema) GetBlocks() map[string]fwschema.Block { + return map[string]fwschema.Block{} +} + +// GetDeprecationMessage returns an empty string as identity schemas cannot +// surface deprecation messages. +func (s Schema) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns an empty string as identity schemas cannot +// surface descriptions. +func (s Schema) GetDescription() string { + return "" +} + +// GetMarkdownDescription returns an empty string as identity schemas cannot +// surface descriptions. +func (s Schema) GetMarkdownDescription() string { + return "" +} + +// GetVersion returns the Version field value. +func (s Schema) GetVersion() int64 { + return s.Version +} + +// Type returns the framework type of the schema. +func (s Schema) Type() attr.Type { + return fwschema.SchemaType(s) +} + +// TypeAtPath returns the framework type at the given schema path. +func (s Schema) TypeAtPath(ctx context.Context, p path.Path) (attr.Type, diag.Diagnostics) { + return fwschema.SchemaTypeAtPath(ctx, s, p) +} + +// TypeAtTerraformPath returns the framework type at the given tftypes path. +func (s Schema) TypeAtTerraformPath(ctx context.Context, p *tftypes.AttributePath) (attr.Type, error) { + return fwschema.SchemaTypeAtTerraformPath(ctx, s, p) +} + +// Validate verifies that the schema is not using a reserved field name for a top-level attribute. +// +// Deprecated: Use the ValidateImplementation method instead. +func (s Schema) Validate() diag.Diagnostics { + return s.ValidateImplementation(context.Background()) +} + +// ValidateImplementation contains logic for validating the provider-defined +// implementation of the schema and underlying attributes and blocks to prevent +// unexpected errors or panics. This logic runs during the +// GetResourceIdentitySchemas RPC, or via provider-defined unit testing, and should +// never include false positives. +func (s Schema) ValidateImplementation(ctx context.Context) diag.Diagnostics { + var diags diag.Diagnostics + + for attributeName, attribute := range s.GetAttributes() { + req := fwschema.ValidateImplementationRequest{ + Name: attributeName, + Path: path.Root(attributeName), + } + + diags.Append(fwschema.IsReservedResourceAttributeName(req.Name, req.Path)...) + diags.Append(fwschema.ValidateAttributeImplementation(ctx, attribute, req)...) + } + + return diags +} + +// schemaAttributes is a identity to fwschema type conversion function. +func schemaAttributes(attributes map[string]Attribute) map[string]fwschema.Attribute { + result := make(map[string]fwschema.Attribute, len(attributes)) + + for name, attribute := range attributes { + result[name] = attribute + } + + return result +} diff --git a/resource/identityschema/schema_test.go b/resource/identityschema/schema_test.go new file mode 100644 index 000000000..394cdd426 --- /dev/null +++ b/resource/identityschema/schema_test.go @@ -0,0 +1,915 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestSchemaApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName-attribute": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + step: tftypes.AttributeName("testattr"), + expected: identityschema.StringAttribute{}, + expectedError: nil, + }, + "AttributeName-missing": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + step: tftypes.AttributeName("other"), + expected: nil, + expectedError: fmt.Errorf("could not find attribute or block \"other\" in schema"), + }, + "ElementKeyInt": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to schema"), + }, + "ElementKeyString": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to schema"), + }, + "ElementKeyValue": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to schema"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.schema.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaAttributeAtPath(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + path path.Path + expected fwschema.Attribute + expectedDiags diag.Diagnostics + }{ + "empty-root": { + schema: identityschema.Schema{}, + path: path.Empty(), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Invalid Schema Path", + "When attempting to get the framework attribute associated with a schema path, an unexpected error was returned. "+ + "This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: \n"+ + "Original Error: got unexpected type identityschema.Schema", + ), + }, + }, + "root": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: path.Empty(), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Invalid Schema Path", + "When attempting to get the framework attribute associated with a schema path, an unexpected error was returned. "+ + "This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: \n"+ + "Original Error: got unexpected type identityschema.Schema", + ), + }, + }, + "WithAttributeName-attribute": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "other": identityschema.BoolAttribute{}, + "test": identityschema.StringAttribute{}, + }, + }, + path: path.Root("test"), + expected: identityschema.StringAttribute{}, + }, + "WithElementKeyInt": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: path.Empty().AtListIndex(0), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtListIndex(0), + "Invalid Schema Path", + "When attempting to get the framework attribute associated with a schema path, an unexpected error was returned. "+ + "This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [0]\n"+ + "Original Error: ElementKeyInt(0) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyInt to schema", + ), + }, + }, + "WithElementKeyString": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: path.Empty().AtMapKey("test"), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtMapKey("test"), + "Invalid Schema Path", + "When attempting to get the framework attribute associated with a schema path, an unexpected error was returned. "+ + "This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [\"test\"]\n"+ + "Original Error: ElementKeyString(\"test\") still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyString to schema", + ), + }, + }, + "WithElementKeyValue": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: path.Empty().AtSetValue(types.StringValue("test")), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtSetValue(types.StringValue("test")), + "Invalid Schema Path", + "When attempting to get the framework attribute associated with a schema path, an unexpected error was returned. "+ + "This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [Value(\"test\")]\n"+ + "Original Error: ElementKeyValue(tftypes.String<\"test\">) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyValue to schema", + ), + }, + }, + } + + for name, tc := range testCases { + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := tc.schema.AttributeAtPath(context.Background(), tc.path) + + if diff := cmp.Diff(diags, tc.expectedDiags); diff != "" { + t.Errorf("Unexpected diagnostics (+wanted, -got): %s", diff) + } + + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected result (+wanted, -got): %s", diff) + } + }) + } +} + +func TestSchemaAttributeAtTerraformPath(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + path *tftypes.AttributePath + expected fwschema.Attribute + expectedErr string + }{ + "empty-root": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath(), + expected: nil, + expectedErr: "got unexpected type identityschema.Schema", + }, + "empty-nil": { + schema: identityschema.Schema{}, + path: nil, + expected: nil, + expectedErr: "got unexpected type identityschema.Schema", + }, + "root": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath(), + expected: nil, + expectedErr: "got unexpected type identityschema.Schema", + }, + "nil": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: nil, + expected: nil, + expectedErr: "got unexpected type identityschema.Schema", + }, + "WithAttributeName-attribute": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "other": identityschema.BoolAttribute{}, + "test": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath().WithAttributeName("test"), + expected: identityschema.StringAttribute{}, + }, + "WithElementKeyInt": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath().WithElementKeyInt(0), + expected: nil, + expectedErr: "ElementKeyInt(0) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyInt to schema", + }, + "WithElementKeyString": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath().WithElementKeyString("test"), + expected: nil, + expectedErr: "ElementKeyString(\"test\") still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyString to schema", + }, + "WithElementKeyValue": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath().WithElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedErr: "ElementKeyValue(tftypes.String<\"test\">) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyValue to schema", + }, + } + + for name, tc := range testCases { + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := tc.schema.AttributeAtTerraformPath(context.Background(), tc.path) + + if err != nil { + if tc.expectedErr == "" { + t.Errorf("Unexpected error: %s", err) + return + } + if err.Error() != tc.expectedErr { + t.Errorf("Expected error to be %q, got %q", tc.expectedErr, err.Error()) + return + } + // got expected error + return + } + + if err == nil && tc.expectedErr != "" { + t.Errorf("Expected error to be %q, got nil", tc.expectedErr) + return + } + + if diff := cmp.Diff(got, tc.expected); diff != "" { + t.Errorf("Unexpected result (+wanted, -got): %s", diff) + } + }) + } +} + +func TestSchemaGetAttributes(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected map[string]fwschema.Attribute + }{ + "no-attributes": { + schema: identityschema.Schema{}, + expected: map[string]fwschema.Attribute{}, + }, + "attributes": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr1": identityschema.StringAttribute{}, + "testattr2": identityschema.StringAttribute{}, + }, + }, + expected: map[string]fwschema.Attribute{ + "testattr1": identityschema.StringAttribute{}, + "testattr2": identityschema.StringAttribute{}, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetAttributes() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaGetBlocks(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected map[string]fwschema.Block + }{ + "no-blocks": { + schema: identityschema.Schema{}, + expected: map[string]fwschema.Block{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetBlocks() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected string + }{ + "no-deprecation-message": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected string + }{ + "no-description": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected string + }{ + "no-markdown-description": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaGetVersion(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected int64 + }{ + "no-version": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + expected: 0, + }, + "version": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + Version: 1, + }, + expected: 1, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.GetVersion() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expected attr.Type + }{ + "base": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "testattr": identityschema.StringAttribute{}, + }, + }, + expected: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "testattr": types.StringType, + }, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.schema.Type() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaTypeAtPath(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + path path.Path + expected attr.Type + expectedDiags diag.Diagnostics + }{ + "empty-schema-empty-path": { + schema: identityschema.Schema{}, + path: path.Empty(), + expected: types.ObjectType{}, + }, + "empty-path": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "bool": identityschema.BoolAttribute{}, + "string": identityschema.StringAttribute{}, + }, + }, + path: path.Empty(), + expected: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "bool": types.BoolType, + "string": types.StringType, + }, + }, + }, + "AttributeName-Attribute": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "bool": identityschema.BoolAttribute{}, + "string": identityschema.StringAttribute{}, + }, + }, + path: path.Root("string"), + expected: types.StringType, + }, + "AttributeName-non-existent": { + schema: identityschema.Schema{}, + path: path.Root("non-existent"), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("non-existent"), + "Invalid Schema Path", + "When attempting to get the framework type associated with a schema path, an unexpected error was returned. This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: non-existent\n"+ + "Original Error: AttributeName(\"non-existent\") still remains in the path: could not find attribute or block \"non-existent\" in schema", + ), + }, + }, + "ElementKeyInt": { + schema: identityschema.Schema{}, + path: path.Empty().AtListIndex(0), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtListIndex(0), + "Invalid Schema Path", + "When attempting to get the framework type associated with a schema path, an unexpected error was returned. This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [0]\n"+ + "Original Error: ElementKeyInt(0) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyInt to schema", + ), + }, + }, + "ElementKeyString": { + schema: identityschema.Schema{}, + path: path.Empty().AtMapKey("invalid"), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtMapKey("invalid"), + "Invalid Schema Path", + "When attempting to get the framework type associated with a schema path, an unexpected error was returned. This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [\"invalid\"]\n"+ + "Original Error: ElementKeyString(\"invalid\") still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyString to schema", + ), + }, + }, + "ElementKeyValue": { + schema: identityschema.Schema{}, + path: path.Empty().AtSetValue(types.StringNull()), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtSetValue(types.StringNull()), + "Invalid Schema Path", + "When attempting to get the framework type associated with a schema path, an unexpected error was returned. This is always an issue with the provider. Please report this to the provider developers.\n\n"+ + "Path: [Value()]\n"+ + "Original Error: ElementKeyValue(tftypes.String) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyValue to schema", + ), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := testCase.schema.TypeAtPath(context.Background(), testCase.path) + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaTypeAtTerraformPath(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + path *tftypes.AttributePath + expected attr.Type + expectedError error + }{ + "empty-schema-nil-path": { + schema: identityschema.Schema{}, + path: nil, + expected: types.ObjectType{}, + }, + "empty-schema-empty-path": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath(), + expected: types.ObjectType{}, + }, + "nil-path": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "bool": identityschema.BoolAttribute{}, + "string": identityschema.StringAttribute{}, + }, + }, + path: nil, + expected: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "bool": types.BoolType, + "string": types.StringType, + }, + }, + }, + "empty-path": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "bool": identityschema.BoolAttribute{}, + "string": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath(), + expected: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "bool": types.BoolType, + "string": types.StringType, + }, + }, + }, + "AttributeName-Attribute": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "bool": identityschema.BoolAttribute{}, + "string": identityschema.StringAttribute{}, + }, + }, + path: tftypes.NewAttributePath().WithAttributeName("string"), + expected: types.StringType, + }, + "AttributeName-non-existent": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath().WithAttributeName("non-existent"), + expectedError: fmt.Errorf("AttributeName(\"non-existent\") still remains in the path: could not find attribute or block \"non-existent\" in schema"), + }, + "ElementKeyInt": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath().WithElementKeyInt(0), + expectedError: fmt.Errorf("ElementKeyInt(0) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyInt to schema"), + }, + "ElementKeyString": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath().WithElementKeyString("invalid"), + expectedError: fmt.Errorf("ElementKeyString(\"invalid\") still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyString to schema"), + }, + "ElementKeyValue": { + schema: identityschema.Schema{}, + path: tftypes.NewAttributePath().WithElementKeyValue(tftypes.NewValue(tftypes.String, nil)), + expectedError: fmt.Errorf("ElementKeyValue(tftypes.String) still remains in the path: cannot apply AttributePathStep tftypes.ElementKeyValue to schema"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.schema.TypeAtTerraformPath(context.Background(), testCase.path) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSchemaValidate(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expectedDiags diag.Diagnostics + }{ + "empty-schema": { + schema: identityschema.Schema{}, + }, + "validate-implementation-error": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "depends_on": identityschema.StringAttribute{}, + }, + }, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Reserved Root Attribute/Block Name", + "When validating the resource or data source schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"depends_on\" is a reserved root attribute/block name. "+ + "This is to prevent practitioners from needing special Terraform configuration syntax.", + ), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := testCase.schema.Validate() + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("Unexpected diagnostics (+wanted, -got): %s", diff) + } + }) + } +} + +func TestSchemaValidateImplementation(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + schema identityschema.Schema + expectedDiags diag.Diagnostics + }{ + "empty-schema": { + schema: identityschema.Schema{}, + }, + "attribute-using-reserved-field-name": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "depends_on": identityschema.StringAttribute{}, + }, + }, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Reserved Root Attribute/Block Name", + "When validating the resource or data source schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"depends_on\" is a reserved root attribute/block name. "+ + "This is to prevent practitioners from needing special Terraform configuration syntax.", + ), + }, + }, + "attribute-using-invalid-field-name": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "^": identityschema.StringAttribute{}, + }, + }, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute/Block Name", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"^\" at schema path \"^\" is an invalid attribute/block name. "+ + "Names must only contain lowercase alphanumeric characters (a-z, 0-9) and underscores (_).", + ), + }, + }, + "attribute-with-validate-attribute-implementation-error": { + schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test": identityschema.ListAttribute{ + RequiredForImport: true, + }, + }, + }, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" is missing the CustomType or ElementType field on a collection Attribute. "+ + "One of these fields is required to prevent other unexpected errors or panics.", + ), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := testCase.schema.ValidateImplementation(context.Background()) + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("Unexpected diagnostics (+wanted, -got): %s", diff) + } + }) + } +} diff --git a/resource/identityschema/string_attribute.go b/resource/identityschema/string_attribute.go new file mode 100644 index 000000000..3061b89cf --- /dev/null +++ b/resource/identityschema/string_attribute.go @@ -0,0 +1,127 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema + +import ( + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +// Ensure the implementation satisfies the desired interfaces. +var ( + _ Attribute = StringAttribute{} +) + +// StringAttribute represents a schema attribute that is a string. When +// retrieving the value for this attribute, use types.String as the value type +// unless the CustomType field is set. +// +// Terraform configurations configure this attribute using expressions that +// return a string or directly via double quote syntax. +// +// example_attribute = "value" +type StringAttribute struct { + // CustomType enables the use of a custom attribute type in place of the + // default basetypes.StringType. When retrieving data, the basetypes.StringValuable + // associated with this custom type must be used in place of types.String. + CustomType basetypes.StringTypable + + // RequiredForImport indicates whether the practitioner must enter a value for + // this attribute when importing a managed resource by this identity. + // RequiredForImport and OptionalForImport cannot both be true. + RequiredForImport bool + + // OptionalForImport indicates whether the practitioner can choose to enter a value + // for this attribute when importing a managed resource by this identity. + // OptionalForImport and RequiredForImport cannot both be true. + OptionalForImport bool + + // Description is used in various tooling, like the language server or the documentation + // generator, to give practitioners more information about what this attribute is, + // what it's for, and how it should be used. It can be written as plain text with no + // special formatting, or formatted as Markdown. + Description string +} + +// ApplyTerraform5AttributePathStep always returns an error as it is not +// possible to step further into a StringAttribute. +func (a StringAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) { + return a.GetType().ApplyTerraform5AttributePathStep(step) +} + +// Equal returns true if the given Attribute is a StringAttribute +// and all fields are equal. +func (a StringAttribute) Equal(o fwschema.Attribute) bool { + if _, ok := o.(StringAttribute); !ok { + return false + } + + return fwschema.AttributesEqual(a, o) +} + +// GetDeprecationMessage returns an empty string as identity attributes cannot +// surface deprecation messages. +func (a StringAttribute) GetDeprecationMessage() string { + return "" +} + +// GetDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain plaintext or Markdown. +func (a StringAttribute) GetDescription() string { + return a.Description +} + +// GetMarkdownDescription returns the Description field value. For identity attributes, +// there is only a single description field that is permitted to contain Markdown or plaintext. +func (a StringAttribute) GetMarkdownDescription() string { + return a.Description +} + +// GetType returns types.StringType or the CustomType field value if defined. +func (a StringAttribute) GetType() attr.Type { + if a.CustomType != nil { + return a.CustomType + } + + return types.StringType +} + +// IsComputed returns false as it's not relevant for identity schemas. +func (a StringAttribute) IsComputed() bool { + return false +} + +// IsOptional returns false as it's not relevant for identity schemas. +func (a StringAttribute) IsOptional() bool { + return false +} + +// IsRequired returns false as it's not relevant for identity schemas. +func (a StringAttribute) IsRequired() bool { + return false +} + +// IsSensitive returns false as it's not relevant for identity schemas. +func (a StringAttribute) IsSensitive() bool { + return false +} + +// IsWriteOnly returns false as it's not relevant for identity schemas. +func (a StringAttribute) IsWriteOnly() bool { + return false +} + +// IsRequiredForImport returns the RequiredForImport field value. +func (a StringAttribute) IsRequiredForImport() bool { + return a.RequiredForImport +} + +// IsOptionalForImport returns the OptionalForImport field value. +func (a StringAttribute) IsOptionalForImport() bool { + return a.OptionalForImport +} diff --git a/resource/identityschema/string_attribute_test.go b/resource/identityschema/string_attribute_test.go new file mode 100644 index 000000000..18f6666e6 --- /dev/null +++ b/resource/identityschema/string_attribute_test.go @@ -0,0 +1,431 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package identityschema_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestStringAttributeApplyTerraform5AttributePathStep(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + step tftypes.AttributePathStep + expected any + expectedError error + }{ + "AttributeName": { + attribute: identityschema.StringAttribute{}, + step: tftypes.AttributeName("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.AttributeName to basetypes.StringType"), + }, + "ElementKeyInt": { + attribute: identityschema.StringAttribute{}, + step: tftypes.ElementKeyInt(1), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyInt to basetypes.StringType"), + }, + "ElementKeyString": { + attribute: identityschema.StringAttribute{}, + step: tftypes.ElementKeyString("test"), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyString to basetypes.StringType"), + }, + "ElementKeyValue": { + attribute: identityschema.StringAttribute{}, + step: tftypes.ElementKeyValue(tftypes.NewValue(tftypes.String, "test")), + expected: nil, + expectedError: fmt.Errorf("cannot apply AttributePathStep tftypes.ElementKeyValue to basetypes.StringType"), + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, err := testCase.attribute.ApplyTerraform5AttributePathStep(testCase.step) + + if err != nil { + if testCase.expectedError == nil { + t.Fatalf("expected no error, got: %s", err) + } + + if !strings.Contains(err.Error(), testCase.expectedError.Error()) { + t.Fatalf("expected error %q, got: %s", testCase.expectedError, err) + } + } + + if err == nil && testCase.expectedError != nil { + t.Fatalf("got no error, expected: %s", testCase.expectedError) + } + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeGetDeprecationMessage(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected string + }{ + "no-deprecation-message": { + attribute: identityschema.StringAttribute{}, + expected: "", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDeprecationMessage() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeEqual(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + other fwschema.Attribute + expected bool + }{ + "different-type": { + attribute: identityschema.StringAttribute{}, + other: testschema.AttributeWithStringValidators{}, + expected: false, + }, + "equal": { + attribute: identityschema.StringAttribute{}, + other: identityschema.StringAttribute{}, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.Equal(testCase.other) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeGetDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected string + }{ + "no-description": { + attribute: identityschema.StringAttribute{}, + expected: "", + }, + "description": { + attribute: identityschema.StringAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeGetMarkdownDescription(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected string + }{ + "no-markdown-description": { + attribute: identityschema.StringAttribute{}, + expected: "", + }, + "markdown-description-from-description": { + attribute: identityschema.StringAttribute{ + Description: "test description", + }, + expected: "test description", + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetMarkdownDescription() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeGetType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected attr.Type + }{ + "base": { + attribute: identityschema.StringAttribute{}, + expected: types.StringType, + }, + "custom-type": { + attribute: identityschema.StringAttribute{ + CustomType: testtypes.StringType{}, + }, + expected: testtypes.StringType{}, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.GetType() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsComputed(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-computed": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsComputed() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptional(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-optional": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptional() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsRequired(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-required": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequired() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsSensitive(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-sensitive": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsSensitive() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsWriteOnly(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-writeOnly": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsWriteOnly() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + "requiredForImport": { + attribute: identityschema.StringAttribute{ + RequiredForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute identityschema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: identityschema.StringAttribute{}, + expected: false, + }, + "optionalForImport": { + attribute: identityschema.StringAttribute{ + OptionalForImport: true, + }, + expected: true, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/resource.go b/resource/resource.go index 7113d4bf5..f99824023 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -196,3 +196,13 @@ type ResourceWithValidateConfig interface { // ValidateConfig performs the validation. ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse) } + +// ResourceWithIdentity is an interface type that extends Resource to implement managed resource identity. +// +// TODO:ResourceIdentity: Add more documentation here to describe what identity is used for. +type ResourceWithIdentity interface { + Resource + + // IdentitySchema should return the identity schema for this resource. + IdentitySchema(context.Context, IdentitySchemaRequest, *IdentitySchemaResponse) +} diff --git a/resource/schema/bool_attribute.go b/resource/schema/bool_attribute.go index fa80f565c..3cdbc0e9f 100644 --- a/resource/schema/bool_attribute.go +++ b/resource/schema/bool_attribute.go @@ -244,6 +244,18 @@ func (a BoolAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a BoolAttribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/bool_attribute_test.go b/resource/schema/bool_attribute_test.go index 9f2d2e863..e260c76d6 100644 --- a/resource/schema/bool_attribute_test.go +++ b/resource/schema/bool_attribute_test.go @@ -580,3 +580,55 @@ func TestBoolAttributeValidateImplementation(t *testing.T) { }) } } + +func TestBoolAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestBoolAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.BoolAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.BoolAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/dynamic_attribute.go b/resource/schema/dynamic_attribute.go index e06600ab4..b7b4f38b5 100644 --- a/resource/schema/dynamic_attribute.go +++ b/resource/schema/dynamic_attribute.go @@ -230,6 +230,18 @@ func (a DynamicAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a DynamicAttribute) IsOptionalForImport() bool { + return false +} + // DynamicDefaultValue returns the Default field value. func (a DynamicAttribute) DynamicDefaultValue() defaults.Dynamic { return a.Default diff --git a/resource/schema/dynamic_attribute_test.go b/resource/schema/dynamic_attribute_test.go index c1340d493..4bacf972d 100644 --- a/resource/schema/dynamic_attribute_test.go +++ b/resource/schema/dynamic_attribute_test.go @@ -580,3 +580,55 @@ func TestDynamicAttributeValidateImplementation(t *testing.T) { }) } } + +func TestDynamicAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestDynamicAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.DynamicAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.DynamicAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/float32_attribute.go b/resource/schema/float32_attribute.go index 3064b4ed9..acd37a157 100644 --- a/resource/schema/float32_attribute.go +++ b/resource/schema/float32_attribute.go @@ -247,6 +247,18 @@ func (a Float32Attribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float32Attribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/float32_attribute_test.go b/resource/schema/float32_attribute_test.go index 4671b082f..18b8d1541 100644 --- a/resource/schema/float32_attribute_test.go +++ b/resource/schema/float32_attribute_test.go @@ -580,3 +580,55 @@ func TestFloat32AttributeValidateImplementation(t *testing.T) { }) } } + +func TestFloat32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/float64_attribute.go b/resource/schema/float64_attribute.go index 205af3f98..8db8fb0be 100644 --- a/resource/schema/float64_attribute.go +++ b/resource/schema/float64_attribute.go @@ -247,6 +247,18 @@ func (a Float64Attribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Float64Attribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/float64_attribute_test.go b/resource/schema/float64_attribute_test.go index 0a42d4770..81d2ea52b 100644 --- a/resource/schema/float64_attribute_test.go +++ b/resource/schema/float64_attribute_test.go @@ -580,3 +580,55 @@ func TestFloat64AttributeValidateImplementation(t *testing.T) { }) } } + +func TestFloat64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestFloat64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Float64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Float64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/int32_attribute.go b/resource/schema/int32_attribute.go index d3f97d60b..5426a5743 100644 --- a/resource/schema/int32_attribute.go +++ b/resource/schema/int32_attribute.go @@ -247,6 +247,18 @@ func (a Int32Attribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int32Attribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/int32_attribute_test.go b/resource/schema/int32_attribute_test.go index a151c9406..2cd74f5d1 100644 --- a/resource/schema/int32_attribute_test.go +++ b/resource/schema/int32_attribute_test.go @@ -580,3 +580,55 @@ func TestInt32AttributeValidateImplementation(t *testing.T) { }) } } + +func TestInt32AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt32AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int32Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int32Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/int64_attribute.go b/resource/schema/int64_attribute.go index c65eb41fa..b700f2cca 100644 --- a/resource/schema/int64_attribute.go +++ b/resource/schema/int64_attribute.go @@ -247,6 +247,18 @@ func (a Int64Attribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a Int64Attribute) IsOptionalForImport() bool { + return false +} + // ValidateImplementation contains logic for validating the // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetProviderSchema RPC and diff --git a/resource/schema/int64_attribute_test.go b/resource/schema/int64_attribute_test.go index 1ce11e722..36b29096e 100644 --- a/resource/schema/int64_attribute_test.go +++ b/resource/schema/int64_attribute_test.go @@ -580,3 +580,55 @@ func TestInt64AttributeValidateImplementation(t *testing.T) { }) } } + +func TestInt64AttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestInt64AttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.Int64Attribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.Int64Attribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/list_attribute.go b/resource/schema/list_attribute.go index 9c1536dbe..29419ebd1 100644 --- a/resource/schema/list_attribute.go +++ b/resource/schema/list_attribute.go @@ -247,6 +247,18 @@ func (a ListAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListAttribute) IsOptionalForImport() bool { + return false +} + // ListDefaultValue returns the Default field value. func (a ListAttribute) ListDefaultValue() defaults.List { return a.Default diff --git a/resource/schema/list_attribute_test.go b/resource/schema/list_attribute_test.go index 1be15bbb7..788ee84fe 100644 --- a/resource/schema/list_attribute_test.go +++ b/resource/schema/list_attribute_test.go @@ -732,3 +732,55 @@ func TestListAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/list_nested_attribute.go b/resource/schema/list_nested_attribute.go index ee1845bb5..76a4e3cdb 100644 --- a/resource/schema/list_nested_attribute.go +++ b/resource/schema/list_nested_attribute.go @@ -278,6 +278,18 @@ func (a ListNestedAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ListNestedAttribute) IsOptionalForImport() bool { + return false +} + // ListDefaultValue returns the Default field value. func (a ListNestedAttribute) ListDefaultValue() defaults.List { return a.Default diff --git a/resource/schema/list_nested_attribute_test.go b/resource/schema/list_nested_attribute_test.go index 258d7cf44..e1eaa3683 100644 --- a/resource/schema/list_nested_attribute_test.go +++ b/resource/schema/list_nested_attribute_test.go @@ -1016,3 +1016,55 @@ func TestListNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestListNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestListNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ListNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ListNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/map_attribute.go b/resource/schema/map_attribute.go index f76a295c3..837b31100 100644 --- a/resource/schema/map_attribute.go +++ b/resource/schema/map_attribute.go @@ -250,6 +250,18 @@ func (a MapAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapAttribute) IsOptionalForImport() bool { + return false +} + // MapDefaultValue returns the Default field value. func (a MapAttribute) MapDefaultValue() defaults.Map { return a.Default diff --git a/resource/schema/map_attribute_test.go b/resource/schema/map_attribute_test.go index b62293350..ce5bfb7da 100644 --- a/resource/schema/map_attribute_test.go +++ b/resource/schema/map_attribute_test.go @@ -731,3 +731,55 @@ func TestMapAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/map_nested_attribute.go b/resource/schema/map_nested_attribute.go index db868f726..cdea0f5b0 100644 --- a/resource/schema/map_nested_attribute.go +++ b/resource/schema/map_nested_attribute.go @@ -278,6 +278,18 @@ func (a MapNestedAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a MapNestedAttribute) IsOptionalForImport() bool { + return false +} + // MapDefaultValue returns the Default field value. func (a MapNestedAttribute) MapDefaultValue() defaults.Map { return a.Default diff --git a/resource/schema/map_nested_attribute_test.go b/resource/schema/map_nested_attribute_test.go index 6f360d85b..94d5ff593 100644 --- a/resource/schema/map_nested_attribute_test.go +++ b/resource/schema/map_nested_attribute_test.go @@ -1016,3 +1016,55 @@ func TestMapNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestMapNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestMapNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.MapNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.MapNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/number_attribute.go b/resource/schema/number_attribute.go index 8f367592e..a443945e8 100644 --- a/resource/schema/number_attribute.go +++ b/resource/schema/number_attribute.go @@ -233,6 +233,18 @@ func (a NumberAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a NumberAttribute) IsOptionalForImport() bool { + return false +} + // NumberDefaultValue returns the Default field value. func (a NumberAttribute) NumberDefaultValue() defaults.Number { return a.Default diff --git a/resource/schema/number_attribute_test.go b/resource/schema/number_attribute_test.go index 537bd1bfc..027324224 100644 --- a/resource/schema/number_attribute_test.go +++ b/resource/schema/number_attribute_test.go @@ -585,3 +585,55 @@ func TestNumberAttributeValidateImplementation(t *testing.T) { }) } } + +func TestNumberAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestNumberAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.NumberAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.NumberAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/object_attribute.go b/resource/schema/object_attribute.go index 03f35aa00..1de48e873 100644 --- a/resource/schema/object_attribute.go +++ b/resource/schema/object_attribute.go @@ -249,6 +249,18 @@ func (a ObjectAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a ObjectAttribute) IsOptionalForImport() bool { + return false +} + // ObjectDefaultValue returns the Default field value. func (a ObjectAttribute) ObjectDefaultValue() defaults.Object { return a.Default diff --git a/resource/schema/object_attribute_test.go b/resource/schema/object_attribute_test.go index 908c48395..fd6df413b 100644 --- a/resource/schema/object_attribute_test.go +++ b/resource/schema/object_attribute_test.go @@ -785,3 +785,55 @@ func TestObjectAttributeValidateImplementation(t *testing.T) { }) } } + +func TestObjectAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestObjectAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.ObjectAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.ObjectAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/set_attribute.go b/resource/schema/set_attribute.go index b65656dac..2c9f08286 100644 --- a/resource/schema/set_attribute.go +++ b/resource/schema/set_attribute.go @@ -235,6 +235,18 @@ func (a SetAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetAttribute) IsOptionalForImport() bool { + return false +} + // SetDefaultValue returns the Default field value. func (a SetAttribute) SetDefaultValue() defaults.Set { return a.Default diff --git a/resource/schema/set_attribute_test.go b/resource/schema/set_attribute_test.go index 2795fbde8..268cb20b3 100644 --- a/resource/schema/set_attribute_test.go +++ b/resource/schema/set_attribute_test.go @@ -715,3 +715,55 @@ func TestSetAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/set_nested_attribute.go b/resource/schema/set_nested_attribute.go index 5d408cf1a..c3b26780e 100644 --- a/resource/schema/set_nested_attribute.go +++ b/resource/schema/set_nested_attribute.go @@ -260,6 +260,18 @@ func (a SetNestedAttribute) IsWriteOnly() bool { return false } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SetNestedAttribute) IsOptionalForImport() bool { + return false +} + // SetDefaultValue returns the Default field value. func (a SetNestedAttribute) SetDefaultValue() defaults.Set { return a.Default diff --git a/resource/schema/set_nested_attribute_test.go b/resource/schema/set_nested_attribute_test.go index a0210f410..406297a92 100644 --- a/resource/schema/set_nested_attribute_test.go +++ b/resource/schema/set_nested_attribute_test.go @@ -983,3 +983,55 @@ func TestSetNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSetNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSetNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SetNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SetNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/single_nested_attribute.go b/resource/schema/single_nested_attribute.go index 12cab9800..0d4e00e24 100644 --- a/resource/schema/single_nested_attribute.go +++ b/resource/schema/single_nested_attribute.go @@ -286,6 +286,18 @@ func (a SingleNestedAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a SingleNestedAttribute) IsOptionalForImport() bool { + return false +} + // ObjectDefaultValue returns the Default field value. func (a SingleNestedAttribute) ObjectDefaultValue() defaults.Object { return a.Default diff --git a/resource/schema/single_nested_attribute_test.go b/resource/schema/single_nested_attribute_test.go index b03725b76..259102bfc 100644 --- a/resource/schema/single_nested_attribute_test.go +++ b/resource/schema/single_nested_attribute_test.go @@ -918,3 +918,55 @@ func TestSingleNestedAttributeValidateImplementation(t *testing.T) { }) } } + +func TestSingleNestedAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestSingleNestedAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.SingleNestedAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.SingleNestedAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/schema/string_attribute.go b/resource/schema/string_attribute.go index 693327016..12340ee6e 100644 --- a/resource/schema/string_attribute.go +++ b/resource/schema/string_attribute.go @@ -229,6 +229,18 @@ func (a StringAttribute) IsWriteOnly() bool { return a.WriteOnly } +// IsRequiredForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsRequiredForImport() bool { + return false +} + +// IsOptionalForImport returns false as this behavior is only relevant +// for managed resource identity schema attributes. +func (a StringAttribute) IsOptionalForImport() bool { + return false +} + // StringDefaultValue returns the Default field value. func (a StringAttribute) StringDefaultValue() defaults.String { return a.Default diff --git a/resource/schema/string_attribute_test.go b/resource/schema/string_attribute_test.go index 6bf70eda4..c1dffa2f2 100644 --- a/resource/schema/string_attribute_test.go +++ b/resource/schema/string_attribute_test.go @@ -580,3 +580,55 @@ func TestStringAttributeValidateImplementation(t *testing.T) { }) } } + +func TestStringAttributeIsRequiredForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-requiredForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsRequiredForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} + +func TestStringAttributeIsOptionalForImport(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attribute schema.StringAttribute + expected bool + }{ + "not-optionalForImport": { + attribute: schema.StringAttribute{}, + expected: false, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := testCase.attribute.IsOptionalForImport() + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} From a31ec4a0b172083292e162077a8127db3d82a859 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Tue, 18 Mar 2025 16:16:18 -0400 Subject: [PATCH 27/42] Resource Identity: Add identity data to RPCs needed to store/read from state (#1112) * new attribute additions (breaks build for existing internal schema attributes) * update internal testing/testschema * implement in datasource/schema * implement in ephemeral/schema * implement in provider/metaschema * implement in provider/schema * implement in resource/schema * add to final internal/testing/testschema * get resource identity schema implementation * protov6 + tests * fix up package docs + build specific TODO comments * spellcheck! * add tfsdk object * ReadResource RPC implementation for v5 * ReadResource RPC implementation for v6 * protov5 + fwserver implementation for PlanResourceChange * protov6 implementation of PlanResourceChange * implement ApplyResourceChange fwserver + protov5 * protov6 implementation of apply resource * update go dep --- internal/fromproto5/applyresourcechange.go | 9 +- .../fromproto5/applyresourcechange_test.go | 64 ++- internal/fromproto5/planresourcechange.go | 9 +- .../fromproto5/planresourcechange_test.go | 64 ++- internal/fromproto5/readresource.go | 9 +- internal/fromproto5/readresource_test.go | 59 ++- internal/fromproto5/resource_identity.go | 62 +++ internal/fromproto5/resource_identity_test.go | 129 +++++ internal/fromproto6/applyresourcechange.go | 9 +- .../fromproto6/applyresourcechange_test.go | 64 ++- internal/fromproto6/planresourcechange.go | 9 +- .../fromproto6/planresourcechange_test.go | 64 ++- internal/fromproto6/readresource.go | 9 +- internal/fromproto6/readresource_test.go | 59 ++- internal/fromproto6/resource_identity.go | 62 +++ internal/fromproto6/resource_identity_test.go | 129 +++++ internal/fwschemadata/data_description.go | 6 + internal/fwserver/server.go | 70 +++ .../fwserver/server_applyresourcechange.go | 51 +- .../server_applyresourcechange_test.go | 317 ++++++++++++ internal/fwserver/server_createresource.go | 50 +- .../fwserver/server_createresource_test.go | 180 +++++++ internal/fwserver/server_deleteresource.go | 34 ++ .../fwserver/server_deleteresource_test.go | 129 ++++- .../fwserver/server_planresourcechange.go | 42 ++ .../server_planresourcechange_test.go | 267 ++++++++++ internal/fwserver/server_readresource.go | 38 ++ internal/fwserver/server_readresource_test.go | 156 ++++++ internal/fwserver/server_updateresource.go | 52 +- .../fwserver/server_updateresource_test.go | 180 +++++++ .../server_applyresourcechange.go | 10 +- .../server_applyresourcechange_test.go | 163 ++++++ .../proto5server/server_planresourcechange.go | 10 +- .../server_planresourcechange_test.go | 147 ++++++ internal/proto5server/server_readresource.go | 10 +- .../proto5server/server_readresource_test.go | 135 +++++ .../server_applyresourcechange.go | 10 +- .../server_applyresourcechange_test.go | 163 ++++++ .../proto6server/server_planresourcechange.go | 10 +- .../server_planresourcechange_test.go | 147 ++++++ internal/proto6server/server_readresource.go | 10 +- .../proto6server/server_readresource_test.go | 135 +++++ .../resourcewithidentityandmodifyplan.go | 43 ++ internal/toproto5/applyresourcechange.go | 5 + internal/toproto5/applyresourcechange_test.go | 80 +++ internal/toproto5/planresourcechange.go | 5 + internal/toproto5/planresourcechange_test.go | 80 +++ internal/toproto5/readresource.go | 5 + internal/toproto5/readresource_test.go | 80 +++ internal/toproto5/resource_identity.go | 35 ++ internal/toproto5/resource_identity_test.go | 109 ++++ internal/toproto6/applyresourcechange.go | 5 + internal/toproto6/applyresourcechange_test.go | 80 +++ internal/toproto6/planresourcechange.go | 5 + internal/toproto6/planresourcechange_test.go | 80 +++ internal/toproto6/readresource.go | 5 + internal/toproto6/readresource_test.go | 80 +++ internal/toproto6/resource_identity.go | 35 ++ internal/toproto6/resource_identity_test.go | 109 ++++ resource/create.go | 12 + resource/delete.go | 7 +- resource/modify_plan.go | 11 + resource/read.go | 12 + resource/update.go | 12 + tfsdk/resource_identity.go | 91 ++++ tfsdk/resource_identity_test.go | 482 ++++++++++++++++++ 66 files changed, 4746 insertions(+), 54 deletions(-) create mode 100644 internal/fromproto5/resource_identity.go create mode 100644 internal/fromproto5/resource_identity_test.go create mode 100644 internal/fromproto6/resource_identity.go create mode 100644 internal/fromproto6/resource_identity_test.go create mode 100644 internal/testing/testprovider/resourcewithidentityandmodifyplan.go create mode 100644 internal/toproto5/resource_identity.go create mode 100644 internal/toproto5/resource_identity_test.go create mode 100644 internal/toproto6/resource_identity.go create mode 100644 internal/toproto6/resource_identity_test.go create mode 100644 tfsdk/resource_identity.go create mode 100644 tfsdk/resource_identity_test.go diff --git a/internal/fromproto5/applyresourcechange.go b/internal/fromproto5/applyresourcechange.go index 08d04d4ac..c32b34387 100644 --- a/internal/fromproto5/applyresourcechange.go +++ b/internal/fromproto5/applyresourcechange.go @@ -17,7 +17,7 @@ import ( // ApplyResourceChangeRequest returns the *fwserver.ApplyResourceChangeRequest // equivalent of a *tfprotov5.ApplyResourceChangeRequest. -func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) { +func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -40,6 +40,7 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso fw := &fwserver.ApplyResourceChangeRequest{ ResourceSchema: resourceSchema, + IdentitySchema: identitySchema, Resource: resource, } @@ -55,6 +56,12 @@ func ApplyResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.ApplyReso fw.PlannedState = plannedState + plannedIdentity, plannedIdentityDiags := ResourceIdentity(ctx, proto5.PlannedIdentity, identitySchema) + + diags.Append(plannedIdentityDiags...) + + fw.PlannedIdentity = plannedIdentity + priorState, priorStateDiags := State(ctx, proto5.PriorState, resourceSchema) diags.Append(priorStateDiags...) diff --git a/internal/fromproto5/applyresourcechange_test.go b/internal/fromproto5/applyresourcechange_test.go index 859181364..a6b09e011 100644 --- a/internal/fromproto5/applyresourcechange_test.go +++ b/internal/fromproto5/applyresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestApplyResourceChangeRequest(t *testing.T) { }, } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -61,6 +86,7 @@ func TestApplyResourceChangeRequest(t *testing.T) { resourceSchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema + identitySchema fwschema.Schema expected *fwserver.ApplyResourceChangeRequest expectedDiagnostics diag.Diagnostics }{ @@ -137,6 +163,42 @@ func TestApplyResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "plannedidentity-missing-schema": { + input: &tfprotov5.ApplyResourceChangeRequest{ + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ApplyResourceChangeRequest{ + ResourceSchema: testFwSchema, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "plannedidentity": { + input: &tfprotov5.ApplyResourceChangeRequest{ + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + resourceSchema: testFwSchema, + expected: &fwserver.ApplyResourceChangeRequest{ + IdentitySchema: testIdentitySchema, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: testIdentitySchema, + }, + ResourceSchema: testFwSchema, + }, + }, "plannedprivate-malformed-json": { input: &tfprotov5.ApplyResourceChangeRequest{ PlannedPrivate: []byte(`{`), @@ -253,7 +315,7 @@ func TestApplyResourceChangeRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema) + got, diags := fromproto5.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto5/planresourcechange.go b/internal/fromproto5/planresourcechange.go index 5bd24c1dd..2255dc35b 100644 --- a/internal/fromproto5/planresourcechange.go +++ b/internal/fromproto5/planresourcechange.go @@ -17,7 +17,7 @@ import ( // PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest // equivalent of a *tfprotov5.PlanResourceChangeRequest. -func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { +func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -41,6 +41,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour fw := &fwserver.PlanResourceChangeRequest{ ResourceBehavior: resourceBehavior, ResourceSchema: resourceSchema, + IdentitySchema: identitySchema, Resource: reqResource, ClientCapabilities: ModifyPlanClientCapabilities(proto5.ClientCapabilities), } @@ -57,6 +58,12 @@ func PlanResourceChangeRequest(ctx context.Context, proto5 *tfprotov5.PlanResour fw.PriorState = priorState + priorIdentity, priorIdentityDiags := ResourceIdentity(ctx, proto5.PriorIdentity, identitySchema) + + diags.Append(priorIdentityDiags...) + + fw.PriorIdentity = priorIdentity + proposedNewState, proposedNewStateDiags := Plan(ctx, proto5.ProposedNewState, resourceSchema) diags.Append(proposedNewStateDiags...) diff --git a/internal/fromproto5/planresourcechange_test.go b/internal/fromproto5/planresourcechange_test.go index b223e0e59..fd37a170f 100644 --- a/internal/fromproto5/planresourcechange_test.go +++ b/internal/fromproto5/planresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -58,6 +83,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { input *tfprotov5.PlanResourceChangeRequest resourceBehavior resource.ResourceBehavior resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema expected *fwserver.PlanResourceChangeRequest @@ -182,6 +208,42 @@ func TestPlanResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "prioridentity-missing-schema": { + input: &tfprotov5.PlanResourceChangeRequest{ + PriorIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ResourceSchema: testFwSchema, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "prioridentity": { + input: &tfprotov5.PlanResourceChangeRequest{ + PriorIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + IdentitySchema: testIdentitySchema, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: testIdentitySchema, + }, + ResourceSchema: testFwSchema, + }, + }, "providermeta-missing-data": { input: &tfprotov5.PlanResourceChangeRequest{}, resourceSchema: testFwSchema, @@ -265,7 +327,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior) + got, diags := fromproto5.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto5/readresource.go b/internal/fromproto5/readresource.go index 04d4b4d2e..003424579 100644 --- a/internal/fromproto5/readresource.go +++ b/internal/fromproto5/readresource.go @@ -17,7 +17,7 @@ import ( // ReadResourceRequest returns the *fwserver.ReadResourceRequest // equivalent of a *tfprotov5.ReadResourceRequest. -func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { +func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -26,6 +26,7 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ fw := &fwserver.ReadResourceRequest{ Resource: reqResource, + IdentitySchema: identitySchema, ClientCapabilities: ReadResourceClientCapabilities(proto5.ClientCapabilities), } @@ -35,6 +36,12 @@ func ReadResourceRequest(ctx context.Context, proto5 *tfprotov5.ReadResourceRequ fw.CurrentState = currentState + currentIdentity, currentIdentityDiags := ResourceIdentity(ctx, proto5.CurrentIdentity, identitySchema) + + diags.Append(currentIdentityDiags...) + + fw.CurrentIdentity = currentIdentity + providerMeta, providerMetaDiags := ProviderMeta(ctx, proto5.ProviderMeta, providerMetaSchema) diags.Append(providerMetaDiags...) diff --git a/internal/fromproto5/readresource_test.go b/internal/fromproto5/readresource_test.go index c58fc01cc..1c8adb7f6 100644 --- a/internal/fromproto5/readresource_test.go +++ b/internal/fromproto5/readresource_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestReadResourceRequest(t *testing.T) { }, } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -59,6 +84,7 @@ func TestReadResourceRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov5.ReadResourceRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema expected *fwserver.ReadResourceRequest @@ -99,6 +125,37 @@ func TestReadResourceRequest(t *testing.T) { }, }, }, + "currentidentity-missing-schema": { + input: &tfprotov5.ReadResourceRequest{ + CurrentIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + expected: &fwserver.ReadResourceRequest{}, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "currentidentity": { + input: &tfprotov5.ReadResourceRequest{ + CurrentIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + expected: &fwserver.ReadResourceRequest{ + IdentitySchema: testIdentitySchema, + CurrentIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: testIdentitySchema, + }, + }, + }, "private-malformed-json": { input: &tfprotov5.ReadResourceRequest{ Private: []byte(`{`), @@ -200,7 +257,7 @@ func TestReadResourceRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema) + got, diags := fromproto5.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto5/resource_identity.go b/internal/fromproto5/resource_identity.go new file mode 100644 index 000000000..c6e1a8014 --- /dev/null +++ b/internal/fromproto5/resource_identity.go @@ -0,0 +1,62 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// TODO:ResourceIdentity: Should we create a wrapping struct to contain the identity data? To match the protocol (in-case we want to introduce other identity things) +// - Need to think more on this (like what if we want to introduce display-only attributes) +// - If we introduce one, add a test as well. +func ResourceIdentity(ctx context.Context, in *tfprotov5.ResourceIdentityData, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { + if in == nil { + return nil, nil + } + + return IdentityData(ctx, in.IdentityData, schema) +} + +// IdentityData returns the *tfsdk.ResourceIdentity for a *tfprotov5.DynamicValue and fwschema.Schema. +func IdentityData(ctx context.Context, proto5DynamicValue *tfprotov5.DynamicValue, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { + if proto5DynamicValue == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if schema == nil { + diags.AddError( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ) + + return nil, diags + } + + data, dynamicValueDiags := DynamicValue(ctx, proto5DynamicValue, schema, fwschemadata.DataDescriptionResourceIdentity) + + diags.Append(dynamicValueDiags...) + + if diags.HasError() { + return nil, diags + } + + fw := &tfsdk.ResourceIdentity{ + Raw: data.TerraformValue, + Schema: schema, + } + + return fw, diags +} diff --git a/internal/fromproto5/resource_identity_test.go b/internal/fromproto5/resource_identity_test.go new file mode 100644 index 000000000..8e7687140 --- /dev/null +++ b/internal/fromproto5/resource_identity_test.go @@ -0,0 +1,129 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto5" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestResourceIdentity(t *testing.T) { + t.Parallel() + + testProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_attribute": tftypes.String, + }, + } + + testProto5Value := tftypes.NewValue(testProto5Type, map[string]tftypes.Value{ + "test_attribute": tftypes.NewValue(tftypes.String, "test-value"), + }) + + testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testFwSchema := testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + RequiredForImport: true, + Type: types.StringType, + }, + }, + } + + testFwSchemaInvalid := testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + RequiredForImport: true, + Type: types.BoolType, + }, + }, + } + + testCases := map[string]struct { + input *tfprotov5.ResourceIdentityData + schema fwschema.Schema + expected *tfsdk.ResourceIdentity + expectedDiagnostics diag.Diagnostics + }{ + "nil": { + input: nil, + expected: nil, + }, + "empty": { + input: &tfprotov5.ResourceIdentityData{}, + expected: nil, + }, + "missing-schema": { + input: &tfprotov5.ResourceIdentityData{ + IdentityData: &testProto5DynamicValue, + }, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "invalid-schema": { + input: &tfprotov5.ResourceIdentityData{ + IdentityData: &testProto5DynamicValue, + }, + schema: testFwSchemaInvalid, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Unable to unmarshal DynamicValue: AttributeName(\"test_attribute\"): couldn't decode bool: msgpack: invalid code=aa decoding bool", + ), + }, + }, + "valid": { + input: &tfprotov5.ResourceIdentityData{ + IdentityData: &testProto5DynamicValue, + }, + schema: testFwSchema, + expected: &tfsdk.ResourceIdentity{ + Raw: testProto5Value, + Schema: testFwSchema, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := fromproto5.ResourceIdentity(context.Background(), testCase.input, testCase.schema) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} diff --git a/internal/fromproto6/applyresourcechange.go b/internal/fromproto6/applyresourcechange.go index f48eb856b..5620ffe40 100644 --- a/internal/fromproto6/applyresourcechange.go +++ b/internal/fromproto6/applyresourcechange.go @@ -17,7 +17,7 @@ import ( // ApplyResourceChangeRequest returns the *fwserver.ApplyResourceChangeRequest // equivalent of a *tfprotov6.ApplyResourceChangeRequest. -func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) { +func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyResourceChangeRequest, resource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ApplyResourceChangeRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -40,6 +40,7 @@ func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyReso fw := &fwserver.ApplyResourceChangeRequest{ ResourceSchema: resourceSchema, + IdentitySchema: identitySchema, Resource: resource, } @@ -55,6 +56,12 @@ func ApplyResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.ApplyReso fw.PlannedState = plannedState + plannedIdentity, plannedIdentityDiags := ResourceIdentity(ctx, proto6.PlannedIdentity, identitySchema) + + diags.Append(plannedIdentityDiags...) + + fw.PlannedIdentity = plannedIdentity + priorState, priorStateDiags := State(ctx, proto6.PriorState, resourceSchema) diags.Append(priorStateDiags...) diff --git a/internal/fromproto6/applyresourcechange_test.go b/internal/fromproto6/applyresourcechange_test.go index 9f845412e..d29913fb3 100644 --- a/internal/fromproto6/applyresourcechange_test.go +++ b/internal/fromproto6/applyresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestApplyResourceChangeRequest(t *testing.T) { }, } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -61,6 +86,7 @@ func TestApplyResourceChangeRequest(t *testing.T) { resourceSchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema + identitySchema fwschema.Schema expected *fwserver.ApplyResourceChangeRequest expectedDiagnostics diag.Diagnostics }{ @@ -137,6 +163,42 @@ func TestApplyResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "plannedidentity-missing-schema": { + input: &tfprotov6.ApplyResourceChangeRequest{ + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ApplyResourceChangeRequest{ + ResourceSchema: testFwSchema, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "plannedidentity": { + input: &tfprotov6.ApplyResourceChangeRequest{ + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + resourceSchema: testFwSchema, + expected: &fwserver.ApplyResourceChangeRequest{ + IdentitySchema: testIdentitySchema, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: testIdentitySchema, + }, + ResourceSchema: testFwSchema, + }, + }, "plannedprivate-malformed-json": { input: &tfprotov6.ApplyResourceChangeRequest{ PlannedPrivate: []byte(`{`), @@ -253,7 +315,7 @@ func TestApplyResourceChangeRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto6.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema) + got, diags := fromproto6.ApplyResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto6/planresourcechange.go b/internal/fromproto6/planresourcechange.go index 6b95f0789..4bffe04d1 100644 --- a/internal/fromproto6/planresourcechange.go +++ b/internal/fromproto6/planresourcechange.go @@ -17,7 +17,7 @@ import ( // PlanResourceChangeRequest returns the *fwserver.PlanResourceChangeRequest // equivalent of a *tfprotov6.PlanResourceChangeRequest. -func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { +func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResourceChangeRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, resourceBehavior resource.ResourceBehavior, identitySchema fwschema.Schema) (*fwserver.PlanResourceChangeRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -41,6 +41,7 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour fw := &fwserver.PlanResourceChangeRequest{ ResourceBehavior: resourceBehavior, ResourceSchema: resourceSchema, + IdentitySchema: identitySchema, Resource: reqResource, ClientCapabilities: ModifyPlanClientCapabilities(proto6.ClientCapabilities), } @@ -57,6 +58,12 @@ func PlanResourceChangeRequest(ctx context.Context, proto6 *tfprotov6.PlanResour fw.PriorState = priorState + priorIdentity, priorIdentityDiags := ResourceIdentity(ctx, proto6.PriorIdentity, identitySchema) + + diags.Append(priorIdentityDiags...) + + fw.PriorIdentity = priorIdentity + proposedNewState, proposedNewStateDiags := Plan(ctx, proto6.ProposedNewState, resourceSchema) diags.Append(proposedNewStateDiags...) diff --git a/internal/fromproto6/planresourcechange_test.go b/internal/fromproto6/planresourcechange_test.go index af8ee025c..3660535ae 100644 --- a/internal/fromproto6/planresourcechange_test.go +++ b/internal/fromproto6/planresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestPlanResourceChangeRequest(t *testing.T) { }, } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -58,6 +83,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { input *tfprotov6.PlanResourceChangeRequest resourceBehavior resource.ResourceBehavior resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema expected *fwserver.PlanResourceChangeRequest @@ -182,6 +208,42 @@ func TestPlanResourceChangeRequest(t *testing.T) { ResourceSchema: testFwSchema, }, }, + "prioridentity-missing-schema": { + input: &tfprotov6.PlanResourceChangeRequest{ + PriorIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + ResourceSchema: testFwSchema, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "prioridentity": { + input: &tfprotov6.PlanResourceChangeRequest{ + PriorIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + resourceSchema: testFwSchema, + expected: &fwserver.PlanResourceChangeRequest{ + IdentitySchema: testIdentitySchema, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: testIdentitySchema, + }, + ResourceSchema: testFwSchema, + }, + }, "providermeta-missing-data": { input: &tfprotov6.PlanResourceChangeRequest{}, resourceSchema: testFwSchema, @@ -265,7 +327,7 @@ func TestPlanResourceChangeRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto6.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior) + got, diags := fromproto6.PlanResourceChangeRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.resourceBehavior, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto6/readresource.go b/internal/fromproto6/readresource.go index a42f669d6..11ea6d844 100644 --- a/internal/fromproto6/readresource.go +++ b/internal/fromproto6/readresource.go @@ -17,7 +17,7 @@ import ( // ReadResourceRequest returns the *fwserver.ReadResourceRequest // equivalent of a *tfprotov6.ReadResourceRequest. -func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { +func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, providerMetaSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ReadResourceRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -26,6 +26,7 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ fw := &fwserver.ReadResourceRequest{ Resource: reqResource, + IdentitySchema: identitySchema, ClientCapabilities: ReadResourceClientCapabilities(proto6.ClientCapabilities), } @@ -35,6 +36,12 @@ func ReadResourceRequest(ctx context.Context, proto6 *tfprotov6.ReadResourceRequ fw.CurrentState = currentState + currentIdentity, currentIdentityDiags := ResourceIdentity(ctx, proto6.CurrentIdentity, identitySchema) + + diags.Append(currentIdentityDiags...) + + fw.CurrentIdentity = currentIdentity + providerMeta, providerMetaDiags := ProviderMeta(ctx, proto6.ProviderMeta, providerMetaSchema) diags.Append(providerMetaDiags...) diff --git a/internal/fromproto6/readresource_test.go b/internal/fromproto6/readresource_test.go index 5ae9e1688..e24e9f5bc 100644 --- a/internal/fromproto6/readresource_test.go +++ b/internal/fromproto6/readresource_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -48,6 +49,30 @@ func TestReadResourceRequest(t *testing.T) { }, } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -59,6 +84,7 @@ func TestReadResourceRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov6.ReadResourceRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource providerMetaSchema fwschema.Schema expected *fwserver.ReadResourceRequest @@ -99,6 +125,37 @@ func TestReadResourceRequest(t *testing.T) { }, }, }, + "currentidentity-missing-schema": { + input: &tfprotov6.ReadResourceRequest{ + CurrentIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + expected: &fwserver.ReadResourceRequest{}, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "currentidentity": { + input: &tfprotov6.ReadResourceRequest{ + CurrentIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + identitySchema: testIdentitySchema, + expected: &fwserver.ReadResourceRequest{ + IdentitySchema: testIdentitySchema, + CurrentIdentity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: testIdentitySchema, + }, + }, + }, "private-malformed-json": { input: &tfprotov6.ReadResourceRequest{ Private: []byte(`{`), @@ -200,7 +257,7 @@ func TestReadResourceRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto6.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema) + got, diags := fromproto6.ReadResourceRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.providerMetaSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto6/resource_identity.go b/internal/fromproto6/resource_identity.go new file mode 100644 index 000000000..d9fc1b4e5 --- /dev/null +++ b/internal/fromproto6/resource_identity.go @@ -0,0 +1,62 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// TODO:ResourceIdentity: Should we create a wrapping struct to contain the identity data? To match the protocol (in-case we want to introduce other identity things) +// - Need to think more on this (like what if we want to introduce display-only attributes) +// - If we introduce one, add a test as well. +func ResourceIdentity(ctx context.Context, in *tfprotov6.ResourceIdentityData, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { + if in == nil { + return nil, nil + } + + return IdentityData(ctx, in.IdentityData, schema) +} + +// IdentityData returns the *tfsdk.ResourceIdentity for a *tfprotov6.DynamicValue and fwschema.Schema. +func IdentityData(ctx context.Context, proto6DynamicValue *tfprotov6.DynamicValue, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { + if proto6DynamicValue == nil { + return nil, nil + } + + var diags diag.Diagnostics + + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if schema == nil { + diags.AddError( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ) + + return nil, diags + } + + data, dynamicValueDiags := DynamicValue(ctx, proto6DynamicValue, schema, fwschemadata.DataDescriptionResourceIdentity) + + diags.Append(dynamicValueDiags...) + + if diags.HasError() { + return nil, diags + } + + fw := &tfsdk.ResourceIdentity{ + Raw: data.TerraformValue, + Schema: schema, + } + + return fw, diags +} diff --git a/internal/fromproto6/resource_identity_test.go b/internal/fromproto6/resource_identity_test.go new file mode 100644 index 000000000..51724170d --- /dev/null +++ b/internal/fromproto6/resource_identity_test.go @@ -0,0 +1,129 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package fromproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestResourceIdentity(t *testing.T) { + t.Parallel() + + testProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_attribute": tftypes.String, + }, + } + + testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{ + "test_attribute": tftypes.NewValue(tftypes.String, "test-value"), + }) + + testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testFwSchema := testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + RequiredForImport: true, + Type: types.StringType, + }, + }, + } + + testFwSchemaInvalid := testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + RequiredForImport: true, + Type: types.BoolType, + }, + }, + } + + testCases := map[string]struct { + input *tfprotov6.ResourceIdentityData + schema fwschema.Schema + expected *tfsdk.ResourceIdentity + expectedDiagnostics diag.Diagnostics + }{ + "nil": { + input: nil, + expected: nil, + }, + "empty": { + input: &tfprotov6.ResourceIdentityData{}, + expected: nil, + }, + "missing-schema": { + input: &tfprotov6.ResourceIdentityData{ + IdentityData: &testProto6DynamicValue, + }, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "invalid-schema": { + input: &tfprotov6.ResourceIdentityData{ + IdentityData: &testProto6DynamicValue, + }, + schema: testFwSchemaInvalid, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Unable to unmarshal DynamicValue: AttributeName(\"test_attribute\"): couldn't decode bool: msgpack: invalid code=aa decoding bool", + ), + }, + }, + "valid": { + input: &tfprotov6.ResourceIdentityData{ + IdentityData: &testProto6DynamicValue, + }, + schema: testFwSchema, + expected: &tfsdk.ResourceIdentity{ + Raw: testProto6Value, + Schema: testFwSchema, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := fromproto6.ResourceIdentity(context.Background(), testCase.input, testCase.schema) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} diff --git a/internal/fwschemadata/data_description.go b/internal/fwschemadata/data_description.go index 70ae62c76..282a53321 100644 --- a/internal/fwschemadata/data_description.go +++ b/internal/fwschemadata/data_description.go @@ -19,6 +19,10 @@ const ( // DataDescriptionEphemeralResultData is used for Data that represents // the result of an ephemeral operation. DataDescriptionEphemeralResultData DataDescription = "ephemeral result data" + + // DataDescriptionResourceIdentity is used for Data that represents + // a managed resource identity. + DataDescriptionResourceIdentity DataDescription = "resource identity" ) // DataDescription is a human friendly type for Data. Used in error @@ -46,6 +50,8 @@ func (d DataDescription) Title() string { return "State" case DataDescriptionEphemeralResultData: return "Ephemeral Result Data" + case DataDescriptionResourceIdentity: + return "Resource Identity" default: return "Data" } diff --git a/internal/fwserver/server.go b/internal/fwserver/server.go index 22723f9ff..b4b8a779b 100644 --- a/internal/fwserver/server.go +++ b/internal/fwserver/server.go @@ -158,6 +158,15 @@ type Server struct { // access from race conditions. resourceSchemasMutex sync.RWMutex + // resourceIdentitySchemas is the cached Resource Identity Schemas for RPCs that need to + // convert resource identity data from the protocol. If not found, it will be + // fetched from the Resource IdentitySchema method. + resourceIdentitySchemas map[string]fwschema.Schema + + // resourceIdentitySchemasMutex is a mutex to protect concurrent resourceIdentitySchemas + // access from race conditions. + resourceIdentitySchemasMutex sync.RWMutex + // resourceFuncs is the cached Resource functions for RPCs that need to // access resources. If not found, it will be fetched from the // Provider.Resources() method. @@ -690,6 +699,67 @@ func (s *Server) ResourceSchemas(ctx context.Context) (map[string]fwschema.Schem return resourceSchemas, diags } +// ResourceIdentitySchema returns the Resource Identity Schema for the given type name and +// caches the result for later Identity operations. Identity is an optional feature for resources, +// so this function will return a nil schema with no diagnostics if the resource type doesn't define +// an identity schema. +func (s *Server) ResourceIdentitySchema(ctx context.Context, typeName string) (fwschema.Schema, diag.Diagnostics) { + s.resourceIdentitySchemasMutex.RLock() + resourceIdentitySchema, ok := s.resourceIdentitySchemas[typeName] + s.resourceIdentitySchemasMutex.RUnlock() + + if ok { + return resourceIdentitySchema, nil + } + + var diags diag.Diagnostics + + r, resourceDiags := s.Resource(ctx, typeName) + + diags.Append(resourceDiags...) + + if diags.HasError() { + return nil, diags + } + + resourceWithIdentity, ok := r.(resource.ResourceWithIdentity) + if !ok { + // It's valid for a resource to not have an identity, so cache and return a nil schema + s.resourceIdentitySchemasMutex.Lock() + if s.resourceIdentitySchemas == nil { + s.resourceIdentitySchemas = make(map[string]fwschema.Schema) + } + + s.resourceIdentitySchemas[typeName] = nil + s.resourceIdentitySchemasMutex.Unlock() + + return nil, nil + } + + identitySchemaReq := resource.IdentitySchemaRequest{} + identitySchemaResp := resource.IdentitySchemaResponse{} + + logging.FrameworkTrace(ctx, "Calling provider defined Resource IdentitySchema method", map[string]interface{}{logging.KeyResourceType: typeName}) + resourceWithIdentity.IdentitySchema(ctx, identitySchemaReq, &identitySchemaResp) + logging.FrameworkTrace(ctx, "Called provider defined Resource IdentitySchema method", map[string]interface{}{logging.KeyResourceType: typeName}) + + diags.Append(identitySchemaResp.Diagnostics...) + + if diags.HasError() { + return identitySchemaResp.IdentitySchema, diags + } + + s.resourceIdentitySchemasMutex.Lock() + if s.resourceIdentitySchemas == nil { + s.resourceIdentitySchemas = make(map[string]fwschema.Schema) + } + + s.resourceIdentitySchemas[typeName] = identitySchemaResp.IdentitySchema + s.resourceIdentitySchemasMutex.Unlock() + + return identitySchemaResp.IdentitySchema, diags +} + // ResourceIdentitySchemas returns a map of Resource Identity Schemas for the // GetResourceIdentitySchemas RPC without caching since not all schemas are guaranteed to // be necessary for later provider operations. The schema implementations are diff --git a/internal/fwserver/server_applyresourcechange.go b/internal/fwserver/server_applyresourcechange.go index a11a72e47..2167ab709 100644 --- a/internal/fwserver/server_applyresourcechange.go +++ b/internal/fwserver/server_applyresourcechange.go @@ -17,13 +17,15 @@ import ( // ApplyResourceChangeRequest is the framework server request for the // ApplyResourceChange RPC. type ApplyResourceChangeRequest struct { - Config *tfsdk.Config - PlannedPrivate *privatestate.Data - PlannedState *tfsdk.Plan - PriorState *tfsdk.State - ProviderMeta *tfsdk.Config - ResourceSchema fwschema.Schema - Resource resource.Resource + Config *tfsdk.Config + PlannedPrivate *privatestate.Data + PlannedState *tfsdk.Plan + PlannedIdentity *tfsdk.ResourceIdentity + PriorState *tfsdk.State + ProviderMeta *tfsdk.Config + ResourceSchema fwschema.Schema + IdentitySchema fwschema.Schema + Resource resource.Resource } // ApplyResourceChangeResponse is the framework server response for the @@ -31,6 +33,7 @@ type ApplyResourceChangeRequest struct { type ApplyResourceChangeResponse struct { Diagnostics diag.Diagnostics NewState *tfsdk.State + NewIdentity *tfsdk.ResourceIdentity Private *privatestate.Data } @@ -45,12 +48,14 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan logging.FrameworkTrace(ctx, "ApplyResourceChange received no PriorState, running CreateResource") createReq := &CreateResourceRequest{ - Config: req.Config, - PlannedPrivate: req.PlannedPrivate, - PlannedState: req.PlannedState, - ProviderMeta: req.ProviderMeta, - ResourceSchema: req.ResourceSchema, - Resource: req.Resource, + Config: req.Config, + PlannedPrivate: req.PlannedPrivate, + PlannedState: req.PlannedState, + PlannedIdentity: req.PlannedIdentity, + ProviderMeta: req.ProviderMeta, + ResourceSchema: req.ResourceSchema, + IdentitySchema: req.IdentitySchema, + Resource: req.Resource, } createResp := &CreateResourceResponse{} @@ -58,6 +63,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan resp.Diagnostics = createResp.Diagnostics resp.NewState = createResp.NewState + resp.NewIdentity = createResp.NewIdentity resp.Private = createResp.Private return @@ -72,6 +78,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan PriorState: req.PriorState, ProviderMeta: req.ProviderMeta, ResourceSchema: req.ResourceSchema, + IdentitySchema: req.IdentitySchema, Resource: req.Resource, } deleteResp := &DeleteResourceResponse{} @@ -80,6 +87,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan resp.Diagnostics = deleteResp.Diagnostics resp.NewState = deleteResp.NewState + resp.NewIdentity = deleteResp.NewIdentity resp.Private = deleteResp.Private return @@ -89,13 +97,15 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan logging.FrameworkTrace(ctx, "ApplyResourceChange running UpdateResource") updateReq := &UpdateResourceRequest{ - Config: req.Config, - PlannedPrivate: req.PlannedPrivate, - PlannedState: req.PlannedState, - PriorState: req.PriorState, - ProviderMeta: req.ProviderMeta, - ResourceSchema: req.ResourceSchema, - Resource: req.Resource, + Config: req.Config, + PlannedPrivate: req.PlannedPrivate, + PlannedState: req.PlannedState, + PlannedIdentity: req.PlannedIdentity, + PriorState: req.PriorState, + ProviderMeta: req.ProviderMeta, + ResourceSchema: req.ResourceSchema, + IdentitySchema: req.IdentitySchema, + Resource: req.Resource, } updateResp := &UpdateResourceResponse{} @@ -103,5 +113,6 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan resp.Diagnostics = updateResp.Diagnostics resp.NewState = updateResp.NewState + resp.NewIdentity = updateResp.NewIdentity resp.Private = updateResp.Private } diff --git a/internal/fwserver/server_applyresourcechange_test.go b/internal/fwserver/server_applyresourcechange_test.go index ea4452244..a8565de7d 100644 --- a/internal/fwserver/server_applyresourcechange_test.go +++ b/internal/fwserver/server_applyresourcechange_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -33,6 +34,12 @@ func TestServerApplyResourceChange(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -44,6 +51,14 @@ func TestServerApplyResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testEmptyPlan := &tfsdk.Plan{ Raw: tftypes.NewValue(testSchemaType, nil), Schema: testSchema, @@ -84,6 +99,10 @@ func TestServerApplyResourceChange(t *testing.T) { TestRequiredWriteOnly types.String `tfsdk:"test_required_write_only"` } + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + testProviderMetaType := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_provider_meta_attribute": tftypes.String, @@ -243,6 +262,69 @@ func TestServerApplyResourceChange(t *testing.T) { }, Private: testEmptyPrivate}, }, + "create-request-plannedidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + PriorState: testEmptyState, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate}, + }, "create-request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -376,6 +458,67 @@ func TestServerApplyResourceChange(t *testing.T) { Private: testEmptyPrivate, }, }, + "create-response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + })...) + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "create-response-newstate-null": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -826,6 +969,48 @@ func TestServerApplyResourceChange(t *testing.T) { NewState: testEmptyState, }, }, + "delete-response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + PlannedState: testEmptyPlan, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(_ context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Delete, Got: Create") + }, + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + if resp.Identity == nil || !resp.Identity.Raw.IsNull() { + resp.Diagnostics.AddError( + "Unexpected resp.Identity", + "expected resp.Identity to be a null object of the schema type.", + ) + } + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Delete, Got: Update") + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + }, + NewState: testEmptyState, + }, + }, "update-request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -940,6 +1125,77 @@ func TestServerApplyResourceChange(t *testing.T) { Private: testEmptyPrivate, }, }, + "update-request-plannedidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-new-value"), + }), + Schema: testSchema, + }, + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-new-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-old-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(_ context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Create") + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Delete") + }, + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-old-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "update-request-priorstate": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -1280,6 +1536,67 @@ func TestServerApplyResourceChange(t *testing.T) { Private: testEmptyPrivate, }, }, + "update-response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-new-value"), + }), + Schema: testSchema, + }, + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-new-value"), + }), + Schema: testSchema, + }, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-old-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(_ context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Create") + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Update, Got: Delete") + }, + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + })...) + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-old-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "update-response-newstate-null": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_createresource.go b/internal/fwserver/server_createresource.go index d5a0aef2e..0e5b873ac 100644 --- a/internal/fwserver/server_createresource.go +++ b/internal/fwserver/server_createresource.go @@ -20,12 +20,14 @@ import ( // CreateResourceRequest is the framework server request for a create request // with the ApplyResourceChange RPC. type CreateResourceRequest struct { - Config *tfsdk.Config - PlannedPrivate *privatestate.Data - PlannedState *tfsdk.Plan - ProviderMeta *tfsdk.Config - ResourceSchema fwschema.Schema - Resource resource.Resource + Config *tfsdk.Config + PlannedPrivate *privatestate.Data + PlannedState *tfsdk.Plan + PlannedIdentity *tfsdk.ResourceIdentity + ProviderMeta *tfsdk.Config + ResourceSchema fwschema.Schema + IdentitySchema fwschema.Schema + Resource resource.Resource } // CreateResourceResponse is the framework server response for a create request @@ -33,6 +35,7 @@ type CreateResourceRequest struct { type CreateResourceResponse struct { Diagnostics diag.Diagnostics NewState *tfsdk.State + NewIdentity *tfsdk.ResourceIdentity Private *privatestate.Data } @@ -97,12 +100,37 @@ func (s *Server) CreateResource(ctx context.Context, req *CreateResourceRequest, createReq.ProviderMeta = *req.ProviderMeta } + // If the resource supports identity and there is no planned identity data, pre-populate with a null value. + // TODO:ResourceIdentity: This logic is likely useless since plan should already handle this, probably should remove. + if req.PlannedIdentity == nil && req.IdentitySchema != nil { + nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + req.PlannedIdentity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullIdentityTfValue.Copy(), + } + } + + // Pre-populate the new identity with the planned identity. + if req.PlannedIdentity != nil { + createReq.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PlannedIdentity.Schema, + Raw: req.PlannedIdentity.Raw.Copy(), + } + + createResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PlannedIdentity.Schema, + Raw: req.PlannedIdentity.Raw.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource Create") req.Resource.Create(ctx, createReq, &createResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Create") resp.Diagnostics = createResp.Diagnostics resp.NewState = &createResp.State + resp.NewIdentity = createResp.Identity if !resp.Diagnostics.HasError() && createResp.State.Raw.Equal(nullSchemaData) { detail := "The Terraform Provider unexpectedly returned no resource state after having no errors in the resource creation. " + @@ -132,6 +160,16 @@ func (s *Server) CreateResource(ctx context.Context, req *CreateResourceRequest, return } + if resp.NewIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Create Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider create operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } + semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionPlan, diff --git a/internal/fwserver/server_createresource_test.go b/internal/fwserver/server_createresource_test.go index 86bedcb8f..3095638ff 100644 --- a/internal/fwserver/server_createresource_test.go +++ b/internal/fwserver/server_createresource_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -33,6 +34,12 @@ func TestServerCreateResource(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchemaTypeWriteOnly := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_required": tftypes.String, @@ -51,6 +58,14 @@ func TestServerCreateResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testSchemaWithSemanticEquals := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -105,6 +120,10 @@ func TestServerCreateResource(t *testing.T) { TestRequired types.String `tfsdk:"test_required"` } + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + type testSchemaDataWithSemanticEquals struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired testtypes.StringValueWithSemanticEquals `tfsdk:"test_required"` @@ -244,6 +263,63 @@ func TestServerCreateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "request-plannedidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.CreateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.CreateResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -457,6 +533,110 @@ func TestServerCreateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.CreateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + })...) + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.CreateResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, + "response-invalid-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.CreateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.CreateResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Create Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider create operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "response-newstate-null": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_deleteresource.go b/internal/fwserver/server_deleteresource.go index 5879b6706..d556badfd 100644 --- a/internal/fwserver/server_deleteresource.go +++ b/internal/fwserver/server_deleteresource.go @@ -23,6 +23,7 @@ type DeleteResourceRequest struct { PriorState *tfsdk.State ProviderMeta *tfsdk.Config ResourceSchema fwschema.Schema + IdentitySchema fwschema.Schema Resource resource.Resource } @@ -31,6 +32,7 @@ type DeleteResourceRequest struct { type DeleteResourceResponse struct { Diagnostics diag.Diagnostics NewState *tfsdk.State + NewIdentity *tfsdk.ResourceIdentity Private *privatestate.Data } @@ -96,6 +98,17 @@ func (s *Server) DeleteResource(ctx context.Context, req *DeleteResourceRequest, resp.Private = req.PlannedPrivate } + // If the resource supports identity pre-populate a null value. + // TODO:ResourceIdentity: This should probably be prior identity, but we don't currently have that in the protocol. + if req.IdentitySchema != nil { + nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + deleteResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullIdentityTfValue.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource Delete") req.Resource.Delete(ctx, deleteReq, &deleteResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Delete") @@ -108,10 +121,21 @@ func (s *Server) DeleteResource(ctx context.Context, req *DeleteResourceRequest, // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/863 deleteResp.Private = nil resp.Private = nil + + // If the resource supports identity send a null value. + if req.IdentitySchema != nil { + nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + deleteResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullIdentityTfValue.Copy(), + } + } } resp.Diagnostics = deleteResp.Diagnostics resp.NewState = &deleteResp.State + resp.NewIdentity = deleteResp.Identity if deleteResp.Private != nil { if resp.Private == nil { @@ -120,4 +144,14 @@ func (s *Server) DeleteResource(ctx context.Context, req *DeleteResourceRequest, resp.Private.Provider = deleteResp.Private } + + if resp.NewIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Delete Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider delete operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } } diff --git a/internal/fwserver/server_deleteresource_test.go b/internal/fwserver/server_deleteresource_test.go index 2042e2764..b47c16c0b 100644 --- a/internal/fwserver/server_deleteresource_test.go +++ b/internal/fwserver/server_deleteresource_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -33,6 +34,12 @@ func TestServerDeleteResource(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -44,6 +51,14 @@ func TestServerDeleteResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testEmptyState := &tfsdk.State{ Raw: tftypes.NewValue(testSchemaType, nil), Schema: testSchema, @@ -54,6 +69,10 @@ func TestServerDeleteResource(t *testing.T) { TestRequired types.String `tfsdk:"test_required"` } + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + testProviderMetaType := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_provider_meta_attribute": tftypes.String, @@ -334,6 +353,114 @@ func TestServerDeleteResource(t *testing.T) { NewState: testEmptyState, }, }, + "response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.DeleteResourceRequest{ + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.Resource{ + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + if resp.Identity == nil || !resp.Identity.Raw.IsNull() { + resp.Diagnostics.AddError( + "Unexpected resp.Identity", + "expected resp.Identity to be a null object of the schema type.", + ) + } + }, + }, + }, + expectedResponse: &fwserver.DeleteResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + }, + NewState: testEmptyState, + }, + }, + "response-newidentity-set-to-null": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.DeleteResourceRequest{ + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.Resource{ + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // This should be nulled out + resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + })...) + }, + }, + }, + expectedResponse: &fwserver.DeleteResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + }, + NewState: testEmptyState, + }, + }, + "response-invalid-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.DeleteResourceRequest{ + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // This should raise a diagnostic + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + } + }, + }, + }, + }, + expectedResponse: &fwserver.DeleteResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Delete Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider delete operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: testEmptyState, + }, + }, "response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -375,7 +502,7 @@ func TestServerDeleteResource(t *testing.T) { Private: testPrivateProvider, }, }, - "response-private-updated": { + "response-private-Deleted": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, }, diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index 52b06688b..7910bac84 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -30,9 +30,11 @@ type PlanResourceChangeRequest struct { Config *tfsdk.Config PriorPrivate *privatestate.Data PriorState *tfsdk.State + PriorIdentity *tfsdk.ResourceIdentity ProposedNewState *tfsdk.Plan ProviderMeta *tfsdk.Config ResourceSchema fwschema.Schema + IdentitySchema fwschema.Schema Resource resource.Resource ResourceBehavior resource.ResourceBehavior } @@ -44,6 +46,7 @@ type PlanResourceChangeResponse struct { Diagnostics diag.Diagnostics PlannedPrivate *privatestate.Data PlannedState *tfsdk.State + PlannedIdentity *tfsdk.ResourceIdentity RequiresReplace path.Paths } @@ -115,6 +118,26 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange } } + // If the resource supports identity and there is no prior identity data, pre-populate with a null value. + // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? + // TODO:ResourceIdentity: Should this be set to all unknowns? + if req.PriorIdentity == nil && req.IdentitySchema != nil { + nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + req.PriorIdentity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullIdentityTfValue.Copy(), + } + } + + // Set the planned identity to the prior identity by default (can be modified later). + if req.PriorIdentity != nil { + resp.PlannedIdentity = &tfsdk.ResourceIdentity{ + Schema: req.PriorIdentity.Schema, + Raw: req.PriorIdentity.Raw.Copy(), + } + } + // Ensure that resp.PlannedPrivate is never nil. resp.PlannedPrivate = privatestate.EmptyData(ctx) @@ -304,9 +327,17 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange modifyPlanReq.ProviderMeta = *req.ProviderMeta } + if resp.PlannedIdentity != nil { + modifyPlanReq.Identity = &tfsdk.ResourceIdentity{ + Schema: resp.PlannedIdentity.Schema, + Raw: resp.PlannedIdentity.Raw.Copy(), + } + } + modifyPlanResp := resource.ModifyPlanResponse{ Diagnostics: resp.Diagnostics, Plan: modifyPlanReq.Plan, + Identity: modifyPlanReq.Identity, RequiresReplace: path.Paths{}, Private: modifyPlanReq.Private, } @@ -317,6 +348,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange resp.Diagnostics = modifyPlanResp.Diagnostics resp.PlannedState = planToState(modifyPlanResp.Plan) + resp.PlannedIdentity = modifyPlanResp.Identity resp.RequiresReplace = append(resp.RequiresReplace, modifyPlanResp.RequiresReplace...) resp.PlannedPrivate.Provider = modifyPlanResp.Private resp.Deferred = modifyPlanResp.Deferred @@ -338,6 +370,16 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange } } + if resp.PlannedIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Plan Response", + "An unexpected error was encountered when creating the plan response. New identity data was returned by the provider planning operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } + // Ensure deterministic RequiresReplace by sorting and deduplicating resp.RequiresReplace = NormaliseRequiresReplace(ctx, resp.RequiresReplace) diff --git a/internal/fwserver/server_planresourcechange_test.go b/internal/fwserver/server_planresourcechange_test.go index f1607daeb..e8cbf3ad7 100644 --- a/internal/fwserver/server_planresourcechange_test.go +++ b/internal/fwserver/server_planresourcechange_test.go @@ -24,6 +24,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/dynamicdefault" @@ -437,6 +438,12 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testIdentitySchemaType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchemaTypeWriteOnly := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_computed": tftypes.String, @@ -574,6 +581,14 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testSchemaWriteOnly := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -1107,6 +1122,10 @@ func TestServerPlanResourceChange(t *testing.T) { TestRequired types.String `tfsdk:"test_required"` } + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + type testSchemaDataBlock struct { TestRequired types.String `tfsdk:"test_required"` TestOptionalBlock types.Object `tfsdk:"test_optional_block"` @@ -3072,6 +3091,66 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "create-resourcewithmodifyplan-request-prioridentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentityAndModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + if data.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+data.TestID.ValueString()) + } + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "create-resourcewithmodifyplan-request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -3420,6 +3499,124 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testEmptyPrivate, }, }, + "create-resourcewithmodifyplan-response-plannedidentity-new": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + // Resource supports identity but there isn't one in state yet + PriorIdentity: nil, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentityAndModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if !req.Identity.Raw.IsNull() { + resp.Diagnostics.AddError("Unexpected request", "expected req.Identity to be null") + return + } + + data := testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + } + + resp.Diagnostics.Append(req.Identity.Set(ctx, &data)...) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, + "create-resourcewithmodifyplan-response-plannedidentity-update": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentityAndModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + data.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(req.Identity.Set(ctx, &data)...) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "create-resourcewithmodifyplan-response-private": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -3556,6 +3753,63 @@ func TestServerPlanResourceChange(t *testing.T) { PlannedPrivate: testPrivateProvider, }, }, + "create-resourcewithmodifyplan-response-invalid-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.PlanResourceChangeRequest{ + Config: &tfsdk.Config{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + ProposedNewState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PriorState: testEmptyState, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithModifyPlan{ + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + } + }, + }, + }, + expectedResponse: &fwserver.PlanResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Plan Response", + "An unexpected error was encountered when creating the plan response. New identity data was returned by the provider planning operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + PlannedState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + PlannedPrivate: testEmptyPrivate, + }, + }, "delete-resourcewithmodifyplan-request-config": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -5704,6 +5958,13 @@ func TestServerPlanResourceChange(t *testing.T) { }), Schema: testSchemaAttributePlanModifierAttributePlan, }, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, ResourceSchema: testSchemaAttributePlanModifierAttributePlan, Resource: &testprovider.Resource{}, }, @@ -5722,6 +5983,12 @@ func TestServerPlanResourceChange(t *testing.T) { }), Schema: testSchemaAttributePlanModifierAttributePlan, }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, PlannedPrivate: testEmptyPrivate, }, }, diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index d260e2912..1c56d44c5 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/logging" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" @@ -20,6 +21,8 @@ import ( // ReadResource RPC. type ReadResourceRequest struct { ClientCapabilities resource.ReadClientCapabilities + IdentitySchema fwschema.Schema + CurrentIdentity *tfsdk.ResourceIdentity CurrentState *tfsdk.State Resource resource.Resource Private *privatestate.Data @@ -32,6 +35,7 @@ type ReadResourceResponse struct { Deferred *resource.Deferred Diagnostics diag.Diagnostics NewState *tfsdk.State + NewIdentity *tfsdk.ResourceIdentity Private *privatestate.Data } @@ -115,12 +119,36 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res resp.Private = req.Private } + // If the resource supports identity and there is no current identity data, pre-populate with a null value. + // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? + if req.CurrentIdentity == nil && req.IdentitySchema != nil { + nullTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + req.CurrentIdentity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullTfValue.Copy(), + } + } + + if req.CurrentIdentity != nil { + readReq.Identity = &tfsdk.ResourceIdentity{ + Schema: req.CurrentIdentity.Schema, + Raw: req.CurrentIdentity.Raw.Copy(), + } + + readResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.CurrentIdentity.Schema, + Raw: req.CurrentIdentity.Raw.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource Read") req.Resource.Read(ctx, readReq, &readResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Read") resp.Diagnostics = readResp.Diagnostics resp.NewState = &readResp.State + resp.NewIdentity = readResp.Identity resp.Deferred = readResp.Deferred if readResp.Private != nil { @@ -135,6 +163,16 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res return } + if resp.NewIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Read Response", + "An unexpected error was encountered when creating the read response. New identity data was returned by the provider read operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } + semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionState, diff --git a/internal/fwserver/server_readresource_test.go b/internal/fwserver/server_readresource_test.go index 9f3aa7065..a846d389c 100644 --- a/internal/fwserver/server_readresource_test.go +++ b/internal/fwserver/server_readresource_test.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -34,6 +35,12 @@ func TestServerReadResource(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testTypeWriteOnly := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_write_only": tftypes.String, @@ -46,6 +53,10 @@ func TestServerReadResource(t *testing.T) { "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), }) + testCurrentIdentityValue := tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + testCurrentStateValueWriteOnly := tftypes.NewValue(testTypeWriteOnly, map[string]tftypes.Value{ "test_write_only": tftypes.NewValue(tftypes.String, nil), "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), @@ -56,6 +67,10 @@ func TestServerReadResource(t *testing.T) { "test_required": tftypes.NewValue(tftypes.String, "test-currentstate-value"), }) + testNewIdentityValue := tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }) + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -67,6 +82,14 @@ func TestServerReadResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testSchemaWriteOnly := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_write_only": schema.StringAttribute{ @@ -121,6 +144,11 @@ func TestServerReadResource(t *testing.T) { Schema: testSchema, } + testCurrentIdentity := &tfsdk.ResourceIdentity{ + Raw: testCurrentIdentityValue, + Schema: testIdentitySchema, + } + testCurrentStateWriteOnly := &tfsdk.State{ Raw: testCurrentStateValueWriteOnly, Schema: testSchemaWriteOnly, @@ -131,6 +159,11 @@ func TestServerReadResource(t *testing.T) { Schema: testSchema, } + testNewIdentity := &tfsdk.ResourceIdentity{ + Raw: testNewIdentityValue, + Schema: testIdentitySchema, + } + testNewStateRemoved := &tfsdk.State{ Raw: tftypes.NewValue(testType, nil), Schema: testSchema, @@ -249,6 +282,36 @@ func TestServerReadResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "request-currentidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + IdentitySchema: testIdentitySchema, + CurrentIdentity: testCurrentIdentity, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("unexpected req.Identity value: %s", identityData.TestID.ValueString()) + } + }, + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + NewIdentity: testCurrentIdentity, + Private: testEmptyPrivate, + }, + }, "request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -532,6 +595,99 @@ func TestServerReadResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-identity-new": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + // Resource supports identity but there isn't one in state yet + CurrentIdentity: nil, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + if !req.Identity.Raw.IsNull() { + resp.Diagnostics.AddError("Unexpected request", "expected req.Identity to be null") + return + } + + identityData := struct { + TestID types.String `tfsdk:"test_id"` + }{ + TestID: types.StringValue("new-id-123"), + } + + resp.Diagnostics.Append(resp.Identity.Set(ctx, &identityData)...) + }, + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + NewIdentity: testNewIdentity, + Private: testEmptyPrivate, + }, + }, + "response-identity-update": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + CurrentIdentity: testCurrentIdentity, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + identityData.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(resp.Identity.Set(ctx, &identityData)...) + }, + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + NewState: testCurrentState, + NewIdentity: testNewIdentity, + Private: testEmptyPrivate, + }, + }, + "response-invalid-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadResourceRequest{ + CurrentState: testCurrentState, + Resource: &testprovider.Resource{ + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: testNewIdentityValue, + Schema: testIdentitySchema, + } + }, + }, + }, + expectedResponse: &fwserver.ReadResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Read Response", + "An unexpected error was encountered when creating the read response. New identity data was returned by the provider read operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + NewState: testCurrentState, + NewIdentity: testNewIdentity, + Private: testEmptyPrivate, + }, + }, "response-state-removeresource": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/fwserver/server_updateresource.go b/internal/fwserver/server_updateresource.go index ad1d9f998..19827b7e3 100644 --- a/internal/fwserver/server_updateresource.go +++ b/internal/fwserver/server_updateresource.go @@ -20,13 +20,15 @@ import ( // UpdateResourceRequest is the framework server request for an update request // with the ApplyResourceChange RPC. type UpdateResourceRequest struct { - Config *tfsdk.Config - PlannedPrivate *privatestate.Data - PlannedState *tfsdk.Plan - PriorState *tfsdk.State - ProviderMeta *tfsdk.Config - ResourceSchema fwschema.Schema - Resource resource.Resource + Config *tfsdk.Config + PlannedPrivate *privatestate.Data + PlannedState *tfsdk.Plan + PlannedIdentity *tfsdk.ResourceIdentity + PriorState *tfsdk.State + ProviderMeta *tfsdk.Config + ResourceSchema fwschema.Schema + IdentitySchema fwschema.Schema + Resource resource.Resource } // UpdateResourceResponse is the framework server response for an update request @@ -34,6 +36,7 @@ type UpdateResourceRequest struct { type UpdateResourceResponse struct { Diagnostics diag.Diagnostics NewState *tfsdk.State + NewIdentity *tfsdk.ResourceIdentity Private *privatestate.Data } @@ -118,12 +121,37 @@ func (s *Server) UpdateResource(ctx context.Context, req *UpdateResourceRequest, resp.Private = req.PlannedPrivate } + // If the resource supports identity and there is no planned identity data, pre-populate with a null value. + // TODO:ResourceIdentity: This logic is likely useless since plan should already handle this, probably should remove. + if req.PlannedIdentity == nil && req.IdentitySchema != nil { + nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + req.PlannedIdentity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullIdentityTfValue.Copy(), + } + } + + // Pre-populate the new identity with the planned identity. + if req.PlannedIdentity != nil { + updateReq.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PlannedIdentity.Schema, + Raw: req.PlannedIdentity.Raw.Copy(), + } + + updateResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PlannedIdentity.Schema, + Raw: req.PlannedIdentity.Raw.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource Update") req.Resource.Update(ctx, updateReq, &updateResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Update") resp.Diagnostics = updateResp.Diagnostics resp.NewState = &updateResp.State + resp.NewIdentity = updateResp.Identity if !resp.Diagnostics.HasError() && updateResp.State.Raw.Equal(nullSchemaData) { resp.Diagnostics.AddError( @@ -145,6 +173,16 @@ func (s *Server) UpdateResource(ctx context.Context, req *UpdateResourceRequest, return } + if resp.NewIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Update Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider update operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } + semanticEqualityReq := SchemaSemanticEqualityRequest{ PriorData: fwschemadata.Data{ Description: fwschemadata.DataDescriptionPlan, diff --git a/internal/fwserver/server_updateresource_test.go b/internal/fwserver/server_updateresource_test.go index 4396e93f1..f17616703 100644 --- a/internal/fwserver/server_updateresource_test.go +++ b/internal/fwserver/server_updateresource_test.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" @@ -34,6 +35,12 @@ func TestServerUpdateResource(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchemaTypeWriteOnly := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test_required": tftypes.String, @@ -52,6 +59,14 @@ func TestServerUpdateResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testSchemaWithSemanticEquals := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -101,6 +116,10 @@ func TestServerUpdateResource(t *testing.T) { TestRequired types.String `tfsdk:"test_required"` } + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + type testSchemaDataWithSemanticEquals struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired testtypes.StringValueWithSemanticEquals `tfsdk:"test_required"` @@ -274,6 +293,63 @@ func TestServerUpdateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "request-plannedidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpdateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.UpdateResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "request-priorstate": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -699,6 +775,110 @@ func TestServerUpdateResource(t *testing.T) { Private: testEmptyPrivate, }, }, + "response-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpdateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.Append(resp.Identity.Set(ctx, testIdentitySchemaData{ + TestID: types.StringValue("new-id-123"), + })...) + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.UpdateResourceResponse{ + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, + "response-invalid-newidentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.UpdateResourceRequest{ + PlannedState: &tfsdk.Plan{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + UpdateMethod: func(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, + }, + }, + expectedResponse: &fwserver.UpdateResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Update Response", + "An unexpected error was encountered when creating the apply response. New identity data was returned by the provider update operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + NewIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + Schema: testIdentitySchema, + }, + NewState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + }), + Schema: testSchema, + }, + Private: testEmptyPrivate, + }, + }, "response-newstate-null": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, diff --git a/internal/proto5server/server_applyresourcechange.go b/internal/proto5server/server_applyresourcechange.go index e4e8bb92e..717d071f2 100644 --- a/internal/proto5server/server_applyresourcechange.go +++ b/internal/proto5server/server_applyresourcechange.go @@ -36,6 +36,14 @@ func (s *Server) ApplyResourceChange(ctx context.Context, proto5Req *tfprotov5.A return toproto5.ApplyResourceChangeResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ApplyResourceChangeResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -44,7 +52,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, proto5Req *tfprotov5.A return toproto5.ApplyResourceChangeResponse(ctx, fwResp), nil } - fwReq, diags := fromproto5.ApplyResourceChangeRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema) + fwReq, diags := fromproto5.ApplyResourceChangeRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto5server/server_applyresourcechange_test.go b/internal/proto5server/server_applyresourcechange_test.go index 54484786c..6c17d703b 100644 --- a/internal/proto5server/server_applyresourcechange_test.go +++ b/internal/proto5server/server_applyresourcechange_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -34,6 +35,20 @@ func TestServerApplyResourceChange(t *testing.T) { testEmptyDynamicValue, _ := tfprotov5.NewDynamicValue(testSchemaType, tftypes.NewValue(testSchemaType, nil)) + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testPlannedIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testNewIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }) + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -45,6 +60,14 @@ func TestServerApplyResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -194,6 +217,79 @@ func TestServerApplyResourceChange(t *testing.T) { }), }, }, + "create-request-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.ApplyResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testPlannedIdentityValue, + }, + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.ApplyResourceChangeResponse{ + NewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testPlannedIdentityValue, + }, + }, + }, "create-request-providermeta": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -372,6 +468,73 @@ func TestServerApplyResourceChange(t *testing.T) { }), }, }, + "create-response-newidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + identityData := struct { + TestID types.String `tfsdk:"test_id"` + }{ + TestID: types.StringValue("new-id-123"), + } + resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...) + + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.ApplyResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.ApplyResourceChangeResponse{ + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewIdentityDynamicValue, + }, + NewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + }, + }, "create-response-newstate-null": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto5server/server_planresourcechange.go b/internal/proto5server/server_planresourcechange.go index a5cec1987..68918c532 100644 --- a/internal/proto5server/server_planresourcechange.go +++ b/internal/proto5server/server_planresourcechange.go @@ -37,6 +37,14 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto5Req *tfprotov5.Pl return toproto5.PlanResourceChangeResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.PlanResourceChangeResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -53,7 +61,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto5Req *tfprotov5.Pl return toproto5.PlanResourceChangeResponse(ctx, fwResp), nil } - fwReq, diags := fromproto5.PlanResourceChangeRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema, resourceBehavior) + fwReq, diags := fromproto5.PlanResourceChangeRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema, resourceBehavior, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto5server/server_planresourcechange_test.go b/internal/proto5server/server_planresourcechange_test.go index fc452f6af..dbc9e609d 100644 --- a/internal/proto5server/server_planresourcechange_test.go +++ b/internal/proto5server/server_planresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -33,6 +34,12 @@ func TestServerPlanResourceChange(t *testing.T) { testEmptyDynamicValue, _ := tfprotov5.NewDynamicValue(testSchemaType, tftypes.NewValue(testSchemaType, nil)) + testIdentitySchemaType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -44,6 +51,18 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -179,6 +198,70 @@ func TestServerPlanResourceChange(t *testing.T) { }), }, }, + "create-request-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndModifyPlan{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + if data.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", data.TestID.ValueString()) + } + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.PlanResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.PlanResourceChangeResponse{ + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + }, + }, "create-request-providermeta": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -345,6 +428,70 @@ func TestServerPlanResourceChange(t *testing.T) { }), }, }, + "create-response-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndModifyPlan{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + data.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(resp.Identity.Set(ctx, &data)...) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.PlanResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorState: &testEmptyDynamicValue, + PriorIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.PlanResourceChangeResponse{ + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + }, + }, + }, "create-response-requiresreplace": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto5server/server_readresource.go b/internal/proto5server/server_readresource.go index e9863e148..299b67835 100644 --- a/internal/proto5server/server_readresource.go +++ b/internal/proto5server/server_readresource.go @@ -37,6 +37,14 @@ func (s *Server) ReadResource(ctx context.Context, proto5Req *tfprotov5.ReadReso return toproto5.ReadResourceResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ReadResourceResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -45,7 +53,7 @@ func (s *Server) ReadResource(ctx context.Context, proto5Req *tfprotov5.ReadReso return toproto5.ReadResourceResponse(ctx, fwResp), nil } - fwReq, diags := fromproto5.ReadResourceRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema) + fwReq, diags := fromproto5.ReadResourceRequest(ctx, proto5Req, resource, resourceSchema, providerMetaSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto5server/server_readresource_test.go b/internal/proto5server/server_readresource_test.go index 8fbba4647..9617d7b7a 100644 --- a/internal/proto5server/server_readresource_test.go +++ b/internal/proto5server/server_readresource_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -46,6 +47,20 @@ func TestServerReadResource(t *testing.T) { testNewStateRemovedDynamicValue, _ := tfprotov5.NewDynamicValue(testType, tftypes.NewValue(testType, nil)) + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testCurrentIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testNewIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }) + testProviderMetaDynamicValue := testNewDynamicValue(t, tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ @@ -70,6 +85,14 @@ func TestServerReadResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testCases := map[string]struct { server *Server request *tfprotov5.ReadResourceRequest @@ -246,6 +269,69 @@ func TestServerReadResource(t *testing.T) { }), }, }, + "request-currentidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.ProviderWithMetaSchema{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {}, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + MetaSchemaMethod: func(_ context.Context, _ provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { + resp.Schema = metaschema.Schema{ + Attributes: map[string]metaschema.Attribute{ + "test_optional": metaschema.StringAttribute{ + Optional: true, + }, + "test_required": metaschema.StringAttribute{ + Required: true, + }, + }, + } + }, + }, + }, + }, + request: &tfprotov5.ReadResourceRequest{ + CurrentState: testEmptyDynamicValue, + CurrentIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.ReadResourceResponse{ + NewState: testEmptyDynamicValue, + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + }, + }, "response-diagnostics": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -332,6 +418,55 @@ func TestServerReadResource(t *testing.T) { NewState: testNewStateDynamicValue, }, }, + "response-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {}, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + identityData.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...) + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.ReadResourceRequest{ + CurrentState: testEmptyDynamicValue, + CurrentIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov5.ReadResourceResponse{ + NewState: testEmptyDynamicValue, + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: testNewIdentityDynamicValue, + }, + }, + }, "response-state-removeresource": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto6server/server_applyresourcechange.go b/internal/proto6server/server_applyresourcechange.go index 0762368b1..85fc2dc11 100644 --- a/internal/proto6server/server_applyresourcechange.go +++ b/internal/proto6server/server_applyresourcechange.go @@ -36,6 +36,14 @@ func (s *Server) ApplyResourceChange(ctx context.Context, proto6Req *tfprotov6.A return toproto6.ApplyResourceChangeResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ApplyResourceChangeResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -44,7 +52,7 @@ func (s *Server) ApplyResourceChange(ctx context.Context, proto6Req *tfprotov6.A return toproto6.ApplyResourceChangeResponse(ctx, fwResp), nil } - fwReq, diags := fromproto6.ApplyResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema) + fwReq, diags := fromproto6.ApplyResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto6server/server_applyresourcechange_test.go b/internal/proto6server/server_applyresourcechange_test.go index 66c12933d..93b4f40e0 100644 --- a/internal/proto6server/server_applyresourcechange_test.go +++ b/internal/proto6server/server_applyresourcechange_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -34,6 +35,20 @@ func TestServerApplyResourceChange(t *testing.T) { testEmptyDynamicValue, _ := tfprotov6.NewDynamicValue(testSchemaType, tftypes.NewValue(testSchemaType, nil)) + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testPlannedIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testNewIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }) + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -45,6 +60,14 @@ func TestServerApplyResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -194,6 +217,79 @@ func TestServerApplyResourceChange(t *testing.T) { }), }, }, + "create-request-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + + // Prevent missing resource state error diagnostic + var data testSchemaData + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.ApplyResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testPlannedIdentityValue, + }, + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.ApplyResourceChangeResponse{ + NewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testPlannedIdentityValue, + }, + }, + }, "create-request-providermeta": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -372,6 +468,73 @@ func TestServerApplyResourceChange(t *testing.T) { }), }, }, + "create-response-newidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + CreateMethod: func(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + identityData := struct { + TestID types.String `tfsdk:"test_id"` + }{ + TestID: types.StringValue("new-id-123"), + } + resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...) + + var data testSchemaData + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + DeleteMethod: func(_ context.Context, _ resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Delete") + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Create, Got: Update") + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.ApplyResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.ApplyResourceChangeResponse{ + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewIdentityDynamicValue, + }, + NewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-plannedstate-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + }, + }, "create-response-newstate-null": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto6server/server_planresourcechange.go b/internal/proto6server/server_planresourcechange.go index 32d13ddd6..cdd057e2f 100644 --- a/internal/proto6server/server_planresourcechange.go +++ b/internal/proto6server/server_planresourcechange.go @@ -37,6 +37,14 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto6Req *tfprotov6.Pl return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -53,7 +61,7 @@ func (s *Server) PlanResourceChange(ctx context.Context, proto6Req *tfprotov6.Pl return toproto6.PlanResourceChangeResponse(ctx, fwResp), nil } - fwReq, diags := fromproto6.PlanResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, resourceBehavior) + fwReq, diags := fromproto6.PlanResourceChangeRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, resourceBehavior, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto6server/server_planresourcechange_test.go b/internal/proto6server/server_planresourcechange_test.go index 35b39c2e0..6cd50604f 100644 --- a/internal/proto6server/server_planresourcechange_test.go +++ b/internal/proto6server/server_planresourcechange_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-go/tfprotov6" @@ -32,6 +33,12 @@ func TestServerPlanResourceChange(t *testing.T) { testEmptyDynamicValue, _ := tfprotov6.NewDynamicValue(testSchemaType, tftypes.NewValue(testSchemaType, nil)) + testIdentitySchemaType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "test_computed": schema.StringAttribute{ @@ -43,6 +50,18 @@ func TestServerPlanResourceChange(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + + type testIdentitySchemaData struct { + TestID types.String `tfsdk:"test_id"` + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -178,6 +197,70 @@ func TestServerPlanResourceChange(t *testing.T) { }), }, }, + "create-request-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndModifyPlan{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + if data.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", data.TestID.ValueString()) + } + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.PlanResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + PriorState: &testEmptyDynamicValue, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.PlanResourceChangeResponse{ + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + }, + }, "create-request-providermeta": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -344,6 +427,70 @@ func TestServerPlanResourceChange(t *testing.T) { }), }, }, + "create-response-plannedidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndModifyPlan{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ModifyPlanMethod: func(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var data testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &data)...) + + data.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(resp.Identity.Set(ctx, &data)...) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.PlanResourceChangeRequest{ + Config: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + ProposedNewState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PriorState: &testEmptyDynamicValue, + PriorIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.PlanResourceChangeResponse{ + PlannedState: testNewDynamicValue(t, testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }), + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewDynamicValue(t, testIdentitySchemaType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }), + }, + }, + }, "create-response-requiresreplace": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto6server/server_readresource.go b/internal/proto6server/server_readresource.go index d5b0cfab5..89cec5523 100644 --- a/internal/proto6server/server_readresource.go +++ b/internal/proto6server/server_readresource.go @@ -36,6 +36,14 @@ func (s *Server) ReadResource(ctx context.Context, proto6Req *tfprotov6.ReadReso return toproto6.ReadResourceResponse(ctx, fwResp), nil } + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ReadResourceResponse(ctx, fwResp), nil + } + providerMetaSchema, diags := s.FrameworkServer.ProviderMetaSchema(ctx) fwResp.Diagnostics.Append(diags...) @@ -44,7 +52,7 @@ func (s *Server) ReadResource(ctx context.Context, proto6Req *tfprotov6.ReadReso return toproto6.ReadResourceResponse(ctx, fwResp), nil } - fwReq, diags := fromproto6.ReadResourceRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema) + fwReq, diags := fromproto6.ReadResourceRequest(ctx, proto6Req, resource, resourceSchema, providerMetaSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto6server/server_readresource_test.go b/internal/proto6server/server_readresource_test.go index 7e2180008..2f096bbef 100644 --- a/internal/proto6server/server_readresource_test.go +++ b/internal/proto6server/server_readresource_test.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -46,6 +47,20 @@ func TestServerReadResource(t *testing.T) { testNewStateRemovedDynamicValue, _ := tfprotov6.NewDynamicValue(testType, tftypes.NewValue(testType, nil)) + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testCurrentIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testNewIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "new-id-123"), + }) + testProviderMetaDynamicValue := testNewDynamicValue(t, tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ @@ -70,6 +85,14 @@ func TestServerReadResource(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testCases := map[string]struct { server *Server request *tfprotov6.ReadResourceRequest @@ -246,6 +269,69 @@ func TestServerReadResource(t *testing.T) { }), }, }, + "request-currentidentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.ProviderWithMetaSchema{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {}, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + MetaSchemaMethod: func(_ context.Context, _ provider.MetaSchemaRequest, resp *provider.MetaSchemaResponse) { + resp.Schema = metaschema.Schema{ + Attributes: map[string]metaschema.Attribute{ + "test_optional": metaschema.StringAttribute{ + Optional: true, + }, + "test_required": metaschema.StringAttribute{ + Required: true, + }, + }, + } + }, + }, + }, + }, + request: &tfprotov6.ReadResourceRequest{ + CurrentState: testEmptyDynamicValue, + CurrentIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.ReadResourceResponse{ + NewState: testEmptyDynamicValue, + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + }, + }, "response-diagnostics": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -332,6 +418,55 @@ func TestServerReadResource(t *testing.T) { NewState: testNewStateDynamicValue, }, }, + "response-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {}, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + ReadMethod: func(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + identityData.TestID = types.StringValue("new-id-123") + + resp.Diagnostics.Append(resp.Identity.Set(ctx, identityData)...) + }, + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.ReadResourceRequest{ + CurrentState: testEmptyDynamicValue, + CurrentIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testCurrentIdentityValue, + }, + TypeName: "test_resource", + }, + expectedResponse: &tfprotov6.ReadResourceResponse{ + NewState: testEmptyDynamicValue, + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: testNewIdentityDynamicValue, + }, + }, + }, "response-state-removeresource": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/testing/testprovider/resourcewithidentityandmodifyplan.go b/internal/testing/testprovider/resourcewithidentityandmodifyplan.go new file mode 100644 index 000000000..5a1dc8847 --- /dev/null +++ b/internal/testing/testprovider/resourcewithidentityandmodifyplan.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testprovider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.Resource = &ResourceWithIdentityAndModifyPlan{} +var _ resource.ResourceWithIdentity = &ResourceWithIdentityAndModifyPlan{} +var _ resource.ResourceWithModifyPlan = &ResourceWithIdentityAndModifyPlan{} + +// Declarative resource.ResourceWithIdentityAndModifyPlan for unit testing. +type ResourceWithIdentityAndModifyPlan struct { + *Resource + + // ResourceWithIdentity interface methods + IdentitySchemaMethod func(context.Context, resource.IdentitySchemaRequest, *resource.IdentitySchemaResponse) + + // ResourceWithModifyPlan interface methods + ModifyPlanMethod func(context.Context, resource.ModifyPlanRequest, *resource.ModifyPlanResponse) +} + +// IdentitySchema implements resource.ResourceWithIdentity. +func (p *ResourceWithIdentityAndModifyPlan) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + if p.IdentitySchemaMethod == nil { + return + } + + p.IdentitySchemaMethod(ctx, req, resp) +} + +// ModifyPlan satisfies the resource.ResourceWithModifyPlan interface. +func (r *ResourceWithIdentityAndModifyPlan) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + if r.ModifyPlanMethod == nil { + return + } + + r.ModifyPlanMethod(ctx, req, resp) +} diff --git a/internal/toproto5/applyresourcechange.go b/internal/toproto5/applyresourcechange.go index 289373999..1b5da0df6 100644 --- a/internal/toproto5/applyresourcechange.go +++ b/internal/toproto5/applyresourcechange.go @@ -27,6 +27,11 @@ func ApplyResourceChangeResponse(ctx context.Context, fw *fwserver.ApplyResource proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.NewState = newState + newIdentity, diags := ResourceIdentity(ctx, fw.NewIdentity) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.NewIdentity = newIdentity + newPrivate, diags := fw.Private.Bytes(ctx) proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto5/applyresourcechange_test.go b/internal/toproto5/applyresourcechange_test.go index 85f8c99dd..63911e17c 100644 --- a/internal/toproto5/applyresourcechange_test.go +++ b/internal/toproto5/applyresourcechange_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -38,6 +39,22 @@ func TestApplyResourceChangeResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + testState := &tfsdk.State{ Raw: testProto5Value, Schema: schema.Schema{ @@ -60,6 +77,28 @@ func TestApplyResourceChangeResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -131,6 +170,37 @@ func TestApplyResourceChangeResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-newidentity": { + input: &fwserver.ApplyResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + NewIdentity: testIdentityInvalid, + }, + expected: &tfprotov5.ApplyResourceChangeResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ApplyResourceChangeResponse{ NewState: testState, @@ -139,6 +209,16 @@ func TestApplyResourceChangeResponse(t *testing.T) { NewState: &testProto5DynamicValue, }, }, + "newidentity": { + input: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: testIdentity, + }, + expected: &tfprotov5.ApplyResourceChangeResponse{ + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + }, "private": { input: &fwserver.ApplyResourceChangeResponse{ Private: &privatestate.Data{ diff --git a/internal/toproto5/planresourcechange.go b/internal/toproto5/planresourcechange.go index f3292a963..23938df27 100644 --- a/internal/toproto5/planresourcechange.go +++ b/internal/toproto5/planresourcechange.go @@ -29,6 +29,11 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.PlannedState = plannedState + plannedIdentity, diags := ResourceIdentity(ctx, fw.PlannedIdentity) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.PlannedIdentity = plannedIdentity + requiresReplace, diags := totftypes.AttributePaths(ctx, fw.RequiresReplace) proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto5/planresourcechange_test.go b/internal/toproto5/planresourcechange_test.go index 05924b5b4..9506ba513 100644 --- a/internal/toproto5/planresourcechange_test.go +++ b/internal/toproto5/planresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -40,6 +41,22 @@ func TestPlanResourceChangeResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + testState := &tfsdk.State{ Raw: testProto5Value, Schema: schema.Schema{ @@ -62,6 +79,28 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -143,6 +182,37 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-plannedidentity": { + input: &fwserver.PlanResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + PlannedIdentity: testIdentityInvalid, + }, + expected: &tfprotov5.PlanResourceChangeResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "plannedprivate-empty": { input: &fwserver.PlanResourceChangeResponse{ PlannedPrivate: &privatestate.Data{ @@ -177,6 +247,16 @@ func TestPlanResourceChangeResponse(t *testing.T) { PlannedState: &testProto5DynamicValue, }, }, + "plannedidentity": { + input: &fwserver.PlanResourceChangeResponse{ + PlannedIdentity: testIdentity, + }, + expected: &tfprotov5.PlanResourceChangeResponse{ + PlannedIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + }, "requiresreplace": { input: &fwserver.PlanResourceChangeResponse{ RequiresReplace: path.Paths{ diff --git a/internal/toproto5/readresource.go b/internal/toproto5/readresource.go index 9193a3950..56695adae 100644 --- a/internal/toproto5/readresource.go +++ b/internal/toproto5/readresource.go @@ -28,6 +28,11 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.NewState = newState + newIdentity, diags := ResourceIdentity(ctx, fw.NewIdentity) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.NewIdentity = newIdentity + newPrivate, diags := fw.Private.Bytes(ctx) proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto5/readresource_test.go b/internal/toproto5/readresource_test.go index 2e9c549d3..8058f1c38 100644 --- a/internal/toproto5/readresource_test.go +++ b/internal/toproto5/readresource_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -39,6 +40,22 @@ func TestReadResourceResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -69,6 +86,28 @@ func TestReadResourceResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testDeferral := &resource.Deferred{ Reason: resource.DeferredReasonAbsentPrereq, } @@ -142,6 +181,37 @@ func TestReadResourceResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-newidentity": { + input: &fwserver.ReadResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + NewIdentity: testIdentityInvalid, + }, + expected: &tfprotov5.ReadResourceResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ReadResourceResponse{ NewState: testState, @@ -150,6 +220,16 @@ func TestReadResourceResponse(t *testing.T) { NewState: &testProto5DynamicValue, }, }, + "newidentity": { + input: &fwserver.ReadResourceResponse{ + NewIdentity: testIdentity, + }, + expected: &tfprotov5.ReadResourceResponse{ + NewIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + }, "private-empty": { input: &fwserver.ReadResourceResponse{ Private: &privatestate.Data{ diff --git a/internal/toproto5/resource_identity.go b/internal/toproto5/resource_identity.go new file mode 100644 index 000000000..eea046ac9 --- /dev/null +++ b/internal/toproto5/resource_identity.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +// ResourceIdentity returns the *tfprotov5.ResourceIdentityData for a *tfsdk.ResourceIdentity. +func ResourceIdentity(ctx context.Context, fw *tfsdk.ResourceIdentity) (*tfprotov5.ResourceIdentityData, diag.Diagnostics) { + if fw == nil { + return nil, nil + } + + identitySchemaData := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionResourceIdentity, + Schema: fw.Schema, + TerraformValue: fw.Raw, + } + + identityData, diags := DynamicValue(ctx, identitySchemaData) + if diags.HasError() { + return nil, diags + } + + return &tfprotov5.ResourceIdentityData{ + IdentityData: identityData, + }, nil +} diff --git a/internal/toproto5/resource_identity_test.go b/internal/toproto5/resource_identity_test.go new file mode 100644 index 000000000..ff62eff67 --- /dev/null +++ b/internal/toproto5/resource_identity_test.go @@ -0,0 +1,109 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestResourceIdentity(t *testing.T) { + t.Parallel() + + testProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_attribute": tftypes.String, + }, + } + + testProto5Value := tftypes.NewValue(testProto5Type, map[string]tftypes.Value{ + "test_attribute": tftypes.NewValue(tftypes.String, "test-value"), + }) + + testProto5DynamicValue, err := tfprotov5.NewDynamicValue(testProto5Type, testProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testResourceIdentity := &tfsdk.ResourceIdentity{ + Raw: testProto5Value, + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + Required: true, + Type: types.StringType, + }, + }, + }, + } + + testResourceIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testProto5Value, + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + Required: true, + Type: types.BoolType, + }, + }, + }, + } + + testCases := map[string]struct { + input *tfsdk.ResourceIdentity + expected *tfprotov5.ResourceIdentityData + expectedDiagnostics diag.Diagnostics + }{ + "nil": { + input: nil, + expected: nil, + }, + "invalid-schema": { + input: testResourceIdentityInvalid, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity to the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Unable to create DynamicValue: AttributeName(\"test_attribute\"): unexpected value type string, tftypes.Bool values must be of type bool", + ), + }, + }, + "valid": { + input: testResourceIdentity, + expected: &tfprotov5.ResourceIdentityData{ + IdentityData: &testProto5DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := toproto5.ResourceIdentity(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} diff --git a/internal/toproto6/applyresourcechange.go b/internal/toproto6/applyresourcechange.go index c3d158f0a..d47230d46 100644 --- a/internal/toproto6/applyresourcechange.go +++ b/internal/toproto6/applyresourcechange.go @@ -27,6 +27,11 @@ func ApplyResourceChangeResponse(ctx context.Context, fw *fwserver.ApplyResource proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.NewState = newState + newIdentity, diags := ResourceIdentity(ctx, fw.NewIdentity) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.NewIdentity = newIdentity + newPrivate, diags := fw.Private.Bytes(ctx) proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto6/applyresourcechange_test.go b/internal/toproto6/applyresourcechange_test.go index 5084841d1..19a9f6144 100644 --- a/internal/toproto6/applyresourcechange_test.go +++ b/internal/toproto6/applyresourcechange_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -38,6 +39,22 @@ func TestApplyResourceChangeResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + testState := &tfsdk.State{ Raw: testProto6Value, Schema: schema.Schema{ @@ -60,6 +77,28 @@ func TestApplyResourceChangeResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -131,6 +170,37 @@ func TestApplyResourceChangeResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-newidentity": { + input: &fwserver.ApplyResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + NewIdentity: testIdentityInvalid, + }, + expected: &tfprotov6.ApplyResourceChangeResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ApplyResourceChangeResponse{ NewState: testState, @@ -139,6 +209,16 @@ func TestApplyResourceChangeResponse(t *testing.T) { NewState: &testProto6DynamicValue, }, }, + "newidentity": { + input: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: testIdentity, + }, + expected: &tfprotov6.ApplyResourceChangeResponse{ + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + }, "private": { input: &fwserver.ApplyResourceChangeResponse{ Private: &privatestate.Data{ diff --git a/internal/toproto6/planresourcechange.go b/internal/toproto6/planresourcechange.go index 486f7ab02..cf9b2642a 100644 --- a/internal/toproto6/planresourcechange.go +++ b/internal/toproto6/planresourcechange.go @@ -29,6 +29,11 @@ func PlanResourceChangeResponse(ctx context.Context, fw *fwserver.PlanResourceCh proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.PlannedState = plannedState + plannedIdentity, diags := ResourceIdentity(ctx, fw.PlannedIdentity) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.PlannedIdentity = plannedIdentity + requiresReplace, diags := totftypes.AttributePaths(ctx, fw.RequiresReplace) proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto6/planresourcechange_test.go b/internal/toproto6/planresourcechange_test.go index 376ec1f9b..344ac0a21 100644 --- a/internal/toproto6/planresourcechange_test.go +++ b/internal/toproto6/planresourcechange_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -40,6 +41,22 @@ func TestPlanResourceChangeResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + testState := &tfsdk.State{ Raw: testProto6Value, Schema: schema.Schema{ @@ -62,6 +79,28 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -143,6 +182,37 @@ func TestPlanResourceChangeResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-plannedidentity": { + input: &fwserver.PlanResourceChangeResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + PlannedIdentity: testIdentityInvalid, + }, + expected: &tfprotov6.PlanResourceChangeResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "plannedprivate-empty": { input: &fwserver.PlanResourceChangeResponse{ PlannedPrivate: &privatestate.Data{ @@ -177,6 +247,16 @@ func TestPlanResourceChangeResponse(t *testing.T) { PlannedState: &testProto6DynamicValue, }, }, + "plannedidentity": { + input: &fwserver.PlanResourceChangeResponse{ + PlannedIdentity: testIdentity, + }, + expected: &tfprotov6.PlanResourceChangeResponse{ + PlannedIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + }, "requiresreplace": { input: &fwserver.PlanResourceChangeResponse{ RequiresReplace: path.Paths{ diff --git a/internal/toproto6/readresource.go b/internal/toproto6/readresource.go index 2de7adf84..ea60e5a0f 100644 --- a/internal/toproto6/readresource.go +++ b/internal/toproto6/readresource.go @@ -28,6 +28,11 @@ func ReadResourceResponse(ctx context.Context, fw *fwserver.ReadResourceResponse proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.NewState = newState + newIdentity, diags := ResourceIdentity(ctx, fw.NewIdentity) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.NewIdentity = newIdentity + newPrivate, diags := fw.Private.Bytes(ctx) proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto6/readresource_test.go b/internal/toproto6/readresource_test.go index 18842699e..0596dc30c 100644 --- a/internal/toproto6/readresource_test.go +++ b/internal/toproto6/readresource_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -39,6 +40,22 @@ func TestReadResourceResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + testState := &tfsdk.State{ Raw: testProto6Value, Schema: schema.Schema{ @@ -61,6 +78,28 @@ func TestReadResourceResponse(t *testing.T) { }, } + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -142,6 +181,37 @@ func TestReadResourceResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-newidentity": { + input: &fwserver.ReadResourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + NewIdentity: testIdentityInvalid, + }, + expected: &tfprotov6.ReadResourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ReadResourceResponse{ NewState: testState, @@ -150,6 +220,16 @@ func TestReadResourceResponse(t *testing.T) { NewState: &testProto6DynamicValue, }, }, + "newidentity": { + input: &fwserver.ReadResourceResponse{ + NewIdentity: testIdentity, + }, + expected: &tfprotov6.ReadResourceResponse{ + NewIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + }, "private-empty": { input: &fwserver.ReadResourceResponse{ Private: &privatestate.Data{ diff --git a/internal/toproto6/resource_identity.go b/internal/toproto6/resource_identity.go new file mode 100644 index 000000000..3dac3de99 --- /dev/null +++ b/internal/toproto6/resource_identity.go @@ -0,0 +1,35 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6 + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +// ResourceIdentity returns the *tfprotov6.ResourceIdentityData for a *tfsdk.ResourceIdentity. +func ResourceIdentity(ctx context.Context, fw *tfsdk.ResourceIdentity) (*tfprotov6.ResourceIdentityData, diag.Diagnostics) { + if fw == nil { + return nil, nil + } + + identitySchemaData := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionResourceIdentity, + Schema: fw.Schema, + TerraformValue: fw.Raw, + } + + identityData, diags := DynamicValue(ctx, identitySchemaData) + if diags.HasError() { + return nil, diags + } + + return &tfprotov6.ResourceIdentityData{ + IdentityData: identityData, + }, nil +} diff --git a/internal/toproto6/resource_identity_test.go b/internal/toproto6/resource_identity_test.go new file mode 100644 index 000000000..699e29b64 --- /dev/null +++ b/internal/toproto6/resource_identity_test.go @@ -0,0 +1,109 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto6_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestResourceIdentity(t *testing.T) { + t.Parallel() + + testProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_attribute": tftypes.String, + }, + } + + testProto6Value := tftypes.NewValue(testProto6Type, map[string]tftypes.Value{ + "test_attribute": tftypes.NewValue(tftypes.String, "test-value"), + }) + + testProto6DynamicValue, err := tfprotov6.NewDynamicValue(testProto6Type, testProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testResourceIdentity := &tfsdk.ResourceIdentity{ + Raw: testProto6Value, + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + Required: true, + Type: types.StringType, + }, + }, + }, + } + + testResourceIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testProto6Value, + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + Required: true, + Type: types.BoolType, + }, + }, + }, + } + + testCases := map[string]struct { + input *tfsdk.ResourceIdentity + expected *tfprotov6.ResourceIdentityData + expectedDiagnostics diag.Diagnostics + }{ + "nil": { + input: nil, + expected: nil, + }, + "invalid-schema": { + input: testResourceIdentityInvalid, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity to the protocol type. "+ + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Unable to create DynamicValue: AttributeName(\"test_attribute\"): unexpected value type string, tftypes.Bool values must be of type bool", + ), + }, + }, + "valid": { + input: testResourceIdentity, + expected: &tfprotov6.ResourceIdentityData{ + IdentityData: &testProto6DynamicValue, + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := toproto6.ResourceIdentity(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiagnostics); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} diff --git a/resource/create.go b/resource/create.go index 8831f8370..920cc5dd3 100644 --- a/resource/create.go +++ b/resource/create.go @@ -23,6 +23,10 @@ type CreateRequest struct { // Plan is the planned state for the resource. Plan tfsdk.Plan + // Identity is the planned identity for the resource. If the resource does not + // support identity, this value will not be set. + Identity *tfsdk.ResourceIdentity + // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config } @@ -37,6 +41,14 @@ type CreateResponse struct { // should be set during the resource's Create operation. State tfsdk.State + // Identity is the identity of the resource following the Create operation. + // This field is pre-populated from CreateRequest.Identity and + // should be set during the resource's Create operation. + // + // If the resource does not support identity, this value will not be set and will + // raise a diagnostic if set by the resource's Create operation. + Identity *tfsdk.ResourceIdentity + // Private is the private state resource data following the Create operation. // This field is not pre-populated as there is no pre-existing private state // data during the resource's Create operation. diff --git a/resource/delete.go b/resource/delete.go index ab81a6c92..8281dffa1 100644 --- a/resource/delete.go +++ b/resource/delete.go @@ -33,10 +33,13 @@ type DeleteRequest struct { // should set values on the DeleteResponse as appropriate. type DeleteResponse struct { // State is the state of the resource following the Delete operation. - // This field is pre-populated from UpdateResourceRequest.Plan and - // should be set during the resource's Update operation. + // This field is pre-populated from DeleteRequest.State and + // should be set during the resource's Delete operation. State tfsdk.State + // Identity is the identity of the resource following the Delete operation. + Identity *tfsdk.ResourceIdentity + // Private is the private state resource data following the Delete // operation. This field is pre-populated from DeleteRequest.Private and // can be modified during the resource's Delete operation in cases where diff --git a/resource/modify_plan.go b/resource/modify_plan.go index 28843cec1..0cc71bea9 100644 --- a/resource/modify_plan.go +++ b/resource/modify_plan.go @@ -35,6 +35,10 @@ type ModifyPlanRequest struct { // State is the current state of the resource. State tfsdk.State + // Identity is the current identity of the resource. If the resource does not + // support identity, this value will not be set. + Identity *tfsdk.ResourceIdentity + // Plan is the planned new state for the resource. Terraform 1.3 and later // supports resource destroy planning, in which this will contain a null // value. @@ -65,6 +69,13 @@ type ModifyPlanResponse struct { // Plan is the planned new state for the resource. Plan tfsdk.Plan + // Identity is the planned new identity of the resource. + // This field is pre-populated from ModifyPlanRequest.Identity. + // + // If the resource does not support identity, this value will not be set and will + // raise a diagnostic if set. + Identity *tfsdk.ResourceIdentity + // RequiresReplace is a list of attribute paths that require the // resource to be replaced. They should point to the specific field // that changed that requires the resource to be destroyed and diff --git a/resource/read.go b/resource/read.go index 53c4cb832..74820e333 100644 --- a/resource/read.go +++ b/resource/read.go @@ -30,6 +30,10 @@ type ReadRequest struct { // operation. State tfsdk.State + // Identity is the current identity of the resource prior to the Read + // operation. If the resource does not support identity, this value will not be set. + Identity *tfsdk.ResourceIdentity + // Private is provider-defined resource private state data which was previously // stored with the resource state. This data is opaque to Terraform and does // not affect plan output. Any existing data is copied to @@ -57,6 +61,14 @@ type ReadResponse struct { // should be set during the resource's Read operation. State tfsdk.State + // Identity is the identity of the resource following the Read operation. + // This field is pre-populated from ReadRequest.Identity and + // should be set during the resource's Read operation. + // + // If the resource does not support identity, this value will not be set and will + // raise a diagnostic if set by the resource's Read operation. + Identity *tfsdk.ResourceIdentity + // Private is the private state resource data following the Read operation. // This field is pre-populated from ReadResourceRequest.Private and // can be modified during the resource's Read operation. diff --git a/resource/update.go b/resource/update.go index 0dceaf8ab..d832b24c3 100644 --- a/resource/update.go +++ b/resource/update.go @@ -27,6 +27,10 @@ type UpdateRequest struct { // operation. State tfsdk.State + // Identity is the planned identity for the resource. If the resource does not + // support identity, this value will not be set. + Identity *tfsdk.ResourceIdentity + // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config @@ -49,6 +53,14 @@ type UpdateResponse struct { // should be set during the resource's Update operation. State tfsdk.State + // Identity is the identity of the resource following the Update operation. + // This field is pre-populated from UpdateRequest.Identity and + // should be set during the resource's Update operation. + // + // If the resource does not support identity, this value will not be set and will + // raise a diagnostic if set by the resource's Update operation. + Identity *tfsdk.ResourceIdentity + // Private is the private state resource data following the Update operation. // This field is pre-populated from UpdateRequest.Private and // can be modified during the resource's Update operation. diff --git a/tfsdk/resource_identity.go b/tfsdk/resource_identity.go new file mode 100644 index 000000000..d02f1e8e3 --- /dev/null +++ b/tfsdk/resource_identity.go @@ -0,0 +1,91 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tfsdk + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +// ResourceIdentity represents the identity data for a managed resource. +type ResourceIdentity struct { + Raw tftypes.Value + Schema fwschema.Schema +} + +// Get populates the struct passed as `target` with the entire identity. +func (s ResourceIdentity) Get(ctx context.Context, target interface{}) diag.Diagnostics { + return s.data().Get(ctx, target) +} + +// GetAttribute retrieves the attribute found at `path` and populates +// the `target` with the value. +// +// Elements under null or unknown collections return null values, however this +// behavior is not protected by compatibility promises. +func (s ResourceIdentity) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { + return s.data().GetAtPath(ctx, path, target) +} + +// PathMatches returns all matching path.Paths from the given path.Expression. +// +// If a parent path is null or unknown, which would prevent a full expression +// from matching, the parent path is returned rather than no match to prevent +// false positives. +func (s ResourceIdentity) PathMatches(ctx context.Context, pathExpr path.Expression) (path.Paths, diag.Diagnostics) { + return s.data().PathMatches(ctx, pathExpr) +} + +// Set populates the entire identity using the supplied Go value. The value `val` +// should be a struct whose values have one of the attr.Value types. Each field +// must be tagged with the corresponding schema field. +func (s *ResourceIdentity) Set(ctx context.Context, val interface{}) diag.Diagnostics { + data := s.data() + diags := data.Set(ctx, val) + + if diags.HasError() { + return diags + } + + s.Raw = data.TerraformValue + + return diags +} + +// SetAttribute sets the attribute at `path` using the supplied Go value. +// +// The attribute path and value must be valid with the current schema. If the +// attribute path already has a value, it will be overwritten. If the attribute +// path does not have a value, it will be added. +// +// The value must not be an untyped nil. Use a typed nil or types package null +// value function instead. For example with a types.StringType attribute, +// use (*string)(nil) or types.StringNull(). +// +// Lists can only have the next element added according to the current length. +func (s *ResourceIdentity) SetAttribute(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics { + data := s.data() + diags := data.SetAtPath(ctx, path, val) + + if diags.HasError() { + return diags + } + + s.Raw = data.TerraformValue + + return diags +} + +func (s ResourceIdentity) data() *fwschemadata.Data { + return &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionResourceIdentity, + Schema: s.Schema, + TerraformValue: s.Raw, + } +} diff --git a/tfsdk/resource_identity_test.go b/tfsdk/resource_identity_test.go new file mode 100644 index 000000000..e73d07872 --- /dev/null +++ b/tfsdk/resource_identity_test.go @@ -0,0 +1,482 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package tfsdk_test + +import ( + "context" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + intreflect "github.com/hashicorp/terraform-plugin-framework/internal/reflect" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func TestResourceIdentityGet(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + resourceIdentity tfsdk.ResourceIdentity + target any + expected any + expectedDiags diag.Diagnostics + }{ + // Refer to fwschemadata.TestDataGet for more exhaustive unit testing. + // These test cases are to ensure ResourceIdentity schema and data values are + // passed appropriately to the shared implementation. + "valid": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "string": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "string": tftypes.NewValue(tftypes.String, "test"), + }, + ), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "string": testschema.Attribute{ + RequiredForImport: true, + Type: types.StringType, + }, + }, + }, + }, + target: new(struct { + String types.String `tfsdk:"string"` + }), + expected: &struct { + String types.String `tfsdk:"string"` + }{ + String: types.StringValue("test"), + }, + }, + "diagnostic": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "bool": tftypes.Bool, + }, + }, + map[string]tftypes.Value{ + "bool": tftypes.NewValue(tftypes.Bool, nil), + }, + ), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "bool": testschema.Attribute{ + RequiredForImport: true, + Type: types.BoolType, + }, + }, + }, + }, + target: new(struct { + String types.String `tfsdk:"bool"` + }), + expected: &struct { + String types.String `tfsdk:"bool"` + }{ + String: types.String{}, + }, + expectedDiags: diag.Diagnostics{ + diag.WithPath( + path.Root("bool"), + intreflect.DiagNewAttributeValueIntoWrongType{ + ValType: reflect.TypeOf(types.Bool{}), + TargetType: reflect.TypeOf(types.String{}), + SchemaType: types.BoolType, + }, + ), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := testCase.resourceIdentity.Get(context.Background(), testCase.target) + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff) + } + + if diff := cmp.Diff(testCase.target, testCase.expected); diff != "" { + t.Errorf("unexpected value (+wanted, -got): %s", diff) + } + }) + } +} + +func TestResourceIdentityGetAttribute(t *testing.T) { + t.Parallel() + + type testCase struct { + resourceIdentity tfsdk.ResourceIdentity + target interface{} + expected interface{} + expectedDiags diag.Diagnostics + } + + testCases := map[string]testCase{ + // Refer to fwschemadata.TestDataGetAtPath for more exhaustive unit + // testing. These test cases are to ensure ResourceIdentity schema and data values + // are passed appropriately to the shared implementation. + "valid": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "namevalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "name": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + }, + }, + }, + target: new(string), + expected: pointer("namevalue"), + }, + "diagnostics": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "namevalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "name": testschema.Attribute{ + Type: testtypes.StringTypeWithValidateWarning{}, + RequiredForImport: true, + }, + }, + }, + }, + target: new(testtypes.String), + expected: &testtypes.String{InternalString: types.StringValue("namevalue"), CreatedBy: testtypes.StringTypeWithValidateWarning{}}, + expectedDiags: diag.Diagnostics{testtypes.TestWarningDiagnostic(path.Root("name"))}, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := tc.resourceIdentity.GetAttribute(context.Background(), path.Root("name"), tc.target) + + if diff := cmp.Diff(diags, tc.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff) + } + + if diff := cmp.Diff(tc.target, tc.expected, cmp.Transformer("testtypes", func(in *testtypes.String) testtypes.String { return *in }), cmp.Transformer("types", func(in *types.String) types.String { return *in })); diff != "" { + t.Errorf("unexpected value (+wanted, -got): %s", diff) + } + }) + } +} + +func TestResourceIdentityPathMatches(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + resourceIdentity tfsdk.ResourceIdentity + expression path.Expression + expected path.Paths + expectedDiags diag.Diagnostics + }{ + // Refer to fwschemadata.TestDataPathMatches for more exhaustive unit testing. + // These test cases are to ensure ResourceIdentity schema and data values are + // passed appropriately to the shared implementation. + "AttributeNameExact-match": { + resourceIdentity: tfsdk.ResourceIdentity{ + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + }, + }, + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "test-value"), + }, + ), + }, + expression: path.MatchRoot("test"), + expected: path.Paths{ + path.Root("test"), + }, + }, + "AttributeNameExact-mismatch": { + resourceIdentity: tfsdk.ResourceIdentity{ + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + }, + }, + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "test-value"), + }, + ), + }, + expression: path.MatchRoot("not-test"), + expected: nil, + expectedDiags: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Path Expression for Schema", + "The Terraform Provider unexpectedly provided a path expression that does not match the current schema. "+ + "This can happen if the path expression does not correctly follow the schema in structure or types. "+ + "Please report this to the provider developers.\n\n"+ + "Path Expression: not-test", + ), + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := testCase.resourceIdentity.PathMatches(context.Background(), testCase.expression) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + +func TestResourceIdentitySet(t *testing.T) { + t.Parallel() + + type testCase struct { + resourceIdentity tfsdk.ResourceIdentity + val interface{} + expected tftypes.Value + expectedDiags diag.Diagnostics + } + + testCases := map[string]testCase{ + // Refer to fwschemadata.TestDataSet for more exhaustive unit testing. + // These test cases are to ensure ResourceIdentity schema and data values are + // passed appropriately to the shared implementation. + "valid": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "oldvalue"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "name": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + }, + }, + }, + val: struct { + Name string `tfsdk:"name"` + }{ + Name: "newvalue", + }, + expected: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "newvalue"), + }), + }, + "diagnostics": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.Value{}, + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "name": testschema.Attribute{ + Type: testtypes.StringTypeWithValidateWarning{}, + RequiredForImport: true, + }, + }, + }, + }, + val: struct { + Name string `tfsdk:"name"` + }{ + Name: "newvalue", + }, + expected: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "newvalue"), + }), + expectedDiags: diag.Diagnostics{testtypes.TestWarningDiagnostic(path.Root("name"))}, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := tc.resourceIdentity.Set(context.Background(), tc.val) + + if diff := cmp.Diff(diags, tc.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff) + } + + if diff := cmp.Diff(tc.resourceIdentity.Raw, tc.expected); diff != "" { + t.Errorf("unexpected value (+wanted, -got): %s", diff) + } + }) + } +} + +func TestResourceIdentitySetAttribute(t *testing.T) { + t.Parallel() + + type testCase struct { + resourceIdentity tfsdk.ResourceIdentity + path path.Path + val interface{} + expected tftypes.Value + expectedDiags diag.Diagnostics + } + + testCases := map[string]testCase{ + // Refer to fwschemadata.TestDataSetAtPath for more exhaustive unit + // testing. These test cases are to ensure ResourceIdentity schema and data values + // are passed appropriately to the shared implementation. + "valid": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + "other": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "originalvalue"), + "other": tftypes.NewValue(tftypes.String, "should be untouched"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test": testschema.Attribute{ + Type: types.StringType, + RequiredForImport: true, + }, + "other": testschema.Attribute{ + Type: types.StringType, + OptionalForImport: true, + }, + }, + }, + }, + path: path.Root("test"), + val: "newvalue", + expected: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + "other": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newvalue"), + "other": tftypes.NewValue(tftypes.String, "should be untouched"), + }), + }, + "diagnostics": { + resourceIdentity: tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "originalname"), + }), + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "name": testschema.Attribute{ + Type: testtypes.StringTypeWithValidateWarning{}, + RequiredForImport: true, + }, + }, + }, + }, + path: path.Root("name"), + val: "newname", + expected: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "newname"), + }), + expectedDiags: diag.Diagnostics{ + testtypes.TestWarningDiagnostic(path.Root("name")), + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + diags := tc.resourceIdentity.SetAttribute(context.Background(), tc.path, tc.val) + + if diff := cmp.Diff(diags, tc.expectedDiags); diff != "" { + for _, diagnostic := range diags { + t.Log(diagnostic) + } + t.Errorf("unexpected diagnostics (+wanted, -got): %s", diff) + } + + if diff := cmp.Diff(tc.resourceIdentity.Raw, tc.expected); diff != "" { + t.Errorf("unexpected value (+wanted, -got): %s", diff) + } + }) + } +} From 34934f712792fe618da12ae9171b7921bbc87f14 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Tue, 18 Mar 2025 16:35:35 -0400 Subject: [PATCH 28/42] chore: Prep for `v1.15.0-alpha.1` pre-release (#1115) --- .changes/unreleased/NOTES-20250318-161843.yaml | 8 ++++++++ .goreleaser.yml | 1 + go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 .changes/unreleased/NOTES-20250318-161843.yaml diff --git a/.changes/unreleased/NOTES-20250318-161843.yaml b/.changes/unreleased/NOTES-20250318-161843.yaml new file mode 100644 index 000000000..9f6fafc22 --- /dev/null +++ b/.changes/unreleased/NOTES-20250318-161843.yaml @@ -0,0 +1,8 @@ +kind: NOTES +body: This alpha pre-release contains an initial implementation for managed resource identity, which can used with Terraform v1.12.0-alpha20250312, + to store and read identity data during plan and apply workflows. A managed resource identity can be used by implementing the + optional `resource.ResourceWithIdentity` interface and defining an identity schema. Once the identity schema is defined, you can + read and store identity data in the state file via the new `Identity` fields in the response objects on the resource CRUD methods. +time: 2025-03-18T16:18:43.661104-04:00 +custom: + Issue: "1112" diff --git a/.goreleaser.yml b/.goreleaser.yml index e8f5fee93..78d877e55 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -5,5 +5,6 @@ builds: milestones: - close: true release: + prerelease: auto ids: - 'none' diff --git a/go.mod b/go.mod index 3687dc3cd..68acdcea9 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.23.7 require ( github.com/google/go-cmp v0.7.0 - github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250314120210-d406d3409aef + github.com/hashicorp/terraform-plugin-go v0.27.0-alpha.1 github.com/hashicorp/terraform-plugin-log v0.9.0 ) diff --git a/go.sum b/go.sum index 975600260..cf2a09e6c 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0U github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= 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.26.1-0.20250314120210-d406d3409aef h1:RtNtj/RAsw/ef2bpeMlCDo+HsREcQVyJ+20AzikH3kw= -github.com/hashicorp/terraform-plugin-go v0.26.1-0.20250314120210-d406d3409aef/go.mod h1:MfDwS/KnIy2QzCwdRtuqIjZ23gpYa9Vm+Z8cFpx8qtU= +github.com/hashicorp/terraform-plugin-go v0.27.0-alpha.1 h1:/IZFNUEafGnJGXRe2iNQQ+vtzEw/5qiD+gOxkFrNbi4= +github.com/hashicorp/terraform-plugin-go v0.27.0-alpha.1/go.mod h1:Tf2HngbyKvovAlGXgBOVGm3EDvbNaN/StUaTXwrej4o= 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.4 h1:JXu/zHB2Ymg/TGVCRu10XqNa4Sh2bWcqCNyKWjnCPJA= From f01eb331c102f536ee3b93c7a6066edbabb815fb Mon Sep 17 00:00:00 2001 From: hc-github-team-tf-provider-devex Date: Tue, 18 Mar 2025 20:39:55 +0000 Subject: [PATCH 29/42] Update changelog --- .changes/1.15.0-alpha.1.md | 7 +++++++ .changes/unreleased/NOTES-20250317-153311.yaml | 7 ------- .changes/unreleased/NOTES-20250318-161843.yaml | 8 -------- CHANGELOG.md | 7 +++++++ 4 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 .changes/1.15.0-alpha.1.md delete mode 100644 .changes/unreleased/NOTES-20250317-153311.yaml delete mode 100644 .changes/unreleased/NOTES-20250318-161843.yaml diff --git a/.changes/1.15.0-alpha.1.md b/.changes/1.15.0-alpha.1.md new file mode 100644 index 000000000..0c6f4f9cf --- /dev/null +++ b/.changes/1.15.0-alpha.1.md @@ -0,0 +1,7 @@ +## 1.15.0-alpha.1 (March 18, 2025) + +NOTES: + +* all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#1114](https://github.com/hashicorp/terraform-plugin-framework/issues/1114)) +* This alpha pre-release contains an initial implementation for managed resource identity, which can used with Terraform v1.12.0-alpha20250312, to store and read identity data during plan and apply workflows. A managed resource identity can be used by implementing the optional `resource.ResourceWithIdentity` interface and defining an identity schema. Once the identity schema is defined, you can read and store identity data in the state file via the new `Identity` fields in the response objects on the resource CRUD methods. ([#1112](https://github.com/hashicorp/terraform-plugin-framework/issues/1112)) + diff --git a/.changes/unreleased/NOTES-20250317-153311.yaml b/.changes/unreleased/NOTES-20250317-153311.yaml deleted file mode 100644 index 032cbf254..000000000 --- a/.changes/unreleased/NOTES-20250317-153311.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: NOTES -body: 'all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). - It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) - before upgrading. Any consumers building on earlier Go versions may experience errors.' -time: 2025-03-17T15:33:11.058742-04:00 -custom: - Issue: "1114" diff --git a/.changes/unreleased/NOTES-20250318-161843.yaml b/.changes/unreleased/NOTES-20250318-161843.yaml deleted file mode 100644 index 9f6fafc22..000000000 --- a/.changes/unreleased/NOTES-20250318-161843.yaml +++ /dev/null @@ -1,8 +0,0 @@ -kind: NOTES -body: This alpha pre-release contains an initial implementation for managed resource identity, which can used with Terraform v1.12.0-alpha20250312, - to store and read identity data during plan and apply workflows. A managed resource identity can be used by implementing the - optional `resource.ResourceWithIdentity` interface and defining an identity schema. Once the identity schema is defined, you can - read and store identity data in the state file via the new `Identity` fields in the response objects on the resource CRUD methods. -time: 2025-03-18T16:18:43.661104-04:00 -custom: - Issue: "1112" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e7c35c6a..fd1a6381d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.15.0-alpha.1 (March 18, 2025) + +NOTES: + +* all: This Go module has been updated to Go 1.23 per the [Go support policy](https://go.dev/doc/devel/release#policy). It is recommended to review the [Go 1.23 release notes](https://go.dev/doc/go1.23) before upgrading. Any consumers building on earlier Go versions may experience errors. ([#1114](https://github.com/hashicorp/terraform-plugin-framework/issues/1114)) +* This alpha pre-release contains an initial implementation for managed resource identity, which can used with Terraform v1.12.0-alpha20250312, to store and read identity data during plan and apply workflows. A managed resource identity can be used by implementing the optional `resource.ResourceWithIdentity` interface and defining an identity schema. Once the identity schema is defined, you can read and store identity data in the state file via the new `Identity` fields in the response objects on the resource CRUD methods. ([#1112](https://github.com/hashicorp/terraform-plugin-framework/issues/1112)) + ## 1.14.1 (February 20, 2025) BUG FIXES: From 15bac5c1758c2f94b904e9a1003cb426a5269830 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:55:10 -0400 Subject: [PATCH 30/42] build(deps): Bump github.com/hashicorp/copywrite in /tools (#1116) Bumps [github.com/hashicorp/copywrite](https://github.com/hashicorp/copywrite) from 0.21.0 to 0.22.0. - [Release notes](https://github.com/hashicorp/copywrite/releases) - [Changelog](https://github.com/hashicorp/copywrite/blob/main/.goreleaser.yaml) - [Commits](https://github.com/hashicorp/copywrite/compare/v0.21.0...v0.22.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/copywrite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 5c4a1c97d..2d37dd783 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -2,7 +2,7 @@ module tools go 1.23.7 -require github.com/hashicorp/copywrite v0.21.0 +require github.com/hashicorp/copywrite v0.22.0 require ( github.com/AlecAivazis/survey/v2 v2.3.7 // indirect diff --git a/tools/go.sum b/tools/go.sum index 797116e3d..18847da43 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -146,8 +146,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/copywrite v0.21.0 h1:IE8uByQdos8s0uAyHF4O8RHV5cJEhmIc+Awk+wkKXKI= -github.com/hashicorp/copywrite v0.21.0/go.mod h1:mu6DAyUI6m6vq8weoJn9a0HDuUUrV+0GQdRp4mD50yU= +github.com/hashicorp/copywrite v0.22.0 h1:mqjMrgP3VptS7aLbu2l39rtznoK+BhphHst6i7HiTAo= +github.com/hashicorp/copywrite v0.22.0/go.mod h1:FqvGJt2+yoYDpVYgFSdg3R2iyhkCVaBmPMhfso0MR2k= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= From 476a8b1f3008d00f37b292cda7d10f8f1d2b8f8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 11:11:16 -0400 Subject: [PATCH 31/42] build(deps): Bump github.com/golang-jwt/jwt/v4 in /tools (#1119) Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.1 to 4.5.2. - [Release notes](https://github.com/golang-jwt/jwt/releases) - [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md) - [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.1...v4.5.2) --- updated-dependencies: - dependency-name: github.com/golang-jwt/jwt/v4 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 2d37dd783..6d6e8a5da 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -17,7 +17,7 @@ require ( github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-openapi/errors v0.20.2 // indirect github.com/go-openapi/strfmt v0.21.3 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-github/v45 v45.2.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 18847da43..78e515527 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -96,8 +96,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= From 8ab70d039eaac0d4bfc32b56dcee6942aff18951 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 11:39:38 -0400 Subject: [PATCH 32/42] Result of tsccr-helper -log-level=info gha update -latest .github/ (#1120) Co-authored-by: hashicorp-tsccr[bot] --- .github/workflows/ci-go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index 5aad2f144..c9e930a11 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -21,7 +21,7 @@ jobs: with: go-version-file: 'go.mod' - run: go mod download - - uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 # v6.5.0 + - uses: golangci/golangci-lint-action@4696ba8babb6127d732c3c6dde519db15edab9ea # v6.5.1 terraform-provider-corner-tfprotov5: defaults: run: From f4f3ad9b7c7c11729980fa658d77c3a69754ffe6 Mon Sep 17 00:00:00 2001 From: "hashicorp-tsccr[bot]" <129506189+hashicorp-tsccr[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 12:17:36 -0500 Subject: [PATCH 33/42] SEC-090: Automated trusted workflow pinning (2025-04-07) (#1127) * Result of tsccr-helper -log-level=info gha update -latest .github/ * golangci-lint migrate --------- Co-authored-by: hashicorp-tsccr[bot] Co-authored-by: Baraa Basata --- .github/workflows/ci-github-actions.yml | 2 +- .github/workflows/ci-go.yml | 12 +++---- .github/workflows/ci-goreleaser.yml | 4 +-- .github/workflows/release.yml | 4 +-- .golangci.yml | 44 ++++++++++++++++++------- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci-github-actions.yml b/.github/workflows/ci-github-actions.yml index 8451dde35..0773b8bc9 100644 --- a/.github/workflows/ci-github-actions.yml +++ b/.github/workflows/ci-github-actions.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - run: go install github.com/rhysd/actionlint/cmd/actionlint@latest diff --git a/.github/workflows/ci-go.yml b/.github/workflows/ci-go.yml index c9e930a11..85e8df51e 100644 --- a/.github/workflows/ci-go.yml +++ b/.github/workflows/ci-go.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - run: go mod download - - uses: golangci/golangci-lint-action@4696ba8babb6127d732c3c6dde519db15edab9ea # v6.5.1 + - uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0 terraform-provider-corner-tfprotov5: defaults: run: @@ -34,7 +34,7 @@ jobs: with: path: terraform-provider-corner repository: hashicorp/terraform-provider-corner - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 @@ -60,7 +60,7 @@ jobs: with: path: terraform-provider-corner repository: hashicorp/terraform-provider-corner - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 @@ -82,13 +82,13 @@ jobs: go-version: [ '1.24', '1.23' ] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version: ${{ matrix.go-version }} - run: go mod download - run: go test -coverprofile=coverage.out ./... - run: go tool cover -html=coverage.out -o coverage.html - - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: go-${{ matrix.go-version }}-coverage path: coverage.html diff --git a/.github/workflows/ci-goreleaser.yml b/.github/workflows/ci-goreleaser.yml index 76735ffb5..59a7b3d8f 100644 --- a/.github/workflows/ci-goreleaser.yml +++ b/.github/workflows/ci-goreleaser.yml @@ -15,9 +15,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' - - uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # v6.2.1 + - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 with: args: check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb7bc6abf..f976e4efe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: ref: ${{ inputs.versionNumber }} fetch-depth: 0 - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: 'go.mod' @@ -93,7 +93,7 @@ jobs: cd .changes sed -e "1{/# /d;}" -e "2{/^$/d;}" ${{ needs.changelog-version.outputs.version }}.md > /tmp/release-notes.txt - - uses: goreleaser/goreleaser-action@90a3faa9d0182683851fbfa97ca1a2cb983bfca3 # v6.2.1 + - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.golangci.yml b/.golangci.yml index 1e7c56bf6..1515f53ae 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,16 +1,11 @@ -issues: - max-issues-per-linter: 0 - max-same-issues: 0 - +version: "2" linters: - disable-all: true + default: none enable: - copyloopvar - durationcheck - errcheck - forcetypeassert - - gofmt - - gosimple - govet - ineffassign - makezero @@ -23,7 +18,34 @@ linters: - unparam - unused - usetesting - -run: - # Prevent false positive timeouts in CI - timeout: 5m + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ + settings: + staticcheck: + checks: + - all + - '-QF1008' # could remove embedded field from selector -- https://staticcheck.io/docs/checks#QF1008 + - '-ST1003' # struct field Id should be ID -- https://staticcheck.io/docs/checks#ST1003 + - '-ST1005' # error strings should not be capitalized -- https://staticcheck.io/docs/checks#ST1005 + - '-ST1016' # methods on the same type should have the same receiver name -- https://staticcheck.io/docs/checks#ST1016 +issues: + max-issues-per-linter: 0 + max-same-issues: 0 +formatters: + enable: + - gofmt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ From db08a72f0247c8f711f219acc153473884a50fab Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Mon, 14 Apr 2025 16:38:37 -0400 Subject: [PATCH 34/42] ResourceIdentity: Add support for import by identity and update pass-through implementations (#1126) * initial import implementation with some TODOs * add tests for fwserver * add changelog for beta --- .../unreleased/NOTES-20250403-121229.yaml | 8 + internal/fromproto5/importresourcestate.go | 9 +- .../fromproto5/importresourcestate_test.go | 64 ++++++- internal/fromproto6/importresourcestate.go | 9 +- .../fromproto6/importresourcestate_test.go | 64 ++++++- .../fwserver/server_importresourcestate.go | 56 +++++- .../server_importresourcestate_test.go | 172 ++++++++++++++++++ .../server_importresourcestate.go | 10 +- .../server_importresourcestate_test.go | 142 +++++++++++++++ .../server_importresourcestate.go | 10 +- .../server_importresourcestate_test.go | 142 +++++++++++++++ .../resourcewithidentityandimportstate.go | 43 +++++ internal/toproto5/importedresource.go | 5 + internal/toproto5/importedresource_test.go | 95 ++++++++++ internal/toproto6/importedresource.go | 5 + internal/toproto6/importedresource_test.go | 95 ++++++++++ resource/import_state.go | 30 ++- 17 files changed, 950 insertions(+), 9 deletions(-) create mode 100644 .changes/unreleased/NOTES-20250403-121229.yaml create mode 100644 internal/testing/testprovider/resourcewithidentityandimportstate.go diff --git a/.changes/unreleased/NOTES-20250403-121229.yaml b/.changes/unreleased/NOTES-20250403-121229.yaml new file mode 100644 index 000000000..e383dcddc --- /dev/null +++ b/.changes/unreleased/NOTES-20250403-121229.yaml @@ -0,0 +1,8 @@ +kind: NOTES +body: This beta pre-release continues the implementation of managed resource identity, which should now be used with Terraform v1.12.0-beta1. + Managed resources now can support import by identity during plan and apply workflows. Managed resources that already support import via the + `resource.ResourceWithImportState` interface will automatically pass-through identity data to the `Read` method. The `RequiredForImport` and + `OptionalForImport` fields on the identity schema can be used to control the validation that Terraform core will apply to the import config block. +time: 2025-04-03T12:12:29.323193-04:00 +custom: + Issue: "1126" diff --git a/internal/fromproto5/importresourcestate.go b/internal/fromproto5/importresourcestate.go index ec40c2119..04034b584 100644 --- a/internal/fromproto5/importresourcestate.go +++ b/internal/fromproto5/importresourcestate.go @@ -18,7 +18,7 @@ import ( // ImportResourceStateRequest returns the *fwserver.ImportResourceStateRequest // equivalent of a *tfprotov5.ImportResourceStateRequest. -func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { +func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -45,10 +45,17 @@ func ImportResourceStateRequest(ctx context.Context, proto5 *tfprotov5.ImportRes Schema: resourceSchema, }, ID: proto5.ID, + IdentitySchema: identitySchema, Resource: reqResource, TypeName: proto5.TypeName, ClientCapabilities: ImportStateClientCapabilities(proto5.ClientCapabilities), } + identity, identityDiags := ResourceIdentity(ctx, proto5.Identity, identitySchema) + + diags.Append(identityDiags...) + + fw.Identity = identity + return fw, diags } diff --git a/internal/fromproto5/importresourcestate_test.go b/internal/fromproto5/importresourcestate_test.go index 39b5fdfa4..737d9a80d 100644 --- a/internal/fromproto5/importresourcestate_test.go +++ b/internal/fromproto5/importresourcestate_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -31,6 +32,30 @@ func TestImportResourceStateRequest(t *testing.T) { }, } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testFwEmptyState := tfsdk.State{ Raw: tftypes.NewValue(testFwSchema.Type().TerraformType(context.Background()), nil), Schema: testFwSchema, @@ -39,6 +64,7 @@ func TestImportResourceStateRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov5.ImportResourceStateRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource expected *fwserver.ImportResourceStateRequest expectedDiagnostics diag.Diagnostics @@ -67,6 +93,42 @@ func TestImportResourceStateRequest(t *testing.T) { ), }, }, + "identity-missing-schema": { + input: &tfprotov5.ImportResourceStateRequest{ + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "identity": { + input: &tfprotov5.ImportResourceStateRequest{ + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + resourceSchema: testFwSchema, + identitySchema: testIdentitySchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + IdentitySchema: testIdentitySchema, + Identity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: testIdentitySchema, + }, + }, + }, "id": { input: &tfprotov5.ImportResourceStateRequest{ ID: "test-id", @@ -122,7 +184,7 @@ func TestImportResourceStateRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto5.ImportResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema) + got, diags := fromproto5.ImportResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto6/importresourcestate.go b/internal/fromproto6/importresourcestate.go index 7070901cd..5e62da5c7 100644 --- a/internal/fromproto6/importresourcestate.go +++ b/internal/fromproto6/importresourcestate.go @@ -18,7 +18,7 @@ import ( // ImportResourceStateRequest returns the *fwserver.ImportResourceStateRequest // equivalent of a *tfprotov6.ImportResourceStateRequest. -func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { +func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportResourceStateRequest, reqResource resource.Resource, resourceSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.ImportResourceStateRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -45,10 +45,17 @@ func ImportResourceStateRequest(ctx context.Context, proto6 *tfprotov6.ImportRes Schema: resourceSchema, }, ID: proto6.ID, + IdentitySchema: identitySchema, Resource: reqResource, TypeName: proto6.TypeName, ClientCapabilities: ImportStateClientCapabilities(proto6.ClientCapabilities), } + identity, identityDiags := ResourceIdentity(ctx, proto6.Identity, identitySchema) + + diags.Append(identityDiags...) + + fw.Identity = identity + return fw, diags } diff --git a/internal/fromproto6/importresourcestate_test.go b/internal/fromproto6/importresourcestate_test.go index 6b385bb1a..c022b3258 100644 --- a/internal/fromproto6/importresourcestate_test.go +++ b/internal/fromproto6/importresourcestate_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -31,6 +32,30 @@ func TestImportResourceStateRequest(t *testing.T) { }, } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_identity_attribute": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_identity_attribute": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_identity_attribute": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + testFwEmptyState := tfsdk.State{ Raw: tftypes.NewValue(testFwSchema.Type().TerraformType(context.Background()), nil), Schema: testFwSchema, @@ -39,6 +64,7 @@ func TestImportResourceStateRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov6.ImportResourceStateRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource expected *fwserver.ImportResourceStateRequest expectedDiagnostics diag.Diagnostics @@ -67,6 +93,42 @@ func TestImportResourceStateRequest(t *testing.T) { ), }, }, + "identity-missing-schema": { + input: &tfprotov6.ImportResourceStateRequest{ + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + resourceSchema: testFwSchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + }, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unable to Convert Resource Identity", + "An unexpected error was encountered when converting the resource identity from the protocol type. "+ + "Identity data was sent in the protocol to a resource that doesn't support identity.\n\n"+ + "This is always a problem with Terraform or terraform-plugin-framework. Please report this to the provider developer.", + ), + }, + }, + "identity": { + input: &tfprotov6.ImportResourceStateRequest{ + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + resourceSchema: testFwSchema, + identitySchema: testIdentitySchema, + expected: &fwserver.ImportResourceStateRequest{ + EmptyState: testFwEmptyState, + IdentitySchema: testIdentitySchema, + Identity: &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: testIdentitySchema, + }, + }, + }, "id": { input: &tfprotov6.ImportResourceStateRequest{ ID: "test-id", @@ -122,7 +184,7 @@ func TestImportResourceStateRequest(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto6.ImportResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema) + got, diags := fromproto6.ImportResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 4abe204cd..23961aacf 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/logging" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -18,14 +19,29 @@ import ( // ImportedResource represents a resource that was imported. type ImportedResource struct { Private *privatestate.Data + Identity *tfsdk.ResourceIdentity State tfsdk.State TypeName string } // ImportResourceStateRequest is the framework server request for the // ImportResourceState RPC. +// +// Either ID or Identity will be supplied depending on how the resource is being imported. type ImportResourceStateRequest struct { - ID string + // ID will come from the import CLI command or an import config block with the "id" attribute assigned. + // + // This ID field is a special string identifier that can be parsed however the provider deems fit. + ID string + + // Identity will come from an import config block with the "identity" attribute assigned and will conform + // to the identity schema defined by the resource. (Terraform v1.12+) + // + // All attributes marked as RequiredForImport will be populated (enforced by Terraform core) and OptionalForImport + // attributes may be null, but could have a config value. + Identity *tfsdk.ResourceIdentity + IdentitySchema fwschema.Schema + Resource resource.Resource // EmptyState is an empty State for the resource schema. This is used to @@ -132,6 +148,29 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta Private: privateProviderData, } + // If the resource supports identity and we are not importing by identity, pre-populate with a null value. + // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? + if req.Identity == nil && req.IdentitySchema != nil { + nullTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) + + req.Identity = &tfsdk.ResourceIdentity{ + Schema: req.IdentitySchema, + Raw: nullTfValue.Copy(), + } + } + + if req.Identity != nil { + importReq.Identity = &tfsdk.ResourceIdentity{ + Schema: req.Identity.Schema, + Raw: req.Identity.Raw.Copy(), + } + + importResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.Identity.Schema, + Raw: req.Identity.Raw.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource ImportState") resourceWithImportState.ImportState(ctx, importReq, &importResp) logging.FrameworkTrace(ctx, "Called provider defined Resource ImportState") @@ -154,7 +193,9 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta importResp.State.Raw = modifiedState - if importResp.State.Raw.Equal(req.EmptyState.Raw) { + // If we are importing by ID, we should ensure that something in the import stub state has been populated, + // otherwise the resource doesn't actually support import, which is a provider issue. + if req.ID != "" && importResp.State.Raw.Equal(req.EmptyState.Raw) { resp.Diagnostics.AddError( "Missing Resource Import State", "An unexpected error was encountered when importing the resource. This is always a problem with the provider. Please give the following information to the provider developer:\n\n"+ @@ -169,10 +210,21 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta private.Provider = importResp.Private } + if importResp.Identity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected ImportState Response", + "An unexpected error was encountered when creating the import response. New identity data was returned by the provider import operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } + resp.Deferred = importResp.Deferred resp.ImportedResources = []ImportedResource{ { State: importResp.State, + Identity: importResp.Identity, TypeName: req.TypeName, Private: private, }, diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index 0f8481eea..ef9a7ac36 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -18,8 +18,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" ) func TestServerImportResourceState(t *testing.T) { @@ -33,6 +35,13 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + "other_test_id": tftypes.String, + }, + } + testTypeWriteOnly := tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "id": tftypes.String, @@ -61,6 +70,16 @@ func TestServerImportResourceState(t *testing.T) { "required": tftypes.NewValue(tftypes.String, nil), }) + testRequestIdentityValue := tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "other_test_id": tftypes.NewValue(tftypes.String, nil), + }) + + testImportedResourceIdentityValue := tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "other_test_id": tftypes.NewValue(tftypes.String, "new-value-123"), + }) + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -75,6 +94,17 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + "other_test_id": identityschema.StringAttribute{ + OptionalForImport: true, + }, + }, + } + testSchemaWriteOnly := schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -105,11 +135,21 @@ func TestServerImportResourceState(t *testing.T) { Schema: testSchema, } + testRequestIdentity := &tfsdk.ResourceIdentity{ + Raw: testRequestIdentityValue, + Schema: testIdentitySchema, + } + testState := &tfsdk.State{ Raw: testStateValue, Schema: testSchema, } + testImportedResourceIdentity := &tfsdk.ResourceIdentity{ + Raw: testImportedResourceIdentityValue, + Schema: testIdentitySchema, + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -202,6 +242,47 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "request-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + Identity: testRequestIdentity, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + OtherTestID types.String `tfsdk:"other_test_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("unexpected req.Identity value: %s", identityData.TestID.ValueString()) + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testEmptyState, + Identity: testRequestIdentity, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, "request-resourcetype-importstate-not-implemented": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -323,6 +404,97 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "response-importedresources-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + Identity: testRequestIdentity, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("other_test_id"), types.StringValue("new-value-123"))...) + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testEmptyState, + Identity: testImportedResourceIdentity, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, + "response-importedresources-identity-supported": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "test-id", + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("test_id"), types.StringValue("id-123"))...) + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("other_test_id"), types.StringValue("new-value-123"))...) + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testState, + Identity: testImportedResourceIdentity, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, + "response-importedresources-invalid-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "test-id", + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.Identity = &tfsdk.ResourceIdentity{ + Raw: testImportedResourceIdentityValue, + Schema: testIdentitySchema, + } + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected ImportState Response", + "An unexpected error was encountered when creating the import response. New identity data was returned by the provider import operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + }, + }, "response-importedresources-deferral-automatic": { server: &fwserver.Server{ Provider: &testprovider.Provider{ diff --git a/internal/proto5server/server_importresourcestate.go b/internal/proto5server/server_importresourcestate.go index 5d89dc908..9baaa8056 100644 --- a/internal/proto5server/server_importresourcestate.go +++ b/internal/proto5server/server_importresourcestate.go @@ -36,7 +36,15 @@ func (s *Server) ImportResourceState(ctx context.Context, proto5Req *tfprotov5.I return toproto5.ImportResourceStateResponse(ctx, fwResp), nil } - fwReq, diags := fromproto5.ImportResourceStateRequest(ctx, proto5Req, resource, resourceSchema) + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.ImportResourceStateResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto5.ImportResourceStateRequest(ctx, proto5Req, resource, resourceSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto5server/server_importresourcestate_test.go b/internal/proto5server/server_importresourcestate_test.go index 268853c7d..e2b539199 100644 --- a/internal/proto5server/server_importresourcestate_test.go +++ b/internal/proto5server/server_importresourcestate_test.go @@ -16,7 +16,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" ) func TestServerImportResourceState(t *testing.T) { @@ -30,12 +32,34 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + "test_other_id": tftypes.String, + }, + } + + testRequestIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "test_other_id": tftypes.NewValue(tftypes.String, nil), + }) + + testImportedResourceIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "test_other_id": tftypes.NewValue(tftypes.String, "new-value-123"), + }) + testStateDynamicValue := testNewDynamicValue(t, testType, map[string]tftypes.Value{ "id": tftypes.NewValue(tftypes.String, "test-id"), "optional": tftypes.NewValue(tftypes.String, nil), "required": tftypes.NewValue(tftypes.String, nil), }) + testEmptyStateDynamicValue, err := tfprotov5.NewDynamicValue(testType, tftypes.NewValue(testType, nil)) + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -50,6 +74,17 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + "test_other_id": identityschema.StringAttribute{ + OptionalForImport: true, + }, + }, + } + testCases := map[string]struct { server *Server request *tfprotov5.ImportResourceStateRequest @@ -99,6 +134,64 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "request-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + TestOtherID types.String `tfsdk:"test_other_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.ImportResourceStateRequest{ + TypeName: "test_resource", + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + }, + expectedResponse: &tfprotov5.ImportResourceStateResponse{ + ImportedResources: []*tfprotov5.ImportedResource{ + { + State: &testEmptyStateDynamicValue, + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + TypeName: "test_resource", + }, + }, + }, + }, "response-diagnostics": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -184,6 +277,55 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "response-importedresources-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("test_other_id"), types.StringValue("new-value-123"))...) + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.ImportResourceStateRequest{ + TypeName: "test_resource", + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + }, + expectedResponse: &tfprotov5.ImportResourceStateResponse{ + ImportedResources: []*tfprotov5.ImportedResource{ + { + State: &testEmptyStateDynamicValue, + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: testImportedResourceIdentityDynamicValue, + }, + TypeName: "test_resource", + }, + }, + }, + }, "response-importedresources-private": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/proto6server/server_importresourcestate.go b/internal/proto6server/server_importresourcestate.go index 46a58c633..aaf7cf1b8 100644 --- a/internal/proto6server/server_importresourcestate.go +++ b/internal/proto6server/server_importresourcestate.go @@ -36,7 +36,15 @@ func (s *Server) ImportResourceState(ctx context.Context, proto6Req *tfprotov6.I return toproto6.ImportResourceStateResponse(ctx, fwResp), nil } - fwReq, diags := fromproto6.ImportResourceStateRequest(ctx, proto6Req, resource, resourceSchema) + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.ImportResourceStateResponse(ctx, fwResp), nil + } + + fwReq, diags := fromproto6.ImportResourceStateRequest(ctx, proto6Req, resource, resourceSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto6server/server_importresourcestate_test.go b/internal/proto6server/server_importresourcestate_test.go index 8c67646f0..f59f287b4 100644 --- a/internal/proto6server/server_importresourcestate_test.go +++ b/internal/proto6server/server_importresourcestate_test.go @@ -16,7 +16,9 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" ) func TestServerImportResourceState(t *testing.T) { @@ -30,12 +32,34 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + "test_other_id": tftypes.String, + }, + } + + testRequestIdentityValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "test_other_id": tftypes.NewValue(tftypes.String, nil), + }) + + testImportedResourceIdentityDynamicValue := testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + "test_other_id": tftypes.NewValue(tftypes.String, "new-value-123"), + }) + testStateDynamicValue := testNewDynamicValue(t, testType, map[string]tftypes.Value{ "id": tftypes.NewValue(tftypes.String, "test-id"), "optional": tftypes.NewValue(tftypes.String, nil), "required": tftypes.NewValue(tftypes.String, nil), }) + testEmptyStateDynamicValue, err := tfprotov6.NewDynamicValue(testType, tftypes.NewValue(testType, nil)) + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + testSchema := schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -50,6 +74,17 @@ func TestServerImportResourceState(t *testing.T) { }, } + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + "test_other_id": identityschema.StringAttribute{ + OptionalForImport: true, + }, + }, + } + testCases := map[string]struct { server *Server request *tfprotov6.ImportResourceStateRequest @@ -99,6 +134,64 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "request-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + var identityData struct { + TestID types.String `tfsdk:"test_id"` + TestOtherID types.String `tfsdk:"test_other_id"` + } + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity", identityData.TestID.ValueString()) + } + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.ImportResourceStateRequest{ + TypeName: "test_resource", + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + }, + expectedResponse: &tfprotov6.ImportResourceStateResponse{ + ImportedResources: []*tfprotov6.ImportedResource{ + { + State: &testEmptyStateDynamicValue, + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + TypeName: "test_resource", + }, + }, + }, + }, "response-diagnostics": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -184,6 +277,55 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "response-importedresources-identity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndImportState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("test_other_id"), types.StringValue("new-value-123"))...) + + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov6.ImportResourceStateRequest{ + TypeName: "test_resource", + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: testRequestIdentityValue, + }, + }, + expectedResponse: &tfprotov6.ImportResourceStateResponse{ + ImportedResources: []*tfprotov6.ImportedResource{ + { + State: &testEmptyStateDynamicValue, + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: testImportedResourceIdentityDynamicValue, + }, + TypeName: "test_resource", + }, + }, + }, + }, "response-importedresources-private": { server: &Server{ FrameworkServer: fwserver.Server{ diff --git a/internal/testing/testprovider/resourcewithidentityandimportstate.go b/internal/testing/testprovider/resourcewithidentityandimportstate.go new file mode 100644 index 000000000..9e26dbf7c --- /dev/null +++ b/internal/testing/testprovider/resourcewithidentityandimportstate.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testprovider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.Resource = &ResourceWithIdentityAndImportState{} +var _ resource.ResourceWithIdentity = &ResourceWithIdentityAndImportState{} +var _ resource.ResourceWithImportState = &ResourceWithIdentityAndImportState{} + +// Declarative resource.ResourceWithIdentityAndImportState for unit testing. +type ResourceWithIdentityAndImportState struct { + *Resource + + // ResourceWithIdentity interface methods + IdentitySchemaMethod func(context.Context, resource.IdentitySchemaRequest, *resource.IdentitySchemaResponse) + + // ResourceWithImportState interface methods + ImportStateMethod func(context.Context, resource.ImportStateRequest, *resource.ImportStateResponse) +} + +// IdentitySchema implements resource.ResourceWithIdentity. +func (p *ResourceWithIdentityAndImportState) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + if p.IdentitySchemaMethod == nil { + return + } + + p.IdentitySchemaMethod(ctx, req, resp) +} + +// ImportState satisfies the resource.ResourceWithImportState interface. +func (r *ResourceWithIdentityAndImportState) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + if r.ImportStateMethod == nil { + return + } + + r.ImportStateMethod(ctx, req, resp) +} diff --git a/internal/toproto5/importedresource.go b/internal/toproto5/importedresource.go index 1a208db64..0b515dbd7 100644 --- a/internal/toproto5/importedresource.go +++ b/internal/toproto5/importedresource.go @@ -27,6 +27,11 @@ func ImportedResource(ctx context.Context, fw *fwserver.ImportedResource) (*tfpr proto5.State = state + identity, identityDiags := ResourceIdentity(ctx, fw.Identity) + + diags = append(diags, identityDiags...) + proto5.Identity = identity + newPrivate, privateDiags := fw.Private.Bytes(ctx) diags = append(diags, privateDiags...) diff --git a/internal/toproto5/importedresource_test.go b/internal/toproto5/importedresource_test.go index 58a1e860b..4d2ef533b 100644 --- a/internal/toproto5/importedresource_test.go +++ b/internal/toproto5/importedresource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -50,6 +51,22 @@ func TestImportResourceStateResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) } + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + testState := tfsdk.State{ Raw: testProto5Value, Schema: schema.Schema{ @@ -85,6 +102,28 @@ func TestImportResourceStateResponse(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testCases := map[string]struct { input *fwserver.ImportResourceStateResponse expected *tfprotov5.ImportResourceStateResponse @@ -154,6 +193,42 @@ func TestImportResourceStateResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-identity": { + input: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + ImportedResources: []fwserver.ImportedResource{ + { + State: testState, + Identity: testIdentityInvalid, + }, + }, + }, + expected: &tfprotov5.ImportResourceStateResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ImportResourceStateResponse{ ImportedResources: []fwserver.ImportedResource{ @@ -170,6 +245,26 @@ func TestImportResourceStateResponse(t *testing.T) { }, }, }, + "identity": { + input: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: testState, + Identity: testIdentity, + }, + }, + }, + expected: &tfprotov5.ImportResourceStateResponse{ + ImportedResources: []*tfprotov5.ImportedResource{ + { + State: &testProto5DynamicValue, + Identity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + }, + }, + }, "private": { input: &fwserver.ImportResourceStateResponse{ ImportedResources: []fwserver.ImportedResource{ diff --git a/internal/toproto6/importedresource.go b/internal/toproto6/importedresource.go index 28fea4b58..a57ed6319 100644 --- a/internal/toproto6/importedresource.go +++ b/internal/toproto6/importedresource.go @@ -27,6 +27,11 @@ func ImportedResource(ctx context.Context, fw *fwserver.ImportedResource) (*tfpr proto6.State = state + identity, identityDiags := ResourceIdentity(ctx, fw.Identity) + + diags = append(diags, identityDiags...) + proto6.Identity = identity + newPrivate, privateDiags := fw.Private.Bytes(ctx) diags = append(diags, privateDiags...) diff --git a/internal/toproto6/importedresource_test.go b/internal/toproto6/importedresource_test.go index 248d0d437..53eb11690 100644 --- a/internal/toproto6/importedresource_test.go +++ b/internal/toproto6/importedresource_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/toproto6" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/tfsdk" ) @@ -44,6 +45,22 @@ func TestImportResourceStateResponse(t *testing.T) { t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) } + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + testEmptyProto6DynamicValue, err := tfprotov6.NewDynamicValue(testEmptyProto6Type, testEmptyProto6Value) if err != nil { @@ -85,6 +102,28 @@ func TestImportResourceStateResponse(t *testing.T) { testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testCases := map[string]struct { input *fwserver.ImportResourceStateResponse expected *tfprotov6.ImportResourceStateResponse @@ -154,6 +193,42 @@ func TestImportResourceStateResponse(t *testing.T) { }, }, }, + "diagnostics-invalid-identity": { + input: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + ImportedResources: []fwserver.ImportedResource{ + { + State: testState, + Identity: testIdentityInvalid, + }, + }, + }, + expected: &tfprotov6.ImportResourceStateResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, "newstate": { input: &fwserver.ImportResourceStateResponse{ ImportedResources: []fwserver.ImportedResource{ @@ -170,6 +245,26 @@ func TestImportResourceStateResponse(t *testing.T) { }, }, }, + "identity": { + input: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: testState, + Identity: testIdentity, + }, + }, + }, + expected: &tfprotov6.ImportResourceStateResponse{ + ImportedResources: []*tfprotov6.ImportedResource{ + { + State: &testProto6DynamicValue, + Identity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + }, + }, + }, "private": { input: &fwserver.ImportResourceStateResponse{ ImportedResources: []fwserver.ImportedResource{ diff --git a/resource/import_state.go b/resource/import_state.go index 63f1b9fc5..1ca670448 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -34,8 +34,19 @@ type ImportStateRequest struct { // as an Attribute. However, this identifier can also be treated as // its own type of value and parsed during import. This value // is not stored in the state unless the provider explicitly stores it. + // + // This ID field is supplied in the "terraform import" CLI command or in the import config block "id" attribute. + // Either ID or Identity must be supplied by the practitioner, depending on the method used to import the resource. ID string + // Identity is the configuration data provided by the practitioner in the import config block "identity" attribute. This + // configuration data will conform to the identity schema defined by the managed resource. If the resource does not support identity, + // this value will not be set. + // + // The "identity" attribute in the import block is only supported in Terraform 1.12 and later. + // Either ID or Identity must be supplied by the practitioner, depending on the method used to import the resource. + Identity *tfsdk.ResourceIdentity + // ClientCapabilities defines optionally supported protocol features for the // ImportResourceState RPC, such as forward-compatible Terraform behavior changes. ClientCapabilities ImportStateClientCapabilities @@ -56,6 +67,14 @@ type ImportStateResponse struct { // refresh the resource, e.g. call the Resource Read method. State tfsdk.State + // Identity is the identity of the resource following the Import operation. + // This field is pre-populated from ImportStateRequest.Identity and + // should be set during the resource's Import operation. + // + // If the resource does not support identity, this value will not be set and will + // raise a diagnostic if set by the resource's Import operation. + Identity *tfsdk.ResourceIdentity + // Private is the private state resource data following the Import operation. // This field is not pre-populated as there is no pre-existing private state // data during the resource's Import operation. @@ -75,6 +94,11 @@ type ImportStateResponse struct { // ImportStatePassthroughID is a helper function to set the import // identifier to a given state attribute path. The attribute must accept a // string value. +// +// This method will also automatically pass through the Identity field if imported by +// the identity attribute of a import config block (Terraform 1.12+ and later). In this +// scenario where identity is provided instead of the string ID, the state field defined +// at `attrPath` will be set to null. func ImportStatePassthroughID(ctx context.Context, attrPath path.Path, req ImportStateRequest, resp *ImportStateResponse) { if attrPath.Equal(path.Empty()) { resp.Diagnostics.AddError( @@ -84,5 +108,9 @@ func ImportStatePassthroughID(ctx context.Context, attrPath path.Path, req Impor ) } - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, attrPath, req.ID)...) + // If the import is using the ID string identifier, (either via the "terraform import" CLI command, or a config block with the "id" attribute set) + // pass through the ID to the designated state attribute. + if req.ID != "" { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, attrPath, req.ID)...) + } } From b25d4c2c6651dab7b3903669219ec84ad723e1b4 Mon Sep 17 00:00:00 2001 From: hc-github-team-tf-provider-devex Date: Tue, 15 Apr 2025 18:52:10 +0000 Subject: [PATCH 35/42] Update changelog --- .changes/1.15.0-beta.1.md | 6 ++++++ .changes/unreleased/NOTES-20250403-121229.yaml | 8 -------- CHANGELOG.md | 6 ++++++ 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 .changes/1.15.0-beta.1.md delete mode 100644 .changes/unreleased/NOTES-20250403-121229.yaml diff --git a/.changes/1.15.0-beta.1.md b/.changes/1.15.0-beta.1.md new file mode 100644 index 000000000..a4f06e93c --- /dev/null +++ b/.changes/1.15.0-beta.1.md @@ -0,0 +1,6 @@ +## 1.15.0-beta.1 (April 15, 2025) + +NOTES: + +* This beta pre-release continues the implementation of managed resource identity, which should now be used with Terraform v1.12.0-beta1. Managed resources now can support import by identity during plan and apply workflows. Managed resources that already support import via the `resource.ResourceWithImportState` interface will automatically pass-through identity data to the `Read` method. The `RequiredForImport` and `OptionalForImport` fields on the identity schema can be used to control the validation that Terraform core will apply to the import config block. ([#1126](https://github.com/hashicorp/terraform-plugin-framework/issues/1126)) + diff --git a/.changes/unreleased/NOTES-20250403-121229.yaml b/.changes/unreleased/NOTES-20250403-121229.yaml deleted file mode 100644 index e383dcddc..000000000 --- a/.changes/unreleased/NOTES-20250403-121229.yaml +++ /dev/null @@ -1,8 +0,0 @@ -kind: NOTES -body: This beta pre-release continues the implementation of managed resource identity, which should now be used with Terraform v1.12.0-beta1. - Managed resources now can support import by identity during plan and apply workflows. Managed resources that already support import via the - `resource.ResourceWithImportState` interface will automatically pass-through identity data to the `Read` method. The `RequiredForImport` and - `OptionalForImport` fields on the identity schema can be used to control the validation that Terraform core will apply to the import config block. -time: 2025-04-03T12:12:29.323193-04:00 -custom: - Issue: "1126" diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1a6381d..69ca27d59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.15.0-beta.1 (April 15, 2025) + +NOTES: + +* This beta pre-release continues the implementation of managed resource identity, which should now be used with Terraform v1.12.0-beta1. Managed resources now can support import by identity during plan and apply workflows. Managed resources that already support import via the `resource.ResourceWithImportState` interface will automatically pass-through identity data to the `Read` method. The `RequiredForImport` and `OptionalForImport` fields on the identity schema can be used to control the validation that Terraform core will apply to the import config block. ([#1126](https://github.com/hashicorp/terraform-plugin-framework/issues/1126)) + ## 1.15.0-alpha.1 (March 18, 2025) NOTES: From 83df4b605986ea4af7a9e6014a43b510246aa7c4 Mon Sep 17 00:00:00 2001 From: Rain Kwan <91649079+rainkwan@users.noreply.github.com> Date: Fri, 18 Apr 2025 16:07:16 -0400 Subject: [PATCH 36/42] Implement validation logic for "identityschema.ListAttribute" (#1125) * Checks the element type of the identityschema.ListAttribute to make sure it is an allowed primitive * Fixed linters --- internal/fwschema/diagnostics.go | 16 +++ .../fwtype/static_collection_validation.go | 17 ++- .../static_collection_validation_test.go | 79 +++++++++++ resource/identityschema/list_attribute.go | 8 +- .../identityschema/list_attribute_test.go | 131 +++++++++++++++++- 5 files changed, 247 insertions(+), 4 deletions(-) diff --git a/internal/fwschema/diagnostics.go b/internal/fwschema/diagnostics.go index c79531326..efb53c00f 100644 --- a/internal/fwschema/diagnostics.go +++ b/internal/fwschema/diagnostics.go @@ -64,3 +64,19 @@ func AttributeDefaultTypeMismatchDiag(attributePath path.Path, expectedType attr "The default value must match the type of the schema.", ) } + +// AttributeInvalidElementTypeDiag returns an error diagnostic to provider +// developers about using non-primitive types in their Attribute +// implementation. This is not allowed. +func AttributeInvalidElementTypeDiag(attributePath path.Path, actualType attr.Type) diag.Diagnostic { + // The diagnostic path is intentionally omitted as it is invalid in this + // context. Diagnostic paths are intended to be mapped to actual data, + // while this path information must be synthesized. + return diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("%q contains an element of type %q that is not allowed for Lists in Resource Identity. ", attributePath, actualType)+ + "Lists in Resource Identity may only have primitive element types such as Bool, Int, Float, Number and String.", + ) +} diff --git a/internal/fwtype/static_collection_validation.go b/internal/fwtype/static_collection_validation.go index 2a91e2fbb..50a4b9f2d 100644 --- a/internal/fwtype/static_collection_validation.go +++ b/internal/fwtype/static_collection_validation.go @@ -5,7 +5,6 @@ package fwtype import ( "fmt" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -140,3 +139,19 @@ func ReturnCollectionWithDynamicTypeDiag() diag.Diagnostic { "If underlying dynamic values are required, replace the return definition with DynamicReturn instead.", ) } + +// IsAllowedPrimitiveType checks if the given attr.Type is an allowed primitive type for resource identity. +func IsAllowedPrimitiveType(typ attr.Type) bool { + switch typ.(type) { + case basetypes.BoolTypable, + basetypes.Float64Typable, + basetypes.Float32Typable, + basetypes.Int64Typable, + basetypes.Int32Typable, + basetypes.NumberTypable, + basetypes.StringTypable: + return true + default: + return false + } +} diff --git a/internal/fwtype/static_collection_validation_test.go b/internal/fwtype/static_collection_validation_test.go index 8000bc91a..058ee3aa9 100644 --- a/internal/fwtype/static_collection_validation_test.go +++ b/internal/fwtype/static_collection_validation_test.go @@ -944,3 +944,82 @@ func TestTypeContainsCollectionWithDynamic(t *testing.T) { }) } } + +func TestIsAllowedPrimitiveType(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attrTyp attr.Type + expected bool + }{ + "nil": { + attrTyp: nil, + expected: false, + }, + "dynamic": { + attrTyp: types.DynamicType, + expected: false, + }, + "bool": { + attrTyp: types.BoolType, + expected: true, + }, + "int64": { + attrTyp: types.Int64Type, + expected: true, + }, + "int32": { + attrTyp: types.Int32Type, + expected: true, + }, + "float64": { + attrTyp: types.Float64Type, + expected: true, + }, + "float32": { + attrTyp: types.Float32Type, + expected: true, + }, + "number": { + attrTyp: types.NumberType, + expected: true, + }, + "string": { + attrTyp: types.StringType, + expected: true, + }, + "list-missing": { + attrTyp: types.ListType{}, + expected: false, + }, + "list-static": { + attrTyp: types.ListType{ + ElemType: types.StringType, + }, + expected: false, + }, + "map": { + attrTyp: types.MapType{}, + expected: false, + }, + "object": { + attrTyp: types.ObjectType{}, + expected: false, + }, + "tuple": { + attrTyp: types.TupleType{}, + expected: false, + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := fwtype.IsAllowedPrimitiveType(testCase.attrTyp) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/resource/identityschema/list_attribute.go b/resource/identityschema/list_attribute.go index ef396c248..185a3bba8 100644 --- a/resource/identityschema/list_attribute.go +++ b/resource/identityschema/list_attribute.go @@ -5,6 +5,7 @@ package identityschema import ( "context" + "github.com/hashicorp/terraform-plugin-framework/internal/fwtype" "github.com/hashicorp/terraform-plugin-go/tftypes" @@ -160,10 +161,13 @@ func (a ListAttribute) IsOptionalForImport() bool { // provider-defined implementation of the attribute to prevent unexpected // errors or panics. This logic runs during the GetResourceIdentitySchemas RPC and // should never include false positives. -func (a ListAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { +func (a ListAttribute) ValidateImplementation(_ context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) { if a.CustomType == nil && a.ElementType == nil { resp.Diagnostics.Append(fwschema.AttributeMissingElementTypeDiag(req.Path)) + return } - // TODO:ResourceIdentity: Write validation + tests that ensure the element type only contains primitive elements (bool, string, number) + if a.CustomType == nil && !fwtype.IsAllowedPrimitiveType(a.ElementType) { + resp.Diagnostics.Append(fwschema.AttributeInvalidElementTypeDiag(req.Path, a.ElementType)) + } } diff --git a/resource/identityschema/list_attribute_test.go b/resource/identityschema/list_attribute_test.go index 45fc40c17..4affa8886 100644 --- a/resource/identityschema/list_attribute_test.go +++ b/resource/identityschema/list_attribute_test.go @@ -6,6 +6,7 @@ package identityschema_test import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/diag" "strings" "testing" @@ -13,7 +14,6 @@ import ( "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" "github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes" @@ -480,6 +480,135 @@ func TestListAttributeValidateImplementation(t *testing.T) { }, }, }, + "elementtype-bool": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.BoolType, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-int64": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.Int64Type, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-int32": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.Int32Type, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-float64": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.Float64Type, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-float32": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.Float32Type, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-number": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.NumberType, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{}, + }, + "elementtype-notprimitive-dynamic": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.DynamicType, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" contains an element of type \"basetypes.DynamicType\" that is not allowed for Lists in Resource Identity. "+ + "Lists in Resource Identity may only have primitive element types such as Bool, Int, Float, Number and String.", + ), + }, + }, + }, + "elementtype-notprimitive-object": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.ObjectType{}, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" contains an element of type \"types.ObjectType[]\" that is not allowed for Lists in Resource Identity. "+ + "Lists in Resource Identity may only have primitive element types such as Bool, Int, Float, Number and String.", + ), + }, + }, + }, + "elementtype-notprimitive-map": { + attribute: identityschema.ListAttribute{ + RequiredForImport: true, + ElementType: types.MapType{}, + }, + request: fwschema.ValidateImplementationRequest{ + Name: "test", + Path: path.Root("test"), + }, + expected: &fwschema.ValidateImplementationResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Invalid Attribute Implementation", + "When validating the schema, an implementation issue was found. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + "\"test\" contains an element of type \"types.MapType[!!! MISSING TYPE !!!]\" that is not allowed for Lists in Resource Identity. "+ + "Lists in Resource Identity may only have primitive element types such as Bool, Int, Float, Number and String.", + ), + }, + }, + }, } for name, testCase := range testCases { From b8e8582a9df0d3d18d3be7904623482eb3a64a4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 08:14:02 -0400 Subject: [PATCH 37/42] build(deps): Bump golang.org/x/net from 0.37.0 to 0.38.0 (#1130) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0. - [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.38.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 68acdcea9..68402e15c 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ 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.37.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect diff --git a/go.sum b/go.sum index cf2a09e6c..cfd3d364a 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,8 @@ go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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= From 0b93e31954f0dbe2a7a81bbaad1e3724d444b61a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 08:20:31 -0400 Subject: [PATCH 38/42] build(deps): Bump golang.org/x/net from 0.37.0 to 0.38.0 in /tools (#1129) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.37.0 to 0.38.0. - [Commits](https://github.com/golang/net/compare/v0.37.0...v0.38.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.38.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/go.mod | 2 +- tools/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/go.mod b/tools/go.mod index 6d6e8a5da..66628b325 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -50,7 +50,7 @@ require ( go.mongodb.org/mongo-driver v1.10.0 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect - golang.org/x/net v0.37.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect diff --git a/tools/go.sum b/tools/go.sum index 78e515527..1b0fbabba 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -413,8 +413,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From 3497335431ff33dad7b7a9631fbf2b54a68825bf Mon Sep 17 00:00:00 2001 From: Rain Kwan <91649079+rainkwan@users.noreply.github.com> Date: Mon, 21 Apr 2025 09:52:18 -0400 Subject: [PATCH 39/42] Add resource identity to Move RPC (#1123) * Add initial updates for identity to MoveResourceState RPC * Add toproto updates for identity to MoveResourceState RPC * Added more tests for identity to move rpc and added identitySchema to MoveState Request * Trying out a solution for proto5server/server_moveresourcestate_test, still failing tests * Fixed failing tests * Update resource/move_state.go * Added a response-invalid-identity test to test invalid identity responses * Updating the response-invalid-identity test output --- internal/fromproto5/moveresourcestate.go | 20 +- internal/fromproto5/moveresourcestate_test.go | 27 ++- internal/fromproto6/moveresourcestate.go | 20 +- internal/fromproto6/moveresourcestate_test.go | 27 ++- internal/fwserver/server_moveresourcestate.go | 50 ++++- .../fwserver/server_moveresourcestate_test.go | 173 +++++++++++++++ .../proto5server/server_moveresourcestate.go | 10 +- .../server_moveresourcestate_test.go | 207 ++++++++++++++++++ .../proto6server/server_moveresourcestate.go | 11 +- .../resourcewithidentityandmovestate.go | 43 ++++ internal/toproto5/moveresourcestate.go | 5 + internal/toproto5/moveresourcestate_test.go | 80 +++++++ internal/toproto6/moveresourcestate.go | 5 + internal/toproto6/moveresourcestate_test.go | 82 ++++++- resource/move_state.go | 11 + tfsdk/resource_identity.go | 11 +- 16 files changed, 749 insertions(+), 33 deletions(-) create mode 100644 internal/testing/testprovider/resourcewithidentityandmovestate.go diff --git a/internal/fromproto5/moveresourcestate.go b/internal/fromproto5/moveresourcestate.go index f5e836b7e..988bb0ab1 100644 --- a/internal/fromproto5/moveresourcestate.go +++ b/internal/fromproto5/moveresourcestate.go @@ -5,7 +5,6 @@ package fromproto5 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" @@ -17,7 +16,7 @@ import ( // MoveResourceStateRequest returns the *fwserver.MoveResourceStateRequest // equivalent of a *tfprotov5.MoveResourceStateRequest. -func MoveResourceStateRequest(ctx context.Context, proto5 *tfprotov5.MoveResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.MoveResourceStateRequest, diag.Diagnostics) { +func MoveResourceStateRequest(ctx context.Context, proto5 *tfprotov5.MoveResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.MoveResourceStateRequest, diag.Diagnostics) { if proto5 == nil { return nil, nil } @@ -38,13 +37,16 @@ func MoveResourceStateRequest(ctx context.Context, proto5 *tfprotov5.MoveResourc } fw := &fwserver.MoveResourceStateRequest{ - SourceProviderAddress: proto5.SourceProviderAddress, - SourceRawState: (*tfprotov6.RawState)(proto5.SourceState), - SourceSchemaVersion: proto5.SourceSchemaVersion, - SourceTypeName: proto5.SourceTypeName, - TargetResource: resource, - TargetResourceSchema: resourceSchema, - TargetTypeName: proto5.TargetTypeName, + SourceProviderAddress: proto5.SourceProviderAddress, + SourceRawState: (*tfprotov6.RawState)(proto5.SourceState), + SourceSchemaVersion: proto5.SourceSchemaVersion, + SourceTypeName: proto5.SourceTypeName, + TargetResource: resource, + TargetResourceSchema: resourceSchema, + TargetTypeName: proto5.TargetTypeName, + SourceIdentity: (*tfprotov6.RawState)(proto5.SourceIdentity), + SourceIdentitySchemaVersion: proto5.SourceIdentitySchemaVersion, + IdentitySchema: identitySchema, } sourcePrivate, sourcePrivateDiags := privatestate.NewData(ctx, proto5.SourcePrivate) diff --git a/internal/fromproto5/moveresourcestate_test.go b/internal/fromproto5/moveresourcestate_test.go index 2122edb75..78182cd0e 100644 --- a/internal/fromproto5/moveresourcestate_test.go +++ b/internal/fromproto5/moveresourcestate_test.go @@ -32,6 +32,7 @@ func TestMoveResourceStateRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov5.MoveResourceStateRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource expected *fwserver.MoveResourceStateRequest expectedDiagnostics diag.Diagnostics @@ -162,13 +163,37 @@ func TestMoveResourceStateRequest(t *testing.T) { TargetTypeName: "examplecloud_thing", }, }, + "SourceIdentity": { + input: &tfprotov5.MoveResourceStateRequest{ + SourceIdentity: testNewTfprotov5RawState(t, map[string]interface{}{ + "test_identity_attribute": "test-value", + }), + }, + resourceSchema: testFwSchema, + expected: &fwserver.MoveResourceStateRequest{ + SourceIdentity: testNewTfprotov6RawState(t, map[string]interface{}{ + "test_identity_attribute": "test-value", + }), + TargetResourceSchema: testFwSchema, + }, + }, + "SourceIdentitySchemaVersion": { + input: &tfprotov5.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + }, + resourceSchema: testFwSchema, + expected: &fwserver.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + TargetResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto5.MoveResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema) + got, diags := fromproto5.MoveResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fromproto6/moveresourcestate.go b/internal/fromproto6/moveresourcestate.go index 6b31a2cc0..7e85ce9f6 100644 --- a/internal/fromproto6/moveresourcestate.go +++ b/internal/fromproto6/moveresourcestate.go @@ -5,7 +5,6 @@ package fromproto6 import ( "context" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" @@ -16,7 +15,7 @@ import ( // MoveResourceStateRequest returns the *fwserver.MoveResourceStateRequest // equivalent of a *tfprotov6.MoveResourceStateRequest. -func MoveResourceStateRequest(ctx context.Context, proto6 *tfprotov6.MoveResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema) (*fwserver.MoveResourceStateRequest, diag.Diagnostics) { +func MoveResourceStateRequest(ctx context.Context, proto6 *tfprotov6.MoveResourceStateRequest, resource resource.Resource, resourceSchema fwschema.Schema, identitySchema fwschema.Schema) (*fwserver.MoveResourceStateRequest, diag.Diagnostics) { if proto6 == nil { return nil, nil } @@ -37,13 +36,16 @@ func MoveResourceStateRequest(ctx context.Context, proto6 *tfprotov6.MoveResourc } fw := &fwserver.MoveResourceStateRequest{ - SourceProviderAddress: proto6.SourceProviderAddress, - SourceRawState: proto6.SourceState, - SourceSchemaVersion: proto6.SourceSchemaVersion, - SourceTypeName: proto6.SourceTypeName, - TargetResource: resource, - TargetResourceSchema: resourceSchema, - TargetTypeName: proto6.TargetTypeName, + SourceProviderAddress: proto6.SourceProviderAddress, + SourceRawState: proto6.SourceState, + SourceSchemaVersion: proto6.SourceSchemaVersion, + SourceTypeName: proto6.SourceTypeName, + TargetResource: resource, + TargetResourceSchema: resourceSchema, + TargetTypeName: proto6.TargetTypeName, + SourceIdentity: proto6.SourceIdentity, + SourceIdentitySchemaVersion: proto6.SourceIdentitySchemaVersion, + IdentitySchema: identitySchema, } sourcePrivate, sourcePrivateDiags := privatestate.NewData(ctx, proto6.SourcePrivate) diff --git a/internal/fromproto6/moveresourcestate_test.go b/internal/fromproto6/moveresourcestate_test.go index 881ab3a49..d53449a72 100644 --- a/internal/fromproto6/moveresourcestate_test.go +++ b/internal/fromproto6/moveresourcestate_test.go @@ -32,6 +32,7 @@ func TestMoveResourceStateRequest(t *testing.T) { testCases := map[string]struct { input *tfprotov6.MoveResourceStateRequest resourceSchema fwschema.Schema + identitySchema fwschema.Schema resource resource.Resource expected *fwserver.MoveResourceStateRequest expectedDiagnostics diag.Diagnostics @@ -162,13 +163,37 @@ func TestMoveResourceStateRequest(t *testing.T) { TargetTypeName: "examplecloud_thing", }, }, + "SourceIdentity": { + input: &tfprotov6.MoveResourceStateRequest{ + SourceIdentity: testNewRawState(t, map[string]interface{}{ + "test_identity_attribute": "test-value", + }), + }, + resourceSchema: testFwSchema, + expected: &fwserver.MoveResourceStateRequest{ + SourceIdentity: testNewRawState(t, map[string]interface{}{ + "test_identity_attribute": "test-value", + }), + TargetResourceSchema: testFwSchema, + }, + }, + "SourceIdentitySchemaVersion": { + input: &tfprotov6.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + }, + resourceSchema: testFwSchema, + expected: &fwserver.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + TargetResourceSchema: testFwSchema, + }, + }, } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { t.Parallel() - got, diags := fromproto6.MoveResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema) + got, diags := fromproto6.MoveResourceStateRequest(context.Background(), testCase.input, testCase.resource, testCase.resourceSchema, testCase.identitySchema) if diff := cmp.Diff(got, testCase.expected); diff != "" { t.Errorf("unexpected difference: %s", diff) diff --git a/internal/fwserver/server_moveresourcestate.go b/internal/fwserver/server_moveresourcestate.go index 1a832f3fe..b838687d3 100644 --- a/internal/fwserver/server_moveresourcestate.go +++ b/internal/fwserver/server_moveresourcestate.go @@ -65,14 +65,25 @@ type MoveResourceStateRequest struct { // TargetTypeName is the type name of the target resource as given by // Terraform across the protocol. TargetTypeName string + + // SourceIdentity is the identity of the source resource. + // + // Only the underlying JSON field is populated. + SourceIdentity *tfprotov6.RawState + + // SourceIdentitySchemaVersion is the version of the source resource state. + SourceIdentitySchemaVersion int64 + + IdentitySchema fwschema.Schema } // MoveResourceStateResponse is the framework server response for the // MoveResourceState RPC. type MoveResourceStateResponse struct { - Diagnostics diag.Diagnostics - TargetPrivate *privatestate.Data - TargetState *tfsdk.State + Diagnostics diag.Diagnostics + TargetPrivate *privatestate.Data + TargetState *tfsdk.State + TargetIdentity *tfsdk.ResourceIdentity } // MoveResourceState implements the framework server MoveResourceState RPC. @@ -125,12 +136,15 @@ func (s *Server) MoveResourceState(ctx context.Context, req *MoveResourceStateRe for _, resourceStateMover := range resourceStateMovers { moveStateReq := resource.MoveStateRequest{ - SourcePrivate: sourcePrivate, - SourceProviderAddress: req.SourceProviderAddress, - SourceRawState: req.SourceRawState, - SourceSchemaVersion: req.SourceSchemaVersion, - SourceTypeName: req.SourceTypeName, + SourcePrivate: sourcePrivate, + SourceProviderAddress: req.SourceProviderAddress, + SourceRawState: req.SourceRawState, + SourceSchemaVersion: req.SourceSchemaVersion, + SourceTypeName: req.SourceTypeName, + SourceIdentity: req.SourceIdentity, + SourceIdentitySchemaVersion: req.SourceIdentitySchemaVersion, } + moveStateResp := resource.MoveStateResponse{ TargetPrivate: privatestate.EmptyProviderData(ctx), TargetState: tfsdk.State{ @@ -139,6 +153,13 @@ func (s *Server) MoveResourceState(ctx context.Context, req *MoveResourceStateRe }, } + if req.IdentitySchema != nil { + moveStateResp.TargetIdentity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil), + Schema: req.IdentitySchema, + } + } + if resourceStateMover.SourceSchema != nil { logging.FrameworkTrace(ctx, "Attempting to populate MoveResourceStateRequest SourceState from provider defined SourceSchema") @@ -221,6 +242,19 @@ func (s *Server) MoveResourceState(ctx context.Context, req *MoveResourceStateRe if !moveStateResp.TargetState.Raw.Equal(tftypes.NewValue(req.TargetResourceSchema.Type().TerraformType(ctx), nil)) { resp.Diagnostics = moveStateResp.Diagnostics resp.TargetState = &moveStateResp.TargetState + if moveStateResp.TargetIdentity != nil { + resp.TargetIdentity = moveStateResp.TargetIdentity + } + + if resp.TargetIdentity != nil && req.IdentitySchema == nil { + resp.Diagnostics.AddError( + "Unexpected Move State Response", + "An unexpected error was encountered when creating the move state response. New identity data was returned by the provider move state operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ) + + return + } if moveStateResp.TargetPrivate != nil { resp.TargetPrivate.Provider = moveStateResp.TargetPrivate diff --git a/internal/fwserver/server_moveresourcestate_test.go b/internal/fwserver/server_moveresourcestate_test.go index 8e2445ba9..90e32aeb4 100644 --- a/internal/fwserver/server_moveresourcestate_test.go +++ b/internal/fwserver/server_moveresourcestate_test.go @@ -6,6 +6,7 @@ package fwserver_test import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "testing" "github.com/google/go-cmp/cmp" @@ -39,8 +40,19 @@ func TestServerMoveResourceState(t *testing.T) { }, }, } + + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + schemaType := testSchema.Type().TerraformType(ctx) + schemaIdentityType := testIdentitySchema.Type().TerraformType(ctx) + testSchemaWriteOnly := schema.Schema{ Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ @@ -812,6 +824,167 @@ func TestServerMoveResourceState(t *testing.T) { }, }, }, + "request-SourceIdentitySchemaVersion": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + // SourceRawState required to prevent error + SourceRawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + TargetResource: &testprovider.ResourceWithMoveState{ + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + expected := int64(123) + + if diff := cmp.Diff(req.SourceIdentitySchemaVersion, expected); diff != "" { + resp.Diagnostics.AddError("Unexpected req.SourceIdentitySchemaVersion difference", diff) + } + + // Prevent missing implementation error, the values do not matter except for response assertion + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + }, + TargetResourceSchema: testSchema, + TargetTypeName: "test_resource", + }, + expectedResponse: &fwserver.MoveResourceStateResponse{ + TargetPrivate: privatestate.EmptyData(ctx), + TargetState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchema, + }, + }, + }, + "request-SourceIdentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.MoveResourceStateRequest{ + // SourceRawState required to prevent error + SourceIdentity: testNewRawState(t, map[string]interface{}{ + "test_id": "test_id_value", + }), + SourceRawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + TargetResource: &testprovider.ResourceWithMoveState{ + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + expectedSourceIdentity := testNewRawState(t, map[string]interface{}{ + "test_id": "test_id_value", + }) + + if diff := cmp.Diff(req.SourceIdentity, expectedSourceIdentity); diff != "" { + resp.Diagnostics.AddError("Unexpected req.SourceIdentity difference", diff) + } + + resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("test_id"), "test_id_value")...) + + // Prevent missing implementation error, the values do not matter except for response assertion + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + }, + TargetResourceSchema: testSchema, + TargetTypeName: "test_resource", + IdentitySchema: testIdentitySchema, + }, + expectedResponse: &fwserver.MoveResourceStateResponse{ + TargetPrivate: privatestate.EmptyData(ctx), + TargetState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchema, + }, + TargetIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(schemaIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "test_id_value"), + }), + Schema: testIdentitySchema, + }, + }, + }, + "response-invalid-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.MoveResourceStateRequest{ + SourceRawState: testNewRawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + TargetResource: &testprovider.ResourceWithMoveState{ + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + // This resource doesn't indicate identity support (via a schema), so this should raise a diagnostic. + resp.TargetIdentity = &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentitySchema.Type().TerraformType(ctx), map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "should-not-be-set"), + }), + Schema: testIdentitySchema, + } + + // Prevent missing implementation error, the values do not matter except for response assertion + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + }, + TargetResourceSchema: testSchema, + TargetTypeName: "test_resource", + }, + expectedResponse: &fwserver.MoveResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Unexpected Move State Response", + "An unexpected error was encountered when creating the move state response. New identity data was returned by the provider move state operation, but the resource does not indicate identity support.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer.", + ), + }, + TargetState: &tfsdk.State{ + Raw: tftypes.NewValue(schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + Schema: testSchema, + }, + TargetIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(schemaIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "should-not-be-set"), + }), + Schema: testIdentitySchema, + }, + TargetPrivate: privatestate.EmptyData(ctx), + }, + }, } for name, testCase := range testCases { diff --git a/internal/proto5server/server_moveresourcestate.go b/internal/proto5server/server_moveresourcestate.go index efa1b118c..23b570ed9 100644 --- a/internal/proto5server/server_moveresourcestate.go +++ b/internal/proto5server/server_moveresourcestate.go @@ -36,11 +36,19 @@ func (s *Server) MoveResourceState(ctx context.Context, proto5Req *tfprotov5.Mov fwResp.Diagnostics.Append(diags...) + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto5Req.TargetTypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto5.MoveResourceStateResponse(ctx, fwResp), nil + } + if fwResp.Diagnostics.HasError() { return toproto5.MoveResourceStateResponse(ctx, fwResp), nil } - fwReq, diags := fromproto5.MoveResourceStateRequest(ctx, proto5Req, resource, resourceSchema) + fwReq, diags := fromproto5.MoveResourceStateRequest(ctx, proto5Req, resource, resourceSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/proto5server/server_moveresourcestate_test.go b/internal/proto5server/server_moveresourcestate_test.go index 5b5e53c97..17b11fc47 100644 --- a/internal/proto5server/server_moveresourcestate_test.go +++ b/internal/proto5server/server_moveresourcestate_test.go @@ -6,6 +6,7 @@ package proto5server import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "testing" "github.com/google/go-cmp/cmp" @@ -39,6 +40,20 @@ func TestServerMoveResourceState(t *testing.T) { } schemaType := testSchema.Type().TerraformType(ctx) + testIdentitySchema := identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + } + + testIdentityType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + testCases := map[string]struct { server *Server request *tfprotov5.MoveResourceStateRequest @@ -413,6 +428,140 @@ func TestServerMoveResourceState(t *testing.T) { }), }, }, + "request-SourceIdentitySchemaVersion": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithMoveState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + expected := int64(123) + + if diff := cmp.Diff(req.SourceIdentitySchemaVersion, expected); diff != "" { + resp.Diagnostics.AddError("Unexpected req.SourceIdentitySchemaVersion difference", diff) + } + + // Prevent missing implementation error, the values do not matter except for response assertion + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.MoveResourceStateRequest{ + SourceIdentitySchemaVersion: 123, + // SourceState required to prevent error + SourceState: testNewTfprotov5RawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + TargetTypeName: "test_resource", + }, + expectedResponse: &tfprotov5.MoveResourceStateResponse{ + TargetState: testNewDynamicValue(t, schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + }, + }, + "request-SourceIdentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndMoveState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + expectedSourceRawState := testNewTfprotov6RawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }) + + if diff := cmp.Diff(req.SourceRawState, expectedSourceRawState); diff != "" { + resp.Diagnostics.AddError("Unexpected req.SourceRawState difference", diff) + } + + expectedSourceIdentity := testNewTfprotov6RawState(t, map[string]interface{}{ + "test_id": "test-id-value", + }) + + if diff := cmp.Diff(req.SourceIdentity, expectedSourceIdentity); diff != "" { + resp.Diagnostics.AddError("Unexpected req.SourceIdentity difference", diff) + } + + resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("test_id"), "test-id-value")...) + + // Prevent missing implementation error, the values do not matter except for response assertion + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.MoveResourceStateRequest{ + SourceState: testNewTfprotov5RawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + SourceIdentity: testNewTfprotov5RawState(t, map[string]interface{}{ + "test_id": "test-id-value", + }), + TargetTypeName: "test_resource", + }, + expectedResponse: &tfprotov5.MoveResourceStateResponse{ + TargetIdentity: &tfprotov5.ResourceIdentityData{IdentityData: testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "test-id-value"), + })}, + TargetState: testNewDynamicValue(t, schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + }, + }, "request-TargetTypeName-missing": { server: &Server{ FrameworkServer: fwserver.Server{ @@ -705,6 +854,64 @@ func TestServerMoveResourceState(t *testing.T) { }), }, }, + "response-TargetIdentity": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ResourcesMethod: func(_ context.Context) []func() resource.Resource { + return []func() resource.Resource{ + func() resource.Resource { + return &testprovider.ResourceWithIdentityAndMoveState{ + Resource: &testprovider.Resource{ + SchemaMethod: func(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = testSchema + }, + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_resource" + }, + }, + MoveStateMethod: func(ctx context.Context) []resource.StateMover { + return []resource.StateMover{ + { + StateMover: func(_ context.Context, req resource.MoveStateRequest, resp *resource.MoveStateResponse) { + resp.Diagnostics.Append(resp.TargetIdentity.SetAttribute(ctx, path.Root("test_id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("id"), "test-id-value")...) + resp.Diagnostics.Append(resp.TargetState.SetAttribute(ctx, path.Root("required_attribute"), "true")...) + }, + }, + } + }, + IdentitySchemaMethod: func(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + resp.IdentitySchema = testIdentitySchema + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.MoveResourceStateRequest{ + SourceState: testNewTfprotov5RawState(t, map[string]interface{}{ + "id": "test-id-value", + "required_attribute": true, + }), + SourceIdentity: testNewTfprotov5RawState(t, map[string]interface{}{ + "test_id": "test-id-value", + }), + TargetTypeName: "test_resource", + }, + expectedResponse: &tfprotov5.MoveResourceStateResponse{ + TargetState: testNewDynamicValue(t, schemaType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "test-id-value"), + "optional_attribute": tftypes.NewValue(tftypes.String, nil), + "required_attribute": tftypes.NewValue(tftypes.String, "true"), + }), + TargetIdentity: &tfprotov5.ResourceIdentityData{IdentityData: testNewDynamicValue(t, testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "test-id-value"), + })}, + }, + }, } for name, testCase := range testCases { diff --git a/internal/proto6server/server_moveresourcestate.go b/internal/proto6server/server_moveresourcestate.go index 1010b5d76..96e3a96cb 100644 --- a/internal/proto6server/server_moveresourcestate.go +++ b/internal/proto6server/server_moveresourcestate.go @@ -5,7 +5,6 @@ package proto6server import ( "context" - "github.com/hashicorp/terraform-plugin-framework/internal/fromproto6" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" "github.com/hashicorp/terraform-plugin-framework/internal/logging" @@ -36,11 +35,19 @@ func (s *Server) MoveResourceState(ctx context.Context, proto6Req *tfprotov6.Mov fwResp.Diagnostics.Append(diags...) + identitySchema, diags := s.FrameworkServer.ResourceIdentitySchema(ctx, proto6Req.TargetTypeName) + + fwResp.Diagnostics.Append(diags...) + + if fwResp.Diagnostics.HasError() { + return toproto6.MoveResourceStateResponse(ctx, fwResp), nil + } + if fwResp.Diagnostics.HasError() { return toproto6.MoveResourceStateResponse(ctx, fwResp), nil } - fwReq, diags := fromproto6.MoveResourceStateRequest(ctx, proto6Req, resource, resourceSchema) + fwReq, diags := fromproto6.MoveResourceStateRequest(ctx, proto6Req, resource, resourceSchema, identitySchema) fwResp.Diagnostics.Append(diags...) diff --git a/internal/testing/testprovider/resourcewithidentityandmovestate.go b/internal/testing/testprovider/resourcewithidentityandmovestate.go new file mode 100644 index 000000000..60f4d0676 --- /dev/null +++ b/internal/testing/testprovider/resourcewithidentityandmovestate.go @@ -0,0 +1,43 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testprovider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.Resource = &ResourceWithIdentityAndMoveState{} +var _ resource.ResourceWithIdentity = &ResourceWithIdentityAndMoveState{} +var _ resource.ResourceWithMoveState = &ResourceWithIdentityAndMoveState{} + +// Declarative resource.ResourceWithIdentityAndMoveState for unit testing. +type ResourceWithIdentityAndMoveState struct { + *Resource + + // ResourceWithIdentity interface methods + IdentitySchemaMethod func(context.Context, resource.IdentitySchemaRequest, *resource.IdentitySchemaResponse) + + // ResourceWithMoveState interface methods + MoveStateMethod func(context.Context) []resource.StateMover +} + +// IdentitySchema implements resource.ResourceWithIdentity. +func (p *ResourceWithIdentityAndMoveState) IdentitySchema(ctx context.Context, req resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) { + if p.IdentitySchemaMethod == nil { + return + } + + p.IdentitySchemaMethod(ctx, req, resp) +} + +// MoveState satisfies the resource.ResourceWithMoveState interface. +func (r *ResourceWithIdentityAndMoveState) MoveState(ctx context.Context) []resource.StateMover { + if r.MoveStateMethod == nil { + return nil + } + + return r.MoveStateMethod(ctx) +} diff --git a/internal/toproto5/moveresourcestate.go b/internal/toproto5/moveresourcestate.go index bfffadf1b..2c598c46c 100644 --- a/internal/toproto5/moveresourcestate.go +++ b/internal/toproto5/moveresourcestate.go @@ -27,6 +27,11 @@ func MoveResourceStateResponse(ctx context.Context, fw *fwserver.MoveResourceSta proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) proto5.TargetPrivate = targetPrivate + targetIdentity, diags := ResourceIdentity(ctx, fw.TargetIdentity) + + proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) + proto5.TargetIdentity = targetIdentity + targetState, diags := State(ctx, fw.TargetState) proto5.Diagnostics = append(proto5.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto5/moveresourcestate_test.go b/internal/toproto5/moveresourcestate_test.go index 36075c0bf..948ec6150 100644 --- a/internal/toproto5/moveresourcestate_test.go +++ b/internal/toproto5/moveresourcestate_test.go @@ -5,6 +5,7 @@ package toproto5_test import ( "context" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" "testing" "github.com/google/go-cmp/cmp" @@ -33,6 +34,44 @@ func TestMoveResourceStateResponse(t *testing.T) { }, ) + testIdentityProto5Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto5Value := tftypes.NewValue(testIdentityProto5Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto5DynamicValue, err := tfprotov5.NewDynamicValue(testIdentityProto5Type, testIdentityProto5Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov5.NewDynamicValue(): %s", err) + } + + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto5Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testCases := map[string]struct { input *fwserver.MoveResourceStateResponse expected *tfprotov5.MoveResourceStateResponse @@ -152,6 +191,47 @@ func TestMoveResourceStateResponse(t *testing.T) { }, }, }, + "TargetIdentity": { + input: &fwserver.MoveResourceStateResponse{ + TargetIdentity: testIdentity, + }, + expected: &tfprotov5.MoveResourceStateResponse{ + TargetIdentity: &tfprotov5.ResourceIdentityData{ + IdentityData: &testIdentityProto5DynamicValue, + }, + }, + }, + "TargetIdentity-invalid": { + input: &fwserver.MoveResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + TargetIdentity: testIdentityInvalid, + }, + expected: &tfprotov5.MoveResourceStateResponse{ + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, } for name, testCase := range testCases { diff --git a/internal/toproto6/moveresourcestate.go b/internal/toproto6/moveresourcestate.go index 86c2a55a8..83a81d446 100644 --- a/internal/toproto6/moveresourcestate.go +++ b/internal/toproto6/moveresourcestate.go @@ -27,6 +27,11 @@ func MoveResourceStateResponse(ctx context.Context, fw *fwserver.MoveResourceSta proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) proto6.TargetPrivate = targetPrivate + targetIdentity, diags := ResourceIdentity(ctx, fw.TargetIdentity) + + proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) + proto6.TargetIdentity = targetIdentity + targetState, diags := State(ctx, fw.TargetState) proto6.Diagnostics = append(proto6.Diagnostics, Diagnostics(ctx, diags)...) diff --git a/internal/toproto6/moveresourcestate_test.go b/internal/toproto6/moveresourcestate_test.go index d5281c970..dfed88c69 100644 --- a/internal/toproto6/moveresourcestate_test.go +++ b/internal/toproto6/moveresourcestate_test.go @@ -5,10 +5,11 @@ package toproto6_test import ( "context" + "github.com/hashicorp/terraform-plugin-framework/resource/identityschema" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" "testing" "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" "github.com/hashicorp/terraform-plugin-framework/diag" @@ -33,6 +34,44 @@ func TestMoveResourceStateResponse(t *testing.T) { }, ) + testIdentityProto6Type := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_id": tftypes.String, + }, + } + + testIdentityProto6Value := tftypes.NewValue(testIdentityProto6Type, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }) + + testIdentityProto6DynamicValue, err := tfprotov6.NewDynamicValue(testIdentityProto6Type, testIdentityProto6Value) + + if err != nil { + t.Fatalf("unexpected error calling tfprotov6.NewDynamicValue(): %s", err) + } + + testIdentity := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.StringAttribute{ + RequiredForImport: true, + }, + }, + }, + } + + testIdentityInvalid := &tfsdk.ResourceIdentity{ + Raw: testIdentityProto6Value, + Schema: identityschema.Schema{ + Attributes: map[string]identityschema.Attribute{ + "test_id": identityschema.BoolAttribute{ + RequiredForImport: true, + }, + }, + }, + } + testCases := map[string]struct { input *fwserver.MoveResourceStateResponse expected *tfprotov6.MoveResourceStateResponse @@ -152,6 +191,47 @@ func TestMoveResourceStateResponse(t *testing.T) { }, }, }, + "TargetIdentity": { + input: &fwserver.MoveResourceStateResponse{ + TargetIdentity: testIdentity, + }, + expected: &tfprotov6.MoveResourceStateResponse{ + TargetIdentity: &tfprotov6.ResourceIdentityData{ + IdentityData: &testIdentityProto6DynamicValue, + }, + }, + }, + "TargetIdentity-invalid": { + input: &fwserver.MoveResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic("test warning summary", "test warning details"), + diag.NewErrorDiagnostic("test error summary", "test error details"), + }, + TargetIdentity: testIdentityInvalid, + }, + expected: &tfprotov6.MoveResourceStateResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "test warning summary", + Detail: "test warning details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "test error summary", + Detail: "test error details", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "Unable to Convert Resource Identity", + Detail: "An unexpected error was encountered when converting the resource identity to the protocol type. " + + "This is always an issue in terraform-plugin-framework used to implement the provider and should be reported to the provider developers.\n\n" + + "Please report this to the provider developer:\n\n" + + "Unable to create DynamicValue: AttributeName(\"test_id\"): unexpected value type string, tftypes.Bool values must be of type bool", + }, + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/move_state.go b/resource/move_state.go index 6918f5b76..977f565d9 100644 --- a/resource/move_state.go +++ b/resource/move_state.go @@ -75,6 +75,14 @@ type MoveStateRequest struct { // other request fields, to determine whether the request should be handled // by this particular implementation. SourceTypeName string + + // SourceIdentity is the identity of the source resource. + // + // Only the underlying JSON field is populated. + SourceIdentity *tfprotov6.RawState + + // SourceIdentitySchemaVersion is the version of the source resource identity. + SourceIdentitySchemaVersion int64 } // MoveStateResponse represents a response to a MoveStateRequest. @@ -107,4 +115,7 @@ type MoveStateResponse struct { // operation. This field is not pre-populated as it is up to implementations // whether the source private data is relevant for the target resource. TargetPrivate *privatestate.ProviderData + + // TargetIdentity is the identity of the target resource. + TargetIdentity *tfsdk.ResourceIdentity } diff --git a/tfsdk/resource_identity.go b/tfsdk/resource_identity.go index d02f1e8e3..fd9f30579 100644 --- a/tfsdk/resource_identity.go +++ b/tfsdk/resource_identity.go @@ -5,7 +5,6 @@ package tfsdk import ( "context" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" @@ -70,6 +69,16 @@ func (s *ResourceIdentity) Set(ctx context.Context, val interface{}) diag.Diagno // // Lists can only have the next element added according to the current length. func (s *ResourceIdentity) SetAttribute(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics { + // If s is nil, then calling s.data triggers a nil pointer error so we return the error diag here + if s == nil { + return diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Missing Identity Definition", + "An unexpected error was encountered when attempting to set a resource identity attribute. The resource does not indicate support via a resource identity schema.\n\n"+ + "This is always a problem with the provider and should be reported to the provider developer."), + } + } + data := s.data() diags := data.SetAtPath(ctx, path, val) From 59870d9483e5085f45a82ba282a04377f5c08bf8 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Wed, 23 Apr 2025 08:18:41 -0400 Subject: [PATCH 40/42] chore: Fix identity TODOs and add documentation for interfaces (#1133) * chore: Fix identity TODOs and add documentation for interfaces * Update resource/resource.go --- internal/fromproto5/resource_identity.go | 4 +--- internal/fromproto6/resource_identity.go | 4 +--- internal/fwserver/server_createresource.go | 1 - internal/fwserver/server_deleteresource.go | 2 -- .../fwserver/server_importresourcestate.go | 1 - .../fwserver/server_planresourcechange.go | 2 -- internal/fwserver/server_readresource.go | 1 - internal/fwserver/server_updateresource.go | 1 - resource/resource.go | 19 ++++++++++++++++++- 9 files changed, 20 insertions(+), 15 deletions(-) diff --git a/internal/fromproto5/resource_identity.go b/internal/fromproto5/resource_identity.go index c6e1a8014..c78f77866 100644 --- a/internal/fromproto5/resource_identity.go +++ b/internal/fromproto5/resource_identity.go @@ -13,9 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov5" ) -// TODO:ResourceIdentity: Should we create a wrapping struct to contain the identity data? To match the protocol (in-case we want to introduce other identity things) -// - Need to think more on this (like what if we want to introduce display-only attributes) -// - If we introduce one, add a test as well. +// ResourceIdentity returns the *tfsdk.ResourceIdentity for a *tfprotov5.ResourceIdentityData and fwschema.Schema. func ResourceIdentity(ctx context.Context, in *tfprotov5.ResourceIdentityData, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { if in == nil { return nil, nil diff --git a/internal/fromproto6/resource_identity.go b/internal/fromproto6/resource_identity.go index d9fc1b4e5..5dc262332 100644 --- a/internal/fromproto6/resource_identity.go +++ b/internal/fromproto6/resource_identity.go @@ -13,9 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov6" ) -// TODO:ResourceIdentity: Should we create a wrapping struct to contain the identity data? To match the protocol (in-case we want to introduce other identity things) -// - Need to think more on this (like what if we want to introduce display-only attributes) -// - If we introduce one, add a test as well. +// ResourceIdentity returns the *tfsdk.ResourceIdentity for a *tfprotov6.ResourceIdentityData and fwschema.Schema. func ResourceIdentity(ctx context.Context, in *tfprotov6.ResourceIdentityData, schema fwschema.Schema) (*tfsdk.ResourceIdentity, diag.Diagnostics) { if in == nil { return nil, nil diff --git a/internal/fwserver/server_createresource.go b/internal/fwserver/server_createresource.go index 0e5b873ac..00a13d68e 100644 --- a/internal/fwserver/server_createresource.go +++ b/internal/fwserver/server_createresource.go @@ -101,7 +101,6 @@ func (s *Server) CreateResource(ctx context.Context, req *CreateResourceRequest, } // If the resource supports identity and there is no planned identity data, pre-populate with a null value. - // TODO:ResourceIdentity: This logic is likely useless since plan should already handle this, probably should remove. if req.PlannedIdentity == nil && req.IdentitySchema != nil { nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/internal/fwserver/server_deleteresource.go b/internal/fwserver/server_deleteresource.go index d556badfd..c7d04380b 100644 --- a/internal/fwserver/server_deleteresource.go +++ b/internal/fwserver/server_deleteresource.go @@ -98,8 +98,6 @@ func (s *Server) DeleteResource(ctx context.Context, req *DeleteResourceRequest, resp.Private = req.PlannedPrivate } - // If the resource supports identity pre-populate a null value. - // TODO:ResourceIdentity: This should probably be prior identity, but we don't currently have that in the protocol. if req.IdentitySchema != nil { nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/internal/fwserver/server_importresourcestate.go b/internal/fwserver/server_importresourcestate.go index 23961aacf..000a0fad6 100644 --- a/internal/fwserver/server_importresourcestate.go +++ b/internal/fwserver/server_importresourcestate.go @@ -149,7 +149,6 @@ func (s *Server) ImportResourceState(ctx context.Context, req *ImportResourceSta } // If the resource supports identity and we are not importing by identity, pre-populate with a null value. - // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? if req.Identity == nil && req.IdentitySchema != nil { nullTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/internal/fwserver/server_planresourcechange.go b/internal/fwserver/server_planresourcechange.go index 7910bac84..fb6c161d8 100644 --- a/internal/fwserver/server_planresourcechange.go +++ b/internal/fwserver/server_planresourcechange.go @@ -119,8 +119,6 @@ func (s *Server) PlanResourceChange(ctx context.Context, req *PlanResourceChange } // If the resource supports identity and there is no prior identity data, pre-populate with a null value. - // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? - // TODO:ResourceIdentity: Should this be set to all unknowns? if req.PriorIdentity == nil && req.IdentitySchema != nil { nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/internal/fwserver/server_readresource.go b/internal/fwserver/server_readresource.go index 1c56d44c5..557b1a714 100644 --- a/internal/fwserver/server_readresource.go +++ b/internal/fwserver/server_readresource.go @@ -120,7 +120,6 @@ func (s *Server) ReadResource(ctx context.Context, req *ReadResourceRequest, res } // If the resource supports identity and there is no current identity data, pre-populate with a null value. - // TODO:ResourceIdentity: Is there any reason a provider WOULD NOT want to populate an identity when it supports one? if req.CurrentIdentity == nil && req.IdentitySchema != nil { nullTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/internal/fwserver/server_updateresource.go b/internal/fwserver/server_updateresource.go index 19827b7e3..041dee187 100644 --- a/internal/fwserver/server_updateresource.go +++ b/internal/fwserver/server_updateresource.go @@ -122,7 +122,6 @@ func (s *Server) UpdateResource(ctx context.Context, req *UpdateResourceRequest, } // If the resource supports identity and there is no planned identity data, pre-populate with a null value. - // TODO:ResourceIdentity: This logic is likely useless since plan should already handle this, probably should remove. if req.PlannedIdentity == nil && req.IdentitySchema != nil { nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) diff --git a/resource/resource.go b/resource/resource.go index f99824023..2cf334a77 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -19,6 +19,9 @@ import ( // - Plan Modification: Schema-based or entire plan // via ResourceWithModifyPlan. // - State Upgrades: ResourceWithUpgradeState +// - Identity: Define an identity schema with ResourceWithIdentity to enable +// storing identity data in state during CRUD operations. Identity data is +// used by Terraform to uniquely identify a managed resource. // // Although not required, it is conventional for resources to implement the // ResourceWithImportState interface. @@ -199,7 +202,21 @@ type ResourceWithValidateConfig interface { // ResourceWithIdentity is an interface type that extends Resource to implement managed resource identity. // -// TODO:ResourceIdentity: Add more documentation here to describe what identity is used for. +// Managed resources can optionally define an identity schema, which represents a separate object stored in state +// alongside the resource instance data. This identity data is used by Terraform to uniquely identify +// managed resources and has additional restrictions that allow external programs to determine equality +// between two identities. +// +// Resource identity schemas can only contain primitive (bool, string, number) attributes and lists that +// contain primitive elements. Additionally, a resource identity should have the following properties: +// - The resource identity should correspond to at most one remote object per provider, across all +// instances of that provider. +// - Given a resource identity, the provider should be able to determine whether the corresponding remote +// object exists, and if so, return the resource state. Resources that support identity can be imported +// by the identity object via the ResourceWithImportState interface. +// - The resource identity should not change during the lifecycle of the remote object. That is, from the +// creation of the remote object in the remote system until its destruction. An exception to this rule +// is an upgrade of the identity data after a schema change, via the ResourceWithUpgradeIdentity interface. type ResourceWithIdentity interface { Resource From ca17ae9cf28cdbf10c8cf2cf442af909e0c3ca7f Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Fri, 2 May 2025 08:56:38 -0400 Subject: [PATCH 41/42] Resource Identity: Add a new import helper that can pass-through an identity attribute and an import ID. (#1134) * Resource Identity: Add a new import helper that can pass-through an identity attribute and an import ID. * Update with correct diag returns --- .../server_importresourcestate_test.go | 125 ++++++++++++++++++ resource/import_state.go | 55 +++++++- 2 files changed, 177 insertions(+), 3 deletions(-) diff --git a/internal/fwserver/server_importresourcestate_test.go b/internal/fwserver/server_importresourcestate_test.go index ef9a7ac36..ea9b1ac68 100644 --- a/internal/fwserver/server_importresourcestate_test.go +++ b/internal/fwserver/server_importresourcestate_test.go @@ -145,6 +145,15 @@ func TestServerImportResourceState(t *testing.T) { Schema: testSchema, } + testStatePassThroughIdentity := &tfsdk.State{ + Raw: tftypes.NewValue(testType, map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "id-123"), + "optional": tftypes.NewValue(tftypes.String, nil), + "required": tftypes.NewValue(tftypes.String, nil), + }), + Schema: testSchema, + } + testImportedResourceIdentity := &tfsdk.ResourceIdentity{ Raw: testImportedResourceIdentityValue, Schema: testIdentitySchema, @@ -655,6 +664,122 @@ func TestServerImportResourceState(t *testing.T) { }, }, }, + "response-importedresources-passthrough-identity-imported-by-id": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "id-123", + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughWithIdentity(ctx, path.Root("id"), path.Root("test_id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testStatePassThroughIdentity, + Identity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + }, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, + "response-importedresources-passthrough-identity-imported-by-identity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + Identity: testRequestIdentity, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resp.Diagnostics.Append(resp.Identity.SetAttribute(ctx, path.Root("other_test_id"), types.StringValue("new-value-123"))...) + resource.ImportStatePassthroughWithIdentity(ctx, path.Root("id"), path.Root("test_id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + ImportedResources: []fwserver.ImportedResource{ + { + State: *testStatePassThroughIdentity, + Identity: testImportedResourceIdentity, + TypeName: "test_resource", + Private: testEmptyPrivate, + }, + }, + }, + }, + "response-importedresources-passthrough-identity-invalid-state-path": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + ID: "id-123", + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughWithIdentity(ctx, path.Root("not-valid"), path.Root("test_id"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("not-valid"), + "State Write Error", + "An unexpected error was encountered trying to retrieve type information at a given path. "+ + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: AttributeName(\"not-valid\") still remains in the path: could not find attribute or block "+ + "\"not-valid\" in schema", + ), + }, + }, + }, + "response-importedresources-passthrough-identity-invalid-identity-path": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ImportResourceStateRequest{ + EmptyState: *testEmptyState, + Identity: testRequestIdentity, + IdentitySchema: testIdentitySchema, + Resource: &testprovider.ResourceWithImportState{ + Resource: &testprovider.Resource{}, + ImportStateMethod: func(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughWithIdentity(ctx, path.Root("id"), path.Root("not-valid"), req, resp) + }, + }, + TypeName: "test_resource", + }, + expectedResponse: &fwserver.ImportResourceStateResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("not-valid"), + "Resource Identity Read Error", + "An unexpected error was encountered trying to retrieve type information at a given path. "+ + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: AttributeName(\"not-valid\") still remains in the path: could not find attribute or block "+ + "\"not-valid\" in schema", + ), + }, + }, + }, } for name, testCase := range testCases { diff --git a/resource/import_state.go b/resource/import_state.go index 1ca670448..2c8e0ea1a 100644 --- a/resource/import_state.go +++ b/resource/import_state.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" ) // ImportStateClientCapabilities allows Terraform to publish information @@ -95,9 +96,9 @@ type ImportStateResponse struct { // identifier to a given state attribute path. The attribute must accept a // string value. // -// This method will also automatically pass through the Identity field if imported by -// the identity attribute of a import config block (Terraform 1.12+ and later). In this -// scenario where identity is provided instead of the string ID, the state field defined +// For resources that support identity, this method will also automatically pass through the +// Identity field if imported by the identity attribute of a import config block (Terraform 1.12+ and later). +// In this scenario where identity is provided instead of the string ID, the state field defined // at `attrPath` will be set to null. func ImportStatePassthroughID(ctx context.Context, attrPath path.Path, req ImportStateRequest, resp *ImportStateResponse) { if attrPath.Equal(path.Empty()) { @@ -106,6 +107,7 @@ func ImportStatePassthroughID(ctx context.Context, attrPath path.Path, req Impor "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ "Resource ImportState method call to ImportStatePassthroughID path must be set to a valid attribute path that can accept a string value.", ) + return } // If the import is using the ID string identifier, (either via the "terraform import" CLI command, or a config block with the "id" attribute set) @@ -114,3 +116,50 @@ func ImportStatePassthroughID(ctx context.Context, attrPath path.Path, req Impor resp.Diagnostics.Append(resp.State.SetAttribute(ctx, attrPath, req.ID)...) } } + +// ImportStatePassthroughWithIdentity is a helper function to retrieve either the import identifier +// or a given identity attribute that is then used to set to given attribute path in state, based on the method used +// by the practitioner to import. The identity and state attributes provided must be of type string. +// +// The helper method should only be used on resources that support identity via the resource.ResourceWithIdentity interface. +// +// This method will also automatically pass through the Identity field if imported by +// the identity attribute of a import config block (Terraform 1.12+ and later). +func ImportStatePassthroughWithIdentity(ctx context.Context, stateAttrPath, identityAttrPath path.Path, req ImportStateRequest, resp *ImportStateResponse) { + if stateAttrPath.Equal(path.Empty()) { + resp.Diagnostics.AddError( + "Resource Import Passthrough Missing State Attribute Path", + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Resource ImportState method call to ImportStatePassthroughWithIdentity path must be set to a valid state attribute path that can accept a string value.", + ) + } + + if identityAttrPath.Equal(path.Empty()) { + resp.Diagnostics.AddError( + "Resource Import Passthrough Missing Identity Attribute Path", + "This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Resource ImportState method call to ImportStatePassthroughWithIdentity path must be set to a valid identity attribute path that is a string value.", + ) + } + + if resp.Diagnostics.HasError() { + return + } + + // If the import is using the import identifier, (either via the "terraform import" CLI command, or a config block with the "id" attribute set) + // pass through the ID to the designated state attribute. + if req.ID != "" { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, stateAttrPath, req.ID)...) + return + } + + // The import isn't using the import identifier, so it must be using identity. Grab the designated + // identity attribute string and set it to state. + var identityAttrVal types.String + resp.Diagnostics.Append(req.Identity.GetAttribute(ctx, identityAttrPath, &identityAttrVal)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, stateAttrPath, identityAttrVal)...) +} From 17f1faf8787c6987ade7b6d58e297f256473ab43 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Fri, 2 May 2025 09:04:54 -0400 Subject: [PATCH 42/42] resource: Add identity to DeleteRequest (#1132) --- .../fwserver/server_applyresourcechange.go | 4 + .../server_applyresourcechange_test.go | 69 +++++++++++++++-- internal/fwserver/server_deleteresource.go | 17 ++++- .../fwserver/server_deleteresource_test.go | 75 ++++++++++++++++--- resource/delete.go | 4 + 5 files changed, 148 insertions(+), 21 deletions(-) diff --git a/internal/fwserver/server_applyresourcechange.go b/internal/fwserver/server_applyresourcechange.go index 2167ab709..aabbeeba7 100644 --- a/internal/fwserver/server_applyresourcechange.go +++ b/internal/fwserver/server_applyresourcechange.go @@ -76,6 +76,10 @@ func (s *Server) ApplyResourceChange(ctx context.Context, req *ApplyResourceChan deleteReq := &DeleteResourceRequest{ PlannedPrivate: req.PlannedPrivate, PriorState: req.PriorState, + // MAINTAINER NOTE: There isn't a separate data field for prior identity, like there is with prior_state and planned_state. + // Here the planned_identity field will contain what would be considered the prior identity (since the final identity value + // after deleting will be null). + PriorIdentity: req.PlannedIdentity, ProviderMeta: req.ProviderMeta, ResourceSchema: req.ResourceSchema, IdentitySchema: req.IdentitySchema, diff --git a/internal/fwserver/server_applyresourcechange_test.go b/internal/fwserver/server_applyresourcechange_test.go index a8565de7d..0fa2d9ab9 100644 --- a/internal/fwserver/server_applyresourcechange_test.go +++ b/internal/fwserver/server_applyresourcechange_test.go @@ -69,6 +69,11 @@ func TestServerApplyResourceChange(t *testing.T) { Schema: testSchema, } + testEmptyIdentity := &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -689,6 +694,52 @@ func TestServerApplyResourceChange(t *testing.T) { NewState: testEmptyState, }, }, + "delete-request-prioridentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ApplyResourceChangeRequest{ + PlannedState: testEmptyPlan, + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + CreateMethod: func(_ context.Context, _ resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Delete, Got: Create") + }, + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + }, + UpdateMethod: func(_ context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Delete, Got: Update") + }, + }, + }, + }, + expectedResponse: &fwserver.ApplyResourceChangeResponse{ + NewIdentity: testEmptyIdentity, + NewState: testEmptyState, + }, + }, "delete-request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -982,6 +1033,12 @@ func TestServerApplyResourceChange(t *testing.T) { }), Schema: testSchema, }, + PlannedIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, IdentitySchema: testIdentitySchema, ResourceSchema: testSchema, Resource: &testprovider.ResourceWithIdentity{ @@ -990,10 +1047,11 @@ func TestServerApplyResourceChange(t *testing.T) { resp.Diagnostics.AddError("Unexpected Method Call", "Expected: Delete, Got: Create") }, DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - if resp.Identity == nil || !resp.Identity.Raw.IsNull() { + // The identity is automatically set to null in the response after the Delete method is called + if resp.Identity == nil || resp.Identity.Raw.IsNull() { resp.Diagnostics.AddError( "Unexpected resp.Identity", - "expected resp.Identity to be a null object of the schema type.", + "expected resp.Identity to be a known non-null object of the schema type.", ) } }, @@ -1004,11 +1062,8 @@ func TestServerApplyResourceChange(t *testing.T) { }, }, expectedResponse: &fwserver.ApplyResourceChangeResponse{ - NewIdentity: &tfsdk.ResourceIdentity{ - Raw: tftypes.NewValue(testIdentityType, nil), - Schema: testIdentitySchema, - }, - NewState: testEmptyState, + NewIdentity: testEmptyIdentity, + NewState: testEmptyState, }, }, "update-request-config": { diff --git a/internal/fwserver/server_deleteresource.go b/internal/fwserver/server_deleteresource.go index c7d04380b..d58e758d0 100644 --- a/internal/fwserver/server_deleteresource.go +++ b/internal/fwserver/server_deleteresource.go @@ -21,6 +21,7 @@ import ( type DeleteResourceRequest struct { PlannedPrivate *privatestate.Data PriorState *tfsdk.State + PriorIdentity *tfsdk.ResourceIdentity ProviderMeta *tfsdk.Config ResourceSchema fwschema.Schema IdentitySchema fwschema.Schema @@ -98,15 +99,27 @@ func (s *Server) DeleteResource(ctx context.Context, req *DeleteResourceRequest, resp.Private = req.PlannedPrivate } - if req.IdentitySchema != nil { + if req.PriorIdentity == nil && req.IdentitySchema != nil { nullIdentityTfValue := tftypes.NewValue(req.IdentitySchema.Type().TerraformType(ctx), nil) - deleteResp.Identity = &tfsdk.ResourceIdentity{ + req.PriorIdentity = &tfsdk.ResourceIdentity{ Schema: req.IdentitySchema, Raw: nullIdentityTfValue.Copy(), } } + if req.PriorIdentity != nil { + deleteReq.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PriorIdentity.Schema, + Raw: req.PriorIdentity.Raw.Copy(), + } + + deleteResp.Identity = &tfsdk.ResourceIdentity{ + Schema: req.PriorIdentity.Schema, + Raw: req.PriorIdentity.Raw.Copy(), + } + } + logging.FrameworkTrace(ctx, "Calling provider defined Resource Delete") req.Resource.Delete(ctx, deleteReq, &deleteResp) logging.FrameworkTrace(ctx, "Called provider defined Resource Delete") diff --git a/internal/fwserver/server_deleteresource_test.go b/internal/fwserver/server_deleteresource_test.go index b47c16c0b..4770f45e9 100644 --- a/internal/fwserver/server_deleteresource_test.go +++ b/internal/fwserver/server_deleteresource_test.go @@ -64,6 +64,11 @@ func TestServerDeleteResource(t *testing.T) { Schema: testSchema, } + testEmptyIdentity := &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, nil), + Schema: testIdentitySchema, + } + type testSchemaData struct { TestComputed types.String `tfsdk:"test_computed"` TestRequired types.String `tfsdk:"test_required"` @@ -163,6 +168,45 @@ func TestServerDeleteResource(t *testing.T) { NewState: testEmptyState, }, }, + "request-prioridentity": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.DeleteResourceRequest{ + PriorState: &tfsdk.State{ + Raw: tftypes.NewValue(testSchemaType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-priorstate-value"), + }), + Schema: testSchema, + }, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, + IdentitySchema: testIdentitySchema, + ResourceSchema: testSchema, + Resource: &testprovider.ResourceWithIdentity{ + Resource: &testprovider.Resource{ + DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var identityData testIdentitySchemaData + + resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...) + + if identityData.TestID.ValueString() != "id-123" { + resp.Diagnostics.AddError("Unexpected req.Identity Value", "Got: "+identityData.TestID.ValueString()) + } + }, + }, + }, + }, + expectedResponse: &fwserver.DeleteResourceResponse{ + NewState: testEmptyState, + NewIdentity: testEmptyIdentity, + }, + }, "request-providermeta": { server: &fwserver.Server{ Provider: &testprovider.Provider{}, @@ -365,25 +409,29 @@ func TestServerDeleteResource(t *testing.T) { }), Schema: testSchema, }, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, IdentitySchema: testIdentitySchema, ResourceSchema: testSchema, Resource: &testprovider.Resource{ DeleteMethod: func(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - if resp.Identity == nil || !resp.Identity.Raw.IsNull() { + // The identity is automatically set to null in the response after the Delete method is called + if resp.Identity == nil || resp.Identity.Raw.IsNull() { resp.Diagnostics.AddError( "Unexpected resp.Identity", - "expected resp.Identity to be a null object of the schema type.", + "expected resp.Identity to be a known non-null object of the schema type.", ) } }, }, }, expectedResponse: &fwserver.DeleteResourceResponse{ - NewIdentity: &tfsdk.ResourceIdentity{ - Raw: tftypes.NewValue(testIdentityType, nil), - Schema: testIdentitySchema, - }, - NewState: testEmptyState, + NewIdentity: testEmptyIdentity, + NewState: testEmptyState, }, }, "response-newidentity-set-to-null": { @@ -398,6 +446,12 @@ func TestServerDeleteResource(t *testing.T) { }), Schema: testSchema, }, + PriorIdentity: &tfsdk.ResourceIdentity{ + Raw: tftypes.NewValue(testIdentityType, map[string]tftypes.Value{ + "test_id": tftypes.NewValue(tftypes.String, "id-123"), + }), + Schema: testIdentitySchema, + }, IdentitySchema: testIdentitySchema, ResourceSchema: testSchema, Resource: &testprovider.Resource{ @@ -410,11 +464,8 @@ func TestServerDeleteResource(t *testing.T) { }, }, expectedResponse: &fwserver.DeleteResourceResponse{ - NewIdentity: &tfsdk.ResourceIdentity{ - Raw: tftypes.NewValue(testIdentityType, nil), - Schema: testIdentitySchema, - }, - NewState: testEmptyState, + NewIdentity: testEmptyIdentity, + NewState: testEmptyState, }, }, "response-invalid-newidentity": { diff --git a/resource/delete.go b/resource/delete.go index 8281dffa1..6644737d9 100644 --- a/resource/delete.go +++ b/resource/delete.go @@ -17,6 +17,10 @@ type DeleteRequest struct { // operation. State tfsdk.State + // Identity is the current identity of the resource prior to the Delete + // operation. If the resource does not support identity, this value will not be set. + Identity *tfsdk.ResourceIdentity + // ProviderMeta is metadata from the provider_meta block of the module. ProviderMeta tfsdk.Config