From c8cbcd4f19583fa23f975c4ea0f5a8ba41310ce3 Mon Sep 17 00:00:00 2001 From: Oscar Hermoso Date: Sat, 17 Aug 2024 14:55:19 +0800 Subject: [PATCH] Fix data sources for servers, add tests (tests still WIP) --- CONTRIBUTING.md | 7 ++ docs/data-sources/server.md | 18 +++- docs/data-sources/ssh_key.md | 12 ++- go.mod | 23 +++-- go.sum | 49 ++++++++-- .../data_sources/server_data_source_gen.go | 26 ----- .../data_sources/ssh_key_data_source_gen.go | 26 ----- internal/provider/provider_test.go | 25 +++++ internal/provider/server_data_source.go | 78 ++++++++++++--- internal/provider/server_resource.go | 22 +++-- internal/provider/server_resource_test.go | 81 +++++++++++++++ internal/provider/ssh_key_data_source.go | 34 +++++-- internal/provider/ssh_key_resource.go | 45 +++++++-- internal/provider/ssh_key_resource_test.go | 67 +++++++++++++ internal/provider/util.go | 98 +++++++++++++++++++ provider_code_spec.json | 30 ------ provider_gen_config.yml | 61 ++++++------ 17 files changed, 533 insertions(+), 169 deletions(-) delete mode 100644 internal/data_sources/server_data_source_gen.go delete mode 100644 internal/data_sources/ssh_key_data_source_gen.go create mode 100644 internal/provider/provider_test.go create mode 100644 internal/provider/server_resource_test.go create mode 100644 internal/provider/ssh_key_resource_test.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5133e7f..c8827bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,6 +32,13 @@ terraform plan terraform apply ``` +### Testing + + +```sh +TF_ACC=1 go test -v ./... +``` + ### Adding resources/data sources to the provider 1. Make any changes to `provider_gen_config.yml` (see https://developer.hashicorp.com/terraform/plugin/code-generation/openapi-generator#generator-config) diff --git a/docs/data-sources/server.md b/docs/data-sources/server.md index d7f4ebe..a4a1435 100644 --- a/docs/data-sources/server.md +++ b/docs/data-sources/server.md @@ -3,18 +3,30 @@ page_title: "binarylane_server Data Source - terraform-provider-binarylane" subcategory: "" description: |- - + TODO --- # binarylane_server (Data Source) - +TODO ## Schema +### Required + +- `id` (Number) The ID of the server to fetch. + +### Optional + +- `backups` (Boolean) If true this will enable two daily backups for the server. Options.daily_backups will override this value if provided. Setting this to false has no effect. +- `name` (String) The hostname of your server, such as vps01.yourcompany.com. If not provided, the server will be created with a random name. +- `password` (String) If this is provided the specified or default remote user's account password will be set to this value. Only valid if the server supports password change actions (check server.password_change_supported via the servers endpoint). If omitted and the server supports password change actions a random password will be generated and emailed to the account email address. + ### Read-Only -- `id` (String) The ID of this resource. +- `image` (String) The slug of the selected operating system. +- `region` (String) The slug of the selected region. +- `size` (String) The slug of the selected size. diff --git a/docs/data-sources/ssh_key.md b/docs/data-sources/ssh_key.md index 16fa1df..e771827 100644 --- a/docs/data-sources/ssh_key.md +++ b/docs/data-sources/ssh_key.md @@ -3,18 +3,24 @@ page_title: "binarylane_ssh_key Data Source - terraform-provider-binarylane" subcategory: "" description: |- - + TODO --- # binarylane_ssh_key (Data Source) - +TODO ## Schema +### Optional + +- `default` (Boolean) Optional: If true this will be added to all new server installations (if we support SSH Key injection for the server's operating system). +- `id` (Number) The ID or fingerprint of the SSH Key to fetch. + ### Read-Only -- `id` (String) The ID of this resource. +- `name` (String) A name to help you identify the key. +- `public_key` (String) The public key in OpenSSH "authorized_keys" format. diff --git a/go.mod b/go.mod index 9d1353c..49edc99 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.19.4 github.com/hashicorp/terraform-plugin-framework v1.11.0 github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 + github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-log v0.9.0 + github.com/hashicorp/terraform-plugin-testing v1.10.0 github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 github.com/oapi-codegen/runtime v1.1.1 ) @@ -21,6 +23,7 @@ require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect + github.com/agext/levenshtein v1.2.2 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect @@ -37,11 +40,13 @@ require ( github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/swag v0.22.8 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // 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 + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect @@ -49,10 +54,12 @@ require ( github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hc-install v0.8.0 // indirect + github.com/hashicorp/hcl/v2 v2.21.0 // indirect + github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect github.com/hashicorp/terraform-json v0.22.1 // indirect github.com/hashicorp/terraform-plugin-codegen-spec v0.1.1 // indirect - github.com/hashicorp/terraform-plugin-go v0.23.0 // indirect + github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.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 @@ -66,6 +73,8 @@ require ( github.com/mattn/go-runewidth v0.0.15 // 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 + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/oklog/run v1.0.0 // indirect @@ -76,6 +85,7 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.1 // indirect + 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/vmware-labs/yaml-jsonpath v0.3.2 // indirect @@ -88,16 +98,17 @@ require ( github.com/zclconf/go-cty v1.15.0 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect golang.org/x/crypto v0.26.0 // indirect - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect + golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.26.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.22.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.34.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect + google.golang.org/grpc v1.64.1 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b23fa7b..deef30d 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,11 @@ github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5 github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= +github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +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 v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= @@ -77,6 +80,7 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -109,6 +113,8 @@ github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuD github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= @@ -125,6 +131,10 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI= github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU= +github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= +github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= +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.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg= github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec= @@ -145,6 +155,10 @@ github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/12 github.com/hashicorp/terraform-plugin-go v0.23.0/go.mod h1:1E3Cr9h2vMlahWMbsSEcNrOCxovCZhOOIXjFHbjc/lQ= 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-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg= +github.com/hashicorp/terraform-plugin-testing v1.10.0 h1:2+tmRNhvnfE4Bs8rB6v58S/VpqzGC6RCh9Y8ujdn+aw= +github.com/hashicorp/terraform-plugin-testing v1.10.0/go.mod h1:iWRW3+loP33WMch2P/TEyCxxct/ZEcCGMquSLSCVsrc= 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-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -195,6 +209,10 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -260,6 +278,9 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +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= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -285,6 +306,8 @@ github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUei github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -294,8 +317,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc= +golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= @@ -310,8 +333,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -349,9 +372,11 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn 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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= @@ -365,10 +390,13 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -377,9 +405,10 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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= diff --git a/internal/data_sources/server_data_source_gen.go b/internal/data_sources/server_data_source_gen.go deleted file mode 100644 index 99719de..0000000 --- a/internal/data_sources/server_data_source_gen.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by terraform-plugin-framework-generator DO NOT EDIT. - -package data_sources - -import ( - "context" - "github.com/hashicorp/terraform-plugin-framework/types" - - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" -) - -func ServerDataSourceSchema(ctx context.Context) schema.Schema { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.Int64Attribute{ - Required: true, - Description: "The ID of the server to fetch.", - MarkdownDescription: "The ID of the server to fetch.", - }, - }, - } -} - -type ServerModel struct { - Id types.Int64 `tfsdk:"id"` -} diff --git a/internal/data_sources/ssh_key_data_source_gen.go b/internal/data_sources/ssh_key_data_source_gen.go deleted file mode 100644 index 88cf61a..0000000 --- a/internal/data_sources/ssh_key_data_source_gen.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by terraform-plugin-framework-generator DO NOT EDIT. - -package data_sources - -import ( - "context" - "github.com/hashicorp/terraform-plugin-framework/types" - - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" -) - -func SshKeyDataSourceSchema(ctx context.Context) schema.Schema { - return schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.Int64Attribute{ - Required: true, - Description: "The ID or fingerprint of the SSH Key to fetch.", - MarkdownDescription: "The ID or fingerprint of the SSH Key to fetch.", - }, - }, - } -} - -type SshKeyModel struct { - Id types.Int64 `tfsdk:"id"` -} diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go new file mode 100644 index 0000000..6cd4caa --- /dev/null +++ b/internal/provider/provider_test.go @@ -0,0 +1,25 @@ +package provider + +import ( + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" +) + +const ( + providerConfig = ` +provider "binarylane" { + # api_token = "test123" + # api_endpoint = "https://test.binarylane.internal.au/v2" +} +` +) + +var ( + // testAccProtoV6ProviderFactories are used to instantiate a provider during + // acceptance testing. The factory function will be invoked for every Terraform + // CLI command executed to create a provider server to which the CLI can + // reattach. + testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "binarylane": providerserver.NewProtocol6WithError(New("test")()), + } +) diff --git a/internal/provider/server_data_source.go b/internal/provider/server_data_source.go index ffd2086..58ecd8a 100644 --- a/internal/provider/server_data_source.go +++ b/internal/provider/server_data_source.go @@ -2,36 +2,66 @@ package provider import ( "context" - "terraform-provider-binarylane/internal/data_sources" + "fmt" + "terraform-provider-binarylane/internal/resources" "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" ) -var _ datasource.DataSource = (*serverDataSource)(nil) +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &serverDataSource{} + _ datasource.DataSourceWithConfigure = &serverDataSource{} +) func NewServerDataSource() datasource.DataSource { return &serverDataSource{} } -type serverDataSource struct{} +type serverDataSource struct { + bc *BinarylaneClient +} + +func (d *serverDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + bc, ok := req.ProviderData.(BinarylaneClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData), + ) + + return + } + + d.bc = &bc +} func (d *serverDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_server" } func (d *serverDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Computed: true, - }, - }, + resp.Schema = *convertResourceSchemaToDataSourceSchema(ctx, resources.ServerResourceSchema(ctx)) + resp.Schema.Description = "TODO" + + // Overrides + id := resp.Schema.Attributes["id"] + resp.Schema.Attributes["id"] = d_schema.Int64Attribute{ + Description: id.GetDescription(), + MarkdownDescription: id.GetMarkdownDescription(), + Required: true, // ID is required to find the server } } func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var data data_sources.ServerModel + var data resources.ServerModel // Read Terraform configuration data into the model resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) @@ -41,9 +71,31 @@ func (d *serverDataSource) Read(ctx context.Context, req datasource.ReadRequest, } // Read API call logic + serverResp, err := d.bc.client.GetServersServerIdWithResponse(ctx, data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error reading server: name=%s", data.Id.String()), + err.Error(), + ) + return + } - // Example data value setting - // data.Id = types.StringValue("example-id") + // Set data values + tflog.Trace(ctx, + fmt.Sprintf( + "Reading server: id=%d, name=%s, image=%s, region=%s, size=%s", + *serverResp.JSON200.Server.Id, + *serverResp.JSON200.Server.Name, + *serverResp.JSON200.Server.Image.Slug, + *serverResp.JSON200.Server.Region.Slug, + *serverResp.JSON200.Server.Size.Slug, + ), + ) + data.Id = types.Int64Value(*serverResp.JSON200.Server.Id) + data.Name = types.StringValue(*serverResp.JSON200.Server.Name) + data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug) + data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug) + data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug) // Save data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) diff --git a/internal/provider/server_resource.go b/internal/provider/server_resource.go index 8ab1fa9..cf0ce7b 100644 --- a/internal/provider/server_resource.go +++ b/internal/provider/server_resource.go @@ -61,17 +61,17 @@ func (r *serverResource) Schema(ctx context.Context, _ resource.SchemaRequest, r resp.Schema.Attributes["password"] = &schema.StringAttribute{ Description: pw.GetDescription(), MarkdownDescription: pw.GetMarkdownDescription(), - Optional: true, - Computed: true, - Sensitive: true, // Add sensitive to password + Optional: pw.IsOptional(), + Computed: false, // Computed must be false to allow server to be created without password + Sensitive: true, // Mark password as sensitive } backups := resp.Schema.Attributes["backups"] resp.Schema.Attributes["backups"] = &schema.BoolAttribute{ Description: backups.GetDescription(), MarkdownDescription: backups.GetMarkdownDescription(), - Optional: true, - Computed: true, + Optional: backups.IsComputed(), + Computed: backups.IsOptional(), Default: booldefault.StaticBool(false), // Add default to backups } } @@ -162,8 +162,11 @@ func (r *serverResource) Read(ctx context.Context, req resource.ReadRequest, res return } - assignInt64(serverResp.JSON200.Server.Id, &data.Id) - assignStr(serverResp.JSON200.Server.Name, &data.Name) + data.Id = types.Int64Value(*serverResp.JSON200.Server.Id) + data.Name = types.StringValue(*serverResp.JSON200.Server.Name) + data.Image = types.StringValue(*serverResp.JSON200.Server.Image.Slug) + data.Region = types.StringValue(*serverResp.JSON200.Server.Region.Slug) + data.Size = types.StringValue(*serverResp.JSON200.Server.Size.Slug) if resp.Diagnostics.HasError() { return @@ -224,3 +227,8 @@ func (r *serverResource) Delete(ctx context.Context, req resource.DeleteRequest, return } } + +// func (r *serverResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +// // Retrieve import ID and save to id attribute +// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +// } diff --git a/internal/provider/server_resource_test.go b/internal/provider/server_resource_test.go new file mode 100644 index 0000000..388e967 --- /dev/null +++ b/internal/provider/server_resource_test.go @@ -0,0 +1,81 @@ +package provider + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestServerResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: providerConfig + ` +resource "binarylane_server" "test" { + name = "tf-server-resource-test" + region = "per" + image = "ubuntu-24.04" + size = "std-min" +} + +data "binarylane_server" "test" { + depends_on = [binarylane_server.test] + + id = binarylane_server.test.id +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + // Verify resource values + resource.TestCheckResourceAttr("binarylane_server.test", "name", "tf-server-resource-test"), + resource.TestCheckResourceAttr("binarylane_server.test", "region", "per"), + resource.TestCheckResourceAttr("binarylane_server.test", "image", "ubuntu-24.04"), + resource.TestCheckResourceAttr("binarylane_server.test", "size", "std-min"), + resource.TestCheckResourceAttrSet("binarylane_server.test", "id"), + + // Verify data source values + resource.TestCheckResourceAttr("data.binarylane_server.test", "name", "tf-server-resource-test"), + resource.TestCheckResourceAttr("data.binarylane_server.test", "region", "per"), + resource.TestCheckResourceAttr("data.binarylane_server.test", "image", "ubuntu-24.04"), + resource.TestCheckResourceAttr("data.binarylane_server.test", "size", "std-min"), + resource.TestCheckResourceAttrSet("data.binarylane_server.test", "id"), + ), + }, + // ImportState testing + // { + // ResourceName: "binarylane_server.test", + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{}, // nothing to ignore + // }, + // TODO: Update and Read testing + // { + // Config: providerConfig + ` + // resource "binarylane_server" "test" { + // items = [ + // { + // coffee = { + // id = 2 + // } + // quantity = 2 + // }, + // ] + // } + // `, + // Check: resource.ComposeAggregateTestCheckFunc( + // // Verify first order item updated + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.quantity", "2"), + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.id", "2"), + // // Verify first coffee item has Computed attributes updated. + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.description", ""), + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.image", "/packer.png"), + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.name", "Packer Spiced Latte"), + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.price", "350"), + // resource.TestCheckResourceAttr("binarylane_server.test", "items.0.coffee.teaser", "Packed with goodness to spice up your images"), + // ), + // }, + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/internal/provider/ssh_key_data_source.go b/internal/provider/ssh_key_data_source.go index 3cc971b..95881e0 100644 --- a/internal/provider/ssh_key_data_source.go +++ b/internal/provider/ssh_key_data_source.go @@ -7,11 +7,14 @@ import ( "terraform-provider-binarylane/internal/resources" "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" ) -var _ datasource.DataSource = (*sshKeyDataSource)(nil) +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &sshKeyDataSource{} + _ datasource.DataSourceWithConfigure = &sshKeyDataSource{} +) func NewSshKeyDataSource() datasource.DataSource { return &sshKeyDataSource{} @@ -21,18 +24,31 @@ type sshKeyDataSource struct { bc *BinarylaneClient } +func (d *sshKeyDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + bc, ok := req.ProviderData.(BinarylaneClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *BinarylaneClient, got: %T.", req.ProviderData), + ) + + return + } + + d.bc = &bc +} + func (d *sshKeyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_ssh_key" } func (d *sshKeyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Computed: true, - }, - }, - } + resp.Schema = *convertResourceSchemaToDataSourceSchema(ctx, resources.SshKeyResourceSchema(ctx)) + resp.Schema.Description = "TODO" } func (d *sshKeyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { diff --git a/internal/provider/ssh_key_resource.go b/internal/provider/ssh_key_resource.go index af0c39a..2fbb0d6 100644 --- a/internal/provider/ssh_key_resource.go +++ b/internal/provider/ssh_key_resource.go @@ -10,6 +10,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "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/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -55,14 +57,22 @@ func (r *sshKeyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp.Schema.Description = "TODO" // Overrides - defaultAttr := resp.Schema.Attributes["default"] + default_ := resp.Schema.Attributes["default"] resp.Schema.Attributes["default"] = schema.BoolAttribute{ Optional: true, Computed: true, - Description: defaultAttr.GetDescription(), - MarkdownDescription: defaultAttr.GetMarkdownDescription(), + Description: default_.GetDescription(), + MarkdownDescription: default_.GetMarkdownDescription(), Default: booldefault.StaticBool(false), // Add default to backups } + + public_key := resp.Schema.Attributes["public_key"] + resp.Schema.Attributes["public_key"] = schema.StringAttribute{ + Required: true, + Description: public_key.GetDescription(), + MarkdownDescription: public_key.GetMarkdownDescription(), + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + } } func (r *sshKeyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -113,6 +123,8 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res // Read Terraform prior state data into the model resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + data.PublicKey = types.StringValue(data.PublicKey.ValueString()) + if resp.Diagnostics.HasError() { return } @@ -138,7 +150,7 @@ func (r *sshKeyResource) Read(ctx context.Context, req resource.ReadRequest, res data.Id = types.Int64Value(*sshResp.JSON200.SshKey.Id) data.Default = types.BoolValue(*sshResp.JSON200.SshKey.Default) data.Name = types.StringValue(*sshResp.JSON200.SshKey.Name) - data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey) + // data.PublicKey = types.StringValue(*sshResp.JSON200.SshKey.PublicKey) // don't set or it will force replacement every time // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -149,13 +161,29 @@ func (r *sshKeyResource) Update(ctx context.Context, req resource.UpdateRequest, // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { return } // Update API call logic - // TODO + sshResp, err := r.bc.client.PutAccountKeysKeyIdWithResponse(ctx, int(data.Id.ValueInt64()), binarylane.UpdateSshKeyRequest{ + Name: data.Name.ValueString(), + Default: data.Default.ValueBoolPointer(), + }) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Error updating SSH Key: name=%s", data.Name.ValueString()), + err.Error(), + ) + return + } + if sshResp.StatusCode() != http.StatusOK { + resp.Diagnostics.AddError( + "Unexpected HTTP status code updating SSH Key", + fmt.Sprintf("Received %s updating new SSH Key: name=%s. Details: %s", sshResp.Status(), data.Name.ValueString(), sshResp.Body), + ) + return + } // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -188,3 +216,8 @@ func (r *sshKeyResource) Delete(ctx context.Context, req resource.DeleteRequest, return } } + +// func (r *sshKeyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +// // Retrieve import ID and save to id attribute +// resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +// } diff --git a/internal/provider/ssh_key_resource_test.go b/internal/provider/ssh_key_resource_test.go new file mode 100644 index 0000000..c27f53f --- /dev/null +++ b/internal/provider/ssh_key_resource_test.go @@ -0,0 +1,67 @@ +package provider + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestSshKeyResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: providerConfig + ` +resource "binarylane_ssh_key" "test" { + name = "tf_ssh_key_resource_test" + public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal" +} + +data "binarylane_ssh_key" "test" { + depends_on = [binarylane_ssh_key.test] + + id = binarylane_ssh_key.test.id +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + // Verify resource values + resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"), + resource.TestCheckResourceAttr("binarylane_ssh_key.test", "public_key", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"), + resource.TestCheckResourceAttr("binarylane_ssh_key.test", "default", "false"), + resource.TestCheckResourceAttrSet("binarylane_ssh_key.test", "id"), + + // Verify data source values + resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"), + resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "public_key", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal"), + resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "false"), + resource.TestCheckResourceAttrSet("data.binarylane_ssh_key.test", "id"), + ), + }, + // ImportState testing + // TODO + // { + // ResourceName: "binarylane_ssh_key.test", + // ImportState: true, + // ImportStateVerify: true, + // ImportStateVerifyIgnore: []string{}, // nothing to ignore + // }, + // TODO: Update and Read testing + // { + // Config: providerConfig + ` + // resource "binarylane_ssh_key" "test" { + // name = "tf_ssh_key_resource_test" + // public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJCsuosklP0T4fJcQDgkeVh7dQu+eV+vev1CfwdUkj7h test@company.internal" + // default = true + // } + // `, + // Check: resource.ComposeAggregateTestCheckFunc( + // // Verify resource values + // resource.TestCheckResourceAttr("binarylane_ssh_key.test", "name", "tf_ssh_key_resource_test"), + // resource.TestCheckResourceAttr("data.binarylane_ssh_key.test", "default", "true"), + // ), + // }, + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/internal/provider/util.go b/internal/provider/util.go index 0fcb6c2..282ea28 100644 --- a/internal/provider/util.go +++ b/internal/provider/util.go @@ -1,8 +1,13 @@ package provider import ( + "context" + + d_schema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + r_schema "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func assignStr(source *string, target *basetypes.StringValue) { @@ -34,3 +39,96 @@ func assignInt64(source *int64, target *basetypes.Int64Value) { // *target = types.BoolValue(bool(*source)) // } // } + +func convertResourceSchemaToDataSourceSchema(ctx context.Context, rs r_schema.Schema) *d_schema.Schema { + ds := d_schema.Schema{ + Attributes: make(map[string]d_schema.Attribute, len(rs.Attributes)), + Blocks: make(map[string]d_schema.Block), + Description: rs.GetDescription(), + MarkdownDescription: rs.GetMarkdownDescription(), + DeprecationMessage: rs.GetDeprecationMessage(), + } + for name, attr := range rs.Attributes { + switch attr.GetType() { + case types.BoolType: + ds.Attributes[name] = d_schema.BoolAttribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.DynamicType: + ds.Attributes[name] = d_schema.DynamicAttribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.Float32Type: + ds.Attributes[name] = d_schema.Float32Attribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.Float64Type: + ds.Attributes[name] = d_schema.Float64Attribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.Int32Type: + ds.Attributes[name] = d_schema.Int32Attribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.Int64Type: + ds.Attributes[name] = d_schema.Int64Attribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.NumberType: + ds.Attributes[name] = d_schema.NumberAttribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + case types.StringType: + ds.Attributes[name] = d_schema.StringAttribute{ + Description: attr.GetDescription(), + Optional: attr.IsOptional(), + Computed: true, + Sensitive: attr.IsSensitive(), + MarkdownDescription: attr.GetMarkdownDescription(), + DeprecationMessage: attr.GetDeprecationMessage(), + } + default: + tflog.Error(ctx, "Failed to convert resource schema attribute to data source schema attribute", map[string]interface{}{ + "attribute": name, + "type": attr.GetType(), + }) + } + } + + return &ds +} diff --git a/provider_code_spec.json b/provider_code_spec.json index 7689784..75a77db 100644 --- a/provider_code_spec.json +++ b/provider_code_spec.json @@ -1,34 +1,4 @@ { - "datasources": [ - { - "name": "server", - "schema": { - "attributes": [ - { - "name": "id", - "int64": { - "computed_optional_required": "required", - "description": "The ID of the server to fetch." - } - } - ] - } - }, - { - "name": "ssh_key", - "schema": { - "attributes": [ - { - "name": "id", - "int64": { - "computed_optional_required": "required", - "description": "The ID or fingerprint of the SSH Key to fetch." - } - } - ] - } - } - ], "provider": { "name": "binarylane" }, diff --git a/provider_gen_config.yml b/provider_gen_config.yml index 50a86bb..f4b5c11 100644 --- a/provider_gen_config.yml +++ b/provider_gen_config.yml @@ -75,33 +75,34 @@ resources: key_id: id ignores: - ssh_key - -data_sources: - server: - read: - path: /servers/{server_id} - method: GET - schema: - attributes: - aliases: - server_id: id - ignores: - - ipv6 - - licenses - - links - - options - - server - - ssh_keys - - vpc_id - - user_data - - port_blocking - ssh_key: - read: - path: /account/keys/{key_id} - method: GET - schema: - attributes: - aliases: - key_id: id - ignores: - - ssh_key +# +# Currently don't need to generate data sources as they use the same schema as resources +# data_sources: +# server: +# read: +# path: /servers/{server_id} +# method: GET +# schema: +# attributes: +# aliases: +# server_id: id +# ignores: +# - ipv6 +# - licenses +# - links +# - options +# - server +# - ssh_keys +# - vpc_id +# - user_data +# - port_blocking +# ssh_key: +# read: +# path: /account/keys/{key_id} +# method: GET +# schema: +# attributes: +# aliases: +# key_id: id +# ignores: +# - ssh_key