diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cba348..d3db94d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.4.0] - 2024-01-28 + +### Added + +- Added the resource `neon_project_permission` to manage the project's permissions. + +### Changed + +- Updated dependencies: + - Neon Go SDK [v0.4.3](https://github.com/kislerdm/neon-sdk-go/releases/tag/v0.4.3) + - Terraform docs [v0.18.0](https://github.com/hashicorp/terraform-plugin-docs/releases/tag/v0.18.0) + ## [v0.3.2] - 2024-01-11 ### Fixed diff --git a/docs/resources/branch.md b/docs/resources/branch.md index 9e7539c..81e94f7 100644 --- a/docs/resources/branch.md +++ b/docs/resources/branch.md @@ -43,5 +43,3 @@ See details: https://neon.tech/docs/reference/glossary/#lsn - `id` (String) Branch ID. - `logical_size` (Number) Branch logical size in MB. - - diff --git a/docs/resources/database.md b/docs/resources/database.md index 6d0318e..9d4a44c 100644 --- a/docs/resources/database.md +++ b/docs/resources/database.md @@ -49,5 +49,3 @@ resource "neon_database" "example" { ### Read-Only - `id` (String) The ID of this resource. - - diff --git a/docs/resources/endpoint.md b/docs/resources/endpoint.md index 1302950..7dea839 100644 --- a/docs/resources/endpoint.md +++ b/docs/resources/endpoint.md @@ -64,5 +64,3 @@ The maximum value is 604800 seconds (1 week) - `host` (String) Endpoint URI. - `id` (String) Endpoint ID. - `proxy_host` (String) - - diff --git a/docs/resources/project.md b/docs/resources/project.md index 9b2fabe..f7c38ca 100644 --- a/docs/resources/project.md +++ b/docs/resources/project.md @@ -138,5 +138,3 @@ Optional: - `data_transfer_bytes` (Number) Total amount of data transferred from all of a project's branches using the proxy. - `logical_size_bytes` (Number) Limit on the logical size of every project's branch. - `written_data_bytes` (Number) Total amount of data written to all of a project's branches. - - diff --git a/docs/resources/project_permission.md b/docs/resources/project_permission.md new file mode 100644 index 0000000..6e29bdb --- /dev/null +++ b/docs/resources/project_permission.md @@ -0,0 +1,37 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "neon_project_permission Resource - terraform-provider-neon" +subcategory: "" +description: |- + Project's access permission. +--- + +# neon_project_permission (Resource) + +Project's access permission. + +## Example Usage + +```terraform +resource "neon_project" "example" { + name = "foo" +} + +# grant project access to the user with the email foo@bar.qux +resource "neon_project_permission" "share" { + project_id = neon_project.example.id + grantee = "foo@bar.qux" +} +``` + + +## Schema + +### Required + +- `grantee` (String) Email of the user whom to grant project permission. +- `project_id` (String) Project ID. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/role.md b/docs/resources/role.md index dad4d05..3f49b5d 100644 --- a/docs/resources/role.md +++ b/docs/resources/role.md @@ -45,5 +45,3 @@ resource "neon_role" "example" { - `id` (String) The ID of this resource. - `password` (String, Sensitive) Database authentication password. - `protected` (Boolean) - - diff --git a/examples/resources/neon_project_permission/resource.tf b/examples/resources/neon_project_permission/resource.tf new file mode 100644 index 0000000..fb4a1b2 --- /dev/null +++ b/examples/resources/neon_project_permission/resource.tf @@ -0,0 +1,9 @@ +resource "neon_project" "example" { + name = "foo" +} + +# grant project access to the user with the email foo@bar.qux +resource "neon_project_permission" "share" { + project_id = neon_project.example.id + grantee = "foo@bar.qux" +} diff --git a/go.mod b/go.mod index 2262981..e543e7d 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,28 @@ module github.com/kislerdm/terraform-provider-neon go 1.21 require ( - github.com/hashicorp/terraform-plugin-docs v0.13.0 + github.com/google/uuid v1.3.1 + github.com/hashicorp/terraform-plugin-docs v0.18.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 - github.com/kislerdm/neon-sdk-go v0.4.2 + github.com/kislerdm/neon-sdk-go v0.4.3 ) require ( + github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.1.1 // indirect - github.com/Masterminds/sprig/v3 v3.2.2 // indirect + github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/cloudflare/circl v1.3.3 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/hashicorp/cli v1.1.6 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -35,17 +37,17 @@ require ( github.com/hashicorp/hc-install v0.6.2 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.19.0 // indirect - github.com/hashicorp/terraform-json v0.18.0 // indirect + github.com/hashicorp/terraform-exec v0.20.0 // indirect + github.com/hashicorp/terraform-json v0.21.0 // indirect github.com/hashicorp/terraform-plugin-go v0.20.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/huandu/xstrings v1.3.2 // indirect + github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.15 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mitchellh/cli v1.1.5 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect @@ -59,8 +61,11 @@ require ( github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/yuin/goldmark v1.6.0 // indirect + github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.14.1 // indirect - golang.org/x/crypto v0.16.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.18.0 // indirect golang.org/x/sys v0.15.0 // indirect @@ -69,4 +74,5 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/grpc v1.60.0 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect ) diff --git a/go.sum b/go.sum index 6057bb8..8caec38 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,13 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0= +github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= -github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= -github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= @@ -16,7 +17,6 @@ github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= @@ -24,8 +24,9 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -33,9 +34,9 @@ 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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -58,9 +59,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8= +github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -89,12 +91,12 @@ github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5R github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= -github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= -github.com/hashicorp/terraform-json v0.18.0 h1:pCjgJEqqDESv4y0Tzdqfxr/edOIGkjs8keY42xfNBwU= -github.com/hashicorp/terraform-json v0.18.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smPzZrt1Wlm9koLeKazY= -github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= +github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= +github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= +github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= +github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-plugin-docs v0.18.0 h1:2bINhzXc+yDeAcafurshCrIjtdu1XHn9zZ3ISuEhgpk= +github.com/hashicorp/terraform-plugin-docs v0.18.0/go.mod h1:iIUfaJpdUmpi+rI42Kgq+63jAjI8aZVTyxp3Bvk9Hg8= github.com/hashicorp/terraform-plugin-go v0.20.0 h1:oqvoUlL+2EUbKNsJbIt3zqqZ7wi6lzn4ufkn/UA51xQ= github.com/hashicorp/terraform-plugin-go v0.20.0/go.mod h1:Rr8LBdMlY53a3Z/HpP+ZU3/xCDqtKNCkeI9qOyT10QE= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= @@ -107,9 +109,8 @@ github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S 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= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= @@ -119,8 +120,8 @@ github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgf github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kislerdm/neon-sdk-go v0.4.2 h1:WtA0VssPYXY8fTPYQQttQiKRHScjjrenoPMEkxhKoig= -github.com/kislerdm/neon-sdk-go v0.4.2/go.mod h1:WSwEZ7oeR5KfQoCuDh/04LZxnSKDcvfsZyfG/QicDb8= +github.com/kislerdm/neon-sdk-go v0.4.3 h1:x29T1sW893r1Dku5T3SpUUtTmiOwpavh9D7WriHGFQU= +github.com/kislerdm/neon-sdk-go v0.4.3/go.mod h1:WSwEZ7oeR5KfQoCuDh/04LZxnSKDcvfsZyfG/QicDb8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -130,16 +131,17 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng= -github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -158,11 +160,10 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= @@ -179,9 +180,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -192,16 +193,21 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= +github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= +github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 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.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= @@ -220,7 +226,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -230,6 +235,7 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -276,7 +282,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/provider/acc_test.go b/internal/provider/acc_test.go index 0e58324..a43a9cc 100644 --- a/internal/provider/acc_test.go +++ b/internal/provider/acc_test.go @@ -168,7 +168,7 @@ resource "neon_database" "this" { ), resource.TestCheckResourceAttr( resourceNameProject, - "compute_provisioner", "k8s-pod", + "compute_provisioner", "k8s-neonvm", ), resource.TestCheckResourceAttr( resourceNameProject, @@ -232,26 +232,12 @@ resource "neon_database" "this" { // check the project and its settings func(state *terraform.State) error { - // WHEN - // list projects - resp, err := client.ListProjects(nil, nil) + ref, err := readProjectInfo(client, projectName) if err != nil { - return errors.New("listing error: " + err.Error()) - } - - // THEN - var ref neon.ProjectListItem - for _, project := range resp.ProjectsResponse.Projects { - if project.Name == projectName { - ref = project - } - break - } - - if ref.ID == "" { - return errors.New("project " + projectName + " shall be created") + return err } + // check autoscaling if float64(*ref.DefaultEndpointSettings.AutoscalingLimitMinCu) != mustParseFloat64(autoscalingCUMin) { return errors.New("AutoscalingLimitMinCu was not set") } @@ -272,22 +258,13 @@ resource "neon_database" "this" { projectID = ref.ID defaultUser = ref.OwnerID - return nil - }, - - // check data retention - func(state *terraform.State) error { - resp, err := client.GetProject(projectID) - if err != nil { - return err - } - - v, err := strconv.Atoi(historyRetentionSeconds) + // check data retention + v, err = strconv.Atoi(historyRetentionSeconds) if err != nil { t.Fatal(err) } - if resp.Project.HistoryRetentionSeconds != int64(v) { + if ref.HistoryRetentionSeconds != int64(v) { return errors.New("HistoryRetentionSeconds was not set") } @@ -296,14 +273,11 @@ resource "neon_database" "this" { // check the branches func(state *terraform.State) error { - // WHEN - // list projects resp, err := client.ListProjectBranches(projectID) if err != nil { return err } - // THEN if len(resp.Branches) != 2 { return errors.New("only two branches are expected") } @@ -337,14 +311,11 @@ resource "neon_database" "this" { // check the endpoints func(state *terraform.State) error { - // WHEN - // list projects resp, err := client.ListProjectEndpoints(projectID) if err != nil { return err } - // THEN endpoints := resp.Endpoints if len(endpoints) != 2 { return errors.New("only two endpoints are expected") @@ -365,14 +336,11 @@ resource "neon_database" "this" { // check the databases func(state *terraform.State) error { - // WHEN - // list projects resp, err := client.ListProjectBranchDatabases(projectID, defaultBranchID) if err != nil { return err } - // THEN dbs := resp.Databases if len(dbs) != 2 { return errors.New("only two databases is expected") @@ -411,14 +379,11 @@ resource "neon_database" "this" { // check the roles func(state *terraform.State) error { - // WHEN - // list projects resp, err := client.ListProjectBranchRoles(projectID, defaultBranchID) if err != nil { return err } - // THEN roles := resp.Roles if len(roles) != 2 { return errors.New("two roles are expected for the branch " + defaultBranchID) @@ -477,7 +442,7 @@ resource "neon_database" "this" { "neon_endpoint.this", "suspend_timeout_seconds", "0", ), resource.TestCheckResourceAttr( - "neon_endpoint.this", "compute_provisioner", "k8s-pod", + "neon_endpoint.this", "compute_provisioner", "k8s-neonvm", ), resource.TestCheckResourceAttr( "neon_endpoint.this", "type", "read_write", @@ -487,13 +452,11 @@ resource "neon_database" "this" { ), func(state *terraform.State) error { - // WHEN resp, err := client.ListProjectEndpoints(projectID) if err != nil { return err } - // THEN for _, endpoint := range resp.Endpoints { if endpoint.BranchID != defaultBranchID { if err := resource.TestCheckResourceAttr( @@ -576,39 +539,22 @@ func projectAllowedIPs(t *testing.T, client *neon.Client) { // check the project and its settings func(state *terraform.State) error { - // WHEN - // list projects - resp, err := client.ListProjects(nil, nil) + ref, err := readProjectInfo(client, projectName) if err != nil { - return errors.New("listing error: " + err.Error()) - } - - // THEN - var ref neon.ProjectListItem - for _, project := range resp.ProjectsResponse.Projects { - if project.Name == projectName { - ref = project - } - break - } - - if ref.ID == "" { - return errors.New("project " + projectName + " shall be created") + return err } var exceedingAllowedIPs []string - missingIPs := map[string]struct{}{} for _, ip := range wantAllowedIPs { missingIPs[ip] = struct{}{} } - for _, ip := range ref.Settings.AllowedIps.Ips { + for _, ip := range *ref.Settings.AllowedIps.Ips { if _, ok := missingIPs[ip]; ok { delete(missingIPs, ip) continue } - exceedingAllowedIPs = append(exceedingAllowedIPs, ip) } @@ -673,24 +619,9 @@ func projectAllowedIPs(t *testing.T, client *neon.Client) { // check the project and its settings func(state *terraform.State) error { - // WHEN - // list projects - resp, err := client.ListProjects(nil, nil) + ref, err := readProjectInfo(client, projectName) if err != nil { - return errors.New("listing error: " + err.Error()) - } - - // THEN - var ref neon.ProjectListItem - for _, project := range resp.ProjectsResponse.Projects { - if project.Name == projectName { - ref = project - } - break - } - - if ref.ID == "" { - return errors.New("project " + projectName + " shall be created") + return err } var exceedingAllowedIPs []string @@ -700,7 +631,7 @@ func projectAllowedIPs(t *testing.T, client *neon.Client) { missingIPs[ip] = struct{}{} } - for _, ip := range ref.Settings.AllowedIps.Ips { + for _, ip := range *ref.Settings.AllowedIps.Ips { if _, ok := missingIPs[ip]; ok { delete(missingIPs, ip) continue @@ -758,21 +689,9 @@ func projectLogicalReplication(t *testing.T, client *neon.Client) { "enable_logical_replication", "false", ), func(state *terraform.State) error { - resp, err := client.ListProjects(nil, nil) + ref, err := readProjectInfo(client, projectName) if err != nil { - return errors.New("listing error: " + err.Error()) - } - - var ref neon.ProjectListItem - for _, project := range resp.ProjectsResponse.Projects { - if project.Name == projectName { - ref = project - } - break - } - - if ref.ID == "" { - return errors.New("project " + projectName + " shall be created") + return err } if ref.Settings.EnableLogicalReplication == nil || *ref.Settings.EnableLogicalReplication { @@ -819,21 +738,9 @@ func projectLogicalReplication(t *testing.T, client *neon.Client) { "enable_logical_replication", "true", ), func(state *terraform.State) error { - resp, err := client.ListProjects(nil, nil) + ref, err := readProjectInfo(client, projectName) if err != nil { - return errors.New("listing error: " + err.Error()) - } - - var ref neon.ProjectListItem - for _, project := range resp.ProjectsResponse.Projects { - if project.Name == projectName { - ref = project - } - break - } - - if ref.ID == "" { - return errors.New("project " + projectName + " shall be created") + return err } if ref.Settings.EnableLogicalReplication == nil || !*ref.Settings.EnableLogicalReplication { @@ -850,6 +757,32 @@ func projectLogicalReplication(t *testing.T, client *neon.Client) { }) } +func readProjectInfo(client *neon.Client, projectName string) (neon.Project, error) { + resp, err := client.ListProjects(nil, nil) + if err != nil { + return neon.Project{}, errors.New("listing error: " + err.Error()) + } + + var id string + for _, project := range resp.ProjectsResponse.Projects { + if project.Name == projectName { + id = project.ID + } + break + } + + if id == "" { + return neon.Project{}, errors.New("project " + projectName + " shall be created") + } + + p, err := client.GetProject(id) + if err != nil { + return neon.Project{}, errors.New("project details error: " + err.Error()) + } + + return p.Project, nil +} + func mustParseFloat64(s string) float64 { v, err := strconv.ParseFloat(s, 64) if err != nil { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7c788a6..0bede86 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -57,7 +57,7 @@ var projectReadiness = delay{ } func init() { - rand.Seed(time.Now().Unix()) + rand.New(rand.NewSource(time.Now().Unix())) // Set descriptions to support markdown syntax, this will be used in document generation // and the language server. schema.DescriptionKind = schema.StringMarkdown @@ -74,11 +74,12 @@ func New(version string) *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "neon_project": resourceProject(), - "neon_branch": resourceBranch(), - "neon_endpoint": resourceEndpoint(), - "neon_role": resourceRole(), - "neon_database": resourceDatabase(), + "neon_project": resourceProject(), + "neon_branch": resourceBranch(), + "neon_endpoint": resourceEndpoint(), + "neon_role": resourceRole(), + "neon_database": resourceDatabase(), + "neon_project_permission": resourceProjectPermission(), }, ConfigureContextFunc: configure(version), } diff --git a/internal/provider/resource_project.go b/internal/provider/resource_project.go index 81f586c..241c569 100644 --- a/internal/provider/resource_project.go +++ b/internal/provider/resource_project.go @@ -560,7 +560,7 @@ func resourceProjectCreate(ctx context.Context, d *schema.ResourceData, meta int projectDef.Settings = &neon.ProjectSettingsData{} } projectDef.Settings.AllowedIps = &neon.AllowedIps{ - Ips: ips, + Ips: &ips, PrimaryBranchOnly: d.Get("allowed_ips_primary_branch_only").(bool), } } @@ -642,7 +642,7 @@ func resourceProjectUpdate(ctx context.Context, d *schema.ResourceData, meta int req.Settings = &neon.ProjectSettingsData{} } req.Settings.AllowedIps = &neon.AllowedIps{ - Ips: ips, + Ips: &ips, PrimaryBranchOnly: d.Get("allowed_ips_primary_branch_only").(bool), } } @@ -735,4 +735,7 @@ type sdkProject interface { ListProjectBranchEndpoints(string, string) (neon.EndpointsResponse, error) DeleteProject(string) (neon.ProjectResponse, error) ListProjectBranchDatabases(string, string) (neon.DatabasesResponse, error) + GrantPermissionToProject(projectID string, cfg neon.GrantPermissionToProjectRequest) (neon.ProjectPermission, error) + RevokePermissionFromProject(projectID string, permissionID string) (neon.ProjectPermission, error) + ListProjectPermissions(projectID string) (neon.ProjectPermissions, error) } diff --git a/internal/provider/resource_project_permission.go b/internal/provider/resource_project_permission.go new file mode 100644 index 0000000..b182f04 --- /dev/null +++ b/internal/provider/resource_project_permission.go @@ -0,0 +1,164 @@ +package provider + +import ( + "context" + "errors" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + neon "github.com/kislerdm/neon-sdk-go" +) + +func resourceProjectPermission() *schema.Resource { + return &schema.Resource{ + SchemaVersion: 1, + Description: `Project's access permission.`, + Importer: &schema.ResourceImporter{ + StateContext: resourceProjectPermissionImport, + }, + CreateContext: resourceProjectPermissionCreate, + ReadContext: resourceProjectPermissionRead, + DeleteContext: resourceProjectPermissionDelete, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "project_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Project ID.", + }, + "grantee": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Email of the user whom to grant project permission.", + }, + }, + } +} + +func resourceProjectPermissionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + projectID := d.Get("project_id").(string) + email := d.Get("grantee").(string) + + tflog.Trace(ctx, "grant project permission", map[string]interface{}{"projectID": projectID, "email": email}) + + resp, err := meta.(sdkProject).GrantPermissionToProject(projectID, neon.GrantPermissionToProjectRequest{Email: email}) + if err != nil { + return diag.FromErr(err) + } + + id := joinedIDProjectPermission{ + projectID: projectID, + permissionID: resp.ID, + } + d.SetId(id.ToString()) + + return nil +} + +func resourceProjectPermissionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + joinedID, _ := parseJoinedIDProjectPermission(d.Id()) + + tflog.Trace(ctx, "revoke project permission", map[string]interface{}{ + "projectID": joinedID.projectID, + "permissionID": joinedID.permissionID, + }) + + if _, err := meta.(sdkProject).RevokePermissionFromProject(joinedID.projectID, joinedID.permissionID); err != nil { + return diag.FromErr(err) + } + + d.SetId("") + return nil +} + +func resourceProjectPermissionImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + tflog.Trace(ctx, "import project permission", map[string]interface{}{"id": d.Id()}) + err, found := readProjectPermission(ctx, d, meta) + if err != nil { + return nil, err + } + + if !found { + return nil, errors.New("no permission found") + } + + return []*schema.ResourceData{d}, nil +} + +func resourceProjectPermissionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + err, found := readProjectPermission(ctx, d, meta) + if err != nil { + return diag.FromErr(err) + } + + if !found { + tflog.Trace(ctx, "no project permission found") + } + + return nil +} + +func readProjectPermission(ctx context.Context, d *schema.ResourceData, meta interface{}) (error, bool) { + tflog.Trace(ctx, "parse project permission found", map[string]interface{}{"id": d.Id()}) + + joinedID, err := parseJoinedIDProjectPermission(d.Id()) + if err != nil { + return err, false + } + + projectID := joinedID.projectID + tflog.Trace(ctx, "list project permissions", map[string]interface{}{"projectID": projectID}) + + resp, err := meta.(sdkProject).ListProjectPermissions(projectID) + if err != nil { + return err, false + } + + tflog.Trace(ctx, "search project permission", map[string]interface{}{ + "projectID": projectID, + "permissionID": joinedID.permissionID, + }) + + for _, permission := range resp.ProjectPermissions { + if permission.ID == joinedID.permissionID { + if err := d.Set("project_id", projectID); err != nil { + return err, false + } + if err := d.Set("grantee", permission.GrantedToEmail); err != nil { + return err, false + } + return nil, true + } + } + return nil, false +} + +type joinedIDProjectPermission struct { + projectID, permissionID string +} + +const joinedIDProjectPermissionSeparator = "/" + +func (v joinedIDProjectPermission) ToString() string { + return v.projectID + joinedIDProjectPermissionSeparator + v.permissionID +} + +func parseJoinedIDProjectPermission(s string) (*joinedIDProjectPermission, error) { + els := strings.SplitN(s, joinedIDProjectPermissionSeparator, 2) + + if len(els) != 2 { + return nil, errors.New("not recognized format of the project permission resource's ID") + } + + return &joinedIDProjectPermission{ + projectID: els[0], + permissionID: els[1], + }, nil +} diff --git a/internal/provider/resource_project_permission_test.go b/internal/provider/resource_project_permission_test.go new file mode 100644 index 0000000..48d5b86 --- /dev/null +++ b/internal/provider/resource_project_permission_test.go @@ -0,0 +1,344 @@ +package provider + +import ( + "context" + "errors" + "testing" + "time" + + neon "github.com/kislerdm/neon-sdk-go" +) + +func Test_resourceProjectPermissionCreate(t *testing.T) { + t.Parallel() + + t.Run("shall create permission for foo@bar.baz", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + + const ( + email = "foo@bar.baz" + projectID = "myproject" + ) + + if err := definition.Set("grantee", email); err != nil { + t.Fatal(err) + } + if err := definition.Set("project_id", projectID); err != nil { + t.Fatal(err) + } + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + ProjectPermissions: neon.ProjectPermissions{ + ProjectPermissions: []neon.ProjectPermission{}, + }, + }, + } + + d := resourceProjectPermissionCreate(context.TODO(), definition, meta) + if d.HasError() { + t.Fatalf("unexpected errors: %v", d) + } + + gotEmail := meta.ProjectPermissions.ProjectPermissions[0].GrantedToEmail + if gotEmail != email { + t.Fatal("email is not set") + } + + gotPermissionID := meta.ProjectPermissions.ProjectPermissions[0].ID + + wantID := projectID + "/" + gotPermissionID + if definition.Id() != wantID { + t.Errorf("unexpected resource ID: want=%s, got=%s", wantID, definition.Id()) + } + }) + + t.Run("unhappy path", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + + const ( + email = "foo@bar.baz" + projectID = "myproject" + ) + + if err := definition.Set("grantee", email); err != nil { + t.Fatal(err) + } + if err := definition.Set("project_id", projectID); err != nil { + t.Fatal(err) + } + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + err: errors.New("foobar"), + }, + } + + d := resourceProjectPermissionCreate(context.TODO(), definition, meta) + if !d.HasError() { + t.Fatalf("error expected") + } + + if definition.Id() != "" { + t.Errorf("unexpected resource ID: want=%s, got=%s", "", definition.Id()) + } + }) +} + +func Test_resourceProjectPermissionDelete(t *testing.T) { + t.Parallel() + + t.Run("shall revoke existing permission", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + + const ( + email = "foo@bar.baz" + projectID = "myproject" + permissionID = "mypermission" + ) + + if err := definition.Set("grantee", email); err != nil { + t.Fatal(err) + } + if err := definition.Set("project_id", projectID); err != nil { + t.Fatal(err) + } + + id := projectID + "/" + permissionID + definition.SetId(id) + + meta := &sdkClientStub{} + d := resourceProjectPermissionDelete(context.TODO(), definition, meta) + if d.HasError() { + t.Fatalf("unexpected errors: %v", d) + } + + if definition.Id() != "" { + t.Errorf("unexpected resource ID: want=%s, got=%s", "", definition.Id()) + } + }) + + t.Run("unhappy path", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + + const ( + email = "foo@bar.baz" + projectID = "myproject" + permissionID = "mypermission" + ) + + if err := definition.Set("grantee", email); err != nil { + t.Fatal(err) + } + if err := definition.Set("project_id", projectID); err != nil { + t.Fatal(err) + } + id := projectID + "/" + permissionID + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + err: errors.New("foobar"), + }, + } + + d := resourceProjectPermissionCreate(context.TODO(), definition, meta) + if !d.HasError() { + t.Fatalf("error expected") + } + + if definition.Id() != id { + t.Errorf("unexpected resource ID: want=%s, got=%s", id, definition.Id()) + } + }) +} + +func Test_resourceProjectPermissionRead(t *testing.T) { + t.Parallel() + const ( + projectID = "myproject" + permissionID = "mypermission" + id = projectID + "/" + permissionID + ) + + t.Run("shall find the permission given the resource id", func(t *testing.T) { + const email = "foo@bar.baz" + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + ProjectPermissions: neon.ProjectPermissions{ + ProjectPermissions: []neon.ProjectPermission{ + { + GrantedAt: time.Now().UTC(), + GrantedToEmail: email, + ID: permissionID, + }, + }, + }, + }, + } + + d := resourceProjectPermissionRead(context.TODO(), definition, meta) + if d.HasError() { + t.Fatalf("unexpected errors: %v", d) + } + + gotEmail := definition.Get("grantee").(string) + if gotEmail != email { + t.Fatalf("unexpected grantee email found: want=%s, got=%s", email, gotEmail) + } + + gotProjectID := definition.Get("project_id").(string) + if gotProjectID != projectID { + t.Fatalf("unexpected projectID found: want=%s, got=%s", projectID, gotProjectID) + } + }) + + t.Run("shall find no permission by its id", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + ProjectPermissions: neon.ProjectPermissions{}, + }, + } + + d := resourceProjectPermissionRead(context.TODO(), definition, meta) + if d.HasError() { + t.Fatalf("unexpected errors: %v", d) + } + + gotEmail := definition.Get("grantee").(string) + if gotEmail != "" { + t.Fatalf("unexpected grantee email found: want=%s, got=%s", "", gotEmail) + } + + gotProjectID := definition.Get("project_id").(string) + if gotProjectID != "" { + t.Fatalf("unexpected projectID found: want=%s, got=%s", "", gotProjectID) + } + }) + + t.Run("shall fail when listing project's permissions", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + err: errors.New("foobar"), + }, + } + + d := resourceProjectPermissionRead(context.TODO(), definition, meta) + if !d.HasError() { + t.Fatal("error expected") + } + }) +} + +func Test_resourceProjectPermissionImport(t *testing.T) { + t.Parallel() + + const ( + projectID = "foo" + permissionID = "bar" + id = projectID + "/" + permissionID + email = "foo@bar.baz" + ) + + t.Run("shall import the permission given its id", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + ProjectPermissions: neon.ProjectPermissions{ + ProjectPermissions: []neon.ProjectPermission{ + { + GrantedAt: time.Now().UTC(), + GrantedToEmail: email, + ID: permissionID, + }, + }, + }, + }, + } + + resources, err := resourceProjectPermissionImport(context.TODO(), definition, meta) + if err != nil { + t.Fatalf("unexpected errors: %v", err) + } + + d := resources[0] + + gotEmail := d.Get("grantee").(string) + if gotEmail != email { + t.Fatalf("unexpected grantee email found: want=%s, got=%s", email, gotEmail) + } + + gotProjectID := d.Get("project_id").(string) + if gotProjectID != projectID { + t.Fatalf("unexpected projectID found: want=%s, got=%s", projectID, gotProjectID) + } + }) + + t.Run("shall fail because no permission was found by its id", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + ProjectPermissions: neon.ProjectPermissions{}, + }, + } + + _, err := resourceProjectPermissionImport(context.TODO(), definition, meta) + const wantErrStr = "no permission found" + if err.Error() != wantErrStr { + t.Fatalf("'%s' error expected", wantErrStr) + } + }) + + t.Run("shall fail because provided id is not correct", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId("qux") + + meta := &sdkClientStub{} + _, err := resourceProjectPermissionImport(context.TODO(), definition, meta) + + const wantErrStr = "not recognized format of the project permission resource's ID" + if err.Error() != wantErrStr { + t.Fatalf("'%s' error expected", wantErrStr) + } + }) + + t.Run("shall fail when listing project's permissions", func(t *testing.T) { + resource := resourceProjectPermission() + definition := resource.TestResourceData() + definition.SetId(id) + + meta := &sdkClientStub{ + stubProjectPermission: stubProjectPermission{ + err: errors.New("foobar"), + }, + } + + _, err := resourceProjectPermissionImport(context.TODO(), definition, meta) + if err == nil { + t.Fatal("error expected") + } + }) +} diff --git a/internal/provider/resource_project_test.go b/internal/provider/resource_project_test.go index 40d0ae5..ff038f6 100644 --- a/internal/provider/resource_project_test.go +++ b/internal/provider/resource_project_test.go @@ -230,7 +230,7 @@ func Test_resourceProjectCreate(t *testing.T) { got := v.Project.Settings.AllowedIps var ipsExcess []string - for _, ip := range got.Ips { + for _, ip := range *got.Ips { if _, ok := ipsMap[ip]; ok { delete(ipsMap, ip) } else { diff --git a/internal/provider/stub.go b/internal/provider/stub.go index b67c53e..928547c 100644 --- a/internal/provider/stub.go +++ b/internal/provider/stub.go @@ -1,8 +1,14 @@ package provider -import neon "github.com/kislerdm/neon-sdk-go" +import ( + "time" + + "github.com/google/uuid" + neon "github.com/kislerdm/neon-sdk-go" +) type sdkClientStub struct { + stubProjectPermission req interface{} err error } @@ -42,3 +48,44 @@ func (s *sdkClientStub) GetProjectBranchRolePassword(_ string, _ string, _ strin } return neon.RolePasswordResponse{}, nil } + +type stubProjectPermission struct { + ProjectPermissions neon.ProjectPermissions + err error +} + +func (s *stubProjectPermission) GrantPermissionToProject(_ string, cfg neon.GrantPermissionToProjectRequest) (neon.ProjectPermission, error) { + if s.err != nil { + return neon.ProjectPermission{}, s.err + } + + resp := neon.ProjectPermission{ + GrantedAt: time.Now().UTC(), + GrantedToEmail: cfg.Email, + ID: uuid.NewString(), + } + + s.ProjectPermissions.ProjectPermissions = append(s.ProjectPermissions.ProjectPermissions, resp) + return resp, nil +} + +func (s *stubProjectPermission) RevokePermissionFromProject(_ string, permissionID string) (neon.ProjectPermission, error) { + if s.err != nil { + return neon.ProjectPermission{}, s.err + } + + now := time.Now().UTC() + return neon.ProjectPermission{ + GrantedAt: now.Add(-1 * time.Second), + GrantedToEmail: "foo@bar.baz", + ID: permissionID, + RevokedAt: &now, + }, nil +} + +func (s *stubProjectPermission) ListProjectPermissions(_ string) (neon.ProjectPermissions, error) { + if s.err != nil { + return neon.ProjectPermissions{}, s.err + } + return s.ProjectPermissions, nil +}