allow creating entities when they dont exist
This commit is contained in:
parent
3eef94f685
commit
233be61d3e
9 changed files with 2187 additions and 53 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
CLAUDE.md
|
||||
terraform-provider-towerops
|
||||
dist/
|
||||
coverage.out
|
||||
|
|
|
|||
64
go.mod
64
go.mod
|
|
@ -1,30 +1,62 @@
|
|||
module github.com/towerops/terraform-provider-towerops
|
||||
|
||||
go 1.21
|
||||
|
||||
require github.com/hashicorp/terraform-plugin-framework v1.4.2
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/hashicorp/terraform-plugin-framework v1.17.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.29.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.14.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.2 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.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.5.0 // 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.7.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/terraform-plugin-go v0.19.1 // indirect
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.2 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.24.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.27.2 // indirect
|
||||
github.com/hashicorp/terraform-plugin-log v0.10.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // 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.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/oklog/run v1.1.0 // 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
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
|
||||
google.golang.org/grpc v1.60.1 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
github.com/zclconf/go-cty v1.17.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
)
|
||||
|
|
|
|||
234
go.sum
234
go.sum
|
|
@ -1,37 +1,114 @@
|
|||
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=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
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/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/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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.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/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
|
||||
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
|
||||
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
|
||||
github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
|
||||
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
||||
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.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0=
|
||||
github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM=
|
||||
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.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
|
||||
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.4.2 h1:P7a7VP1GZbjc4rv921Xy5OckzhoiO3ig6SGxwelD2sI=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.4.2/go.mod h1:GWl3InPFZi2wVQmdVnINPKys09s9mLmTZr95/ngLnbY=
|
||||
github.com/hashicorp/terraform-plugin-go v0.19.1 h1:lf/jTGTeELcz5IIbn/94mJdmnTjRYm6S6ct/JqCSr50=
|
||||
github.com/hashicorp/terraform-plugin-go v0.19.1/go.mod h1:5NMIS+DXkfacX6o5HCpswda5yjkSYfKzn1Nfl9l+qRs=
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24=
|
||||
github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
||||
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.24.0 h1:mL0xlk9H5g2bn0pPF6JQZk5YlByqSqrO5VoaNtAf8OE=
|
||||
github.com/hashicorp/terraform-exec v0.24.0/go.mod h1:lluc/rDYfAhYdslLJQg3J0oDqo88oGQAdHR+wDqFvo4=
|
||||
github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU=
|
||||
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.17.0 h1:JdX50CFrYcYFY31gkmitAEAzLKoBgsK+iaJjDC8OexY=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.17.0/go.mod h1:4OUXKdHNosX+ys6rLgVlgklfxN3WHR5VHSOABeS/BM0=
|
||||
github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU=
|
||||
github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM=
|
||||
github.com/hashicorp/terraform-plugin-log v0.10.0 h1:eu2kW6/QBVdN4P3Ju2WiB2W3ObjkAsyfBsL3Wh1fj3g=
|
||||
github.com/hashicorp/terraform-plugin-log v0.10.0/go.mod h1:/9RR5Cv2aAbrqcTSdNmY1NRHP4E3ekrXRGjqORpXyB0=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1 h1:mlAq/OrMlg04IuJT7NpefI1wwtdpWudnEmjuQs04t/4=
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1/go.mod h1:GQhpKVvvuwzD79e8/NZ+xzj+ZpWovdPAe8nfV/skwNU=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.14.0 h1:5t4VKrjOJ0rg0sVuSJ86dz5K7PHsMO6OKrHFzDBerWA=
|
||||
github.com/hashicorp/terraform-plugin-testing v1.14.0/go.mod h1:1qfWkecyYe1Do2EEOK/5/WnTyvC8wQucUkkhiGLg5nk=
|
||||
github.com/hashicorp/terraform-registry-address v0.4.0 h1:S1yCGomj30Sao4l5BMPjTGZmCNzuv7/GDTDX99E9gTk=
|
||||
github.com/hashicorp/terraform-registry-address v0.4.0/go.mod h1:LRS1Ay0+mAiRkUyltGT+UHWkIqTFvigGn/LbMshfflE=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
|
||||
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
|
||||
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
|
||||
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
|
@ -41,41 +118,124 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
|||
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/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
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.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
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.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
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/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
|
||||
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
|
||||
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.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/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-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@ package provider
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrNotFound is returned when a resource is not found (404).
|
||||
var ErrNotFound = errors.New("resource not found")
|
||||
|
||||
const defaultBaseURL = "https://towerops.net"
|
||||
|
||||
// Client is the TowerOps API client.
|
||||
|
|
@ -93,6 +97,9 @@ func (c *Client) doRequest(method, path string, body interface{}) ([]byte, error
|
|||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
var apiErr APIError
|
||||
if err := json.Unmarshal(respBody, &apiErr); err == nil {
|
||||
if apiErr.Error != "" {
|
||||
|
|
|
|||
573
internal/provider/client_test.go
Normal file
573
internal/provider/client_test.go
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClient_ErrNotFound(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "not found"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetDevice("nonexistent-id")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("expected ErrNotFound, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_GetDevice_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
t.Errorf("expected GET, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/devices/device-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
if r.Header.Get("Authorization") != "Bearer test-token" {
|
||||
t.Errorf("unexpected auth header: %s", r.Header.Get("Authorization"))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{
|
||||
"id": "device-123",
|
||||
"site_id": "site-456",
|
||||
"ip_address": "192.168.1.1",
|
||||
"name": "Test Device",
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device, err := client.GetDevice("device-123")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if device.ID != "device-123" {
|
||||
t.Errorf("expected ID device-123, got %s", device.ID)
|
||||
}
|
||||
if device.SiteID != "site-456" {
|
||||
t.Errorf("expected SiteID site-456, got %s", device.SiteID)
|
||||
}
|
||||
if device.IPAddress != "192.168.1.1" {
|
||||
t.Errorf("expected IPAddress 192.168.1.1, got %s", device.IPAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateDevice_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
t.Errorf("expected POST, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/devices" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(`{
|
||||
"id": "new-device-id",
|
||||
"site_id": "site-456",
|
||||
"ip_address": "192.168.1.100",
|
||||
"name": "New Device",
|
||||
"monitoring_enabled": true,
|
||||
"snmp_enabled": true,
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{
|
||||
SiteID: "site-456",
|
||||
IPAddress: "192.168.1.100",
|
||||
}
|
||||
|
||||
created, err := client.CreateDevice(device)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if created.ID != "new-device-id" {
|
||||
t.Errorf("expected ID new-device-id, got %s", created.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateDevice_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
t.Errorf("expected PATCH, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/devices/device-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{
|
||||
"id": "device-123",
|
||||
"site_id": "site-456",
|
||||
"ip_address": "192.168.1.200",
|
||||
"name": "Updated Device",
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{
|
||||
SiteID: "site-456",
|
||||
IPAddress: "192.168.1.200",
|
||||
}
|
||||
|
||||
updated, err := client.UpdateDevice("device-123", device)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if updated.IPAddress != "192.168.1.200" {
|
||||
t.Errorf("expected IPAddress 192.168.1.200, got %s", updated.IPAddress)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateDevice_NotFound(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "device not found"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{
|
||||
SiteID: "site-456",
|
||||
IPAddress: "192.168.1.200",
|
||||
}
|
||||
|
||||
_, err := client.UpdateDevice("nonexistent", device)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("expected ErrNotFound, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DeleteDevice_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
t.Errorf("expected DELETE, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/devices/device-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
err := client.DeleteDevice("device-123")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_APIError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error": "invalid request"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetDevice("device-123")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
// Should not be ErrNotFound for 400 errors
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
t.Error("did not expect ErrNotFound for 400 error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_ValidationError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
w.Write([]byte(`{"errors": {"ip_address": "is invalid"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{
|
||||
SiteID: "site-456",
|
||||
IPAddress: "invalid",
|
||||
}
|
||||
|
||||
_, err := client.CreateDevice(device)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
t.Error("did not expect ErrNotFound for validation error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_GetSite_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
t.Errorf("expected GET, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/sites/site-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{
|
||||
"id": "site-123",
|
||||
"name": "Test Site",
|
||||
"location": "New York",
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site, err := client.GetSite("site-123")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if site.ID != "site-123" {
|
||||
t.Errorf("expected ID site-123, got %s", site.ID)
|
||||
}
|
||||
if site.Name != "Test Site" {
|
||||
t.Errorf("expected Name 'Test Site', got %s", site.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_GetSite_NotFound(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "site not found"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetSite("nonexistent")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("expected ErrNotFound, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClient_DefaultBaseURL(t *testing.T) {
|
||||
client := NewClient("test-token", "")
|
||||
if client.BaseURL != defaultBaseURL {
|
||||
t.Errorf("expected default base URL %s, got %s", defaultBaseURL, client.BaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClient_CustomBaseURL(t *testing.T) {
|
||||
client := NewClient("test-token", "https://custom.example.com")
|
||||
if client.BaseURL != "https://custom.example.com" {
|
||||
t.Errorf("expected custom base URL, got %s", client.BaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateSite_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
t.Errorf("expected POST, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/sites" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(`{
|
||||
"id": "new-site-id",
|
||||
"name": "New Site",
|
||||
"location": "Boston",
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{
|
||||
Name: "New Site",
|
||||
}
|
||||
location := "Boston"
|
||||
site.Location = &location
|
||||
|
||||
created, err := client.CreateSite(site)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if created.ID != "new-site-id" {
|
||||
t.Errorf("expected ID new-site-id, got %s", created.ID)
|
||||
}
|
||||
if created.Name != "New Site" {
|
||||
t.Errorf("expected Name 'New Site', got %s", created.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateSite_Error(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error": "name is required"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{}
|
||||
_, err := client.CreateSite(site)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateSite_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{Name: "Test"}
|
||||
_, err := client.CreateSite(site)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateSite_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
t.Errorf("expected PATCH, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/sites/site-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{
|
||||
"id": "site-123",
|
||||
"name": "Updated Site",
|
||||
"location": "Chicago",
|
||||
"inserted_at": "2024-01-01T00:00:00Z"
|
||||
}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{
|
||||
Name: "Updated Site",
|
||||
}
|
||||
|
||||
updated, err := client.UpdateSite("site-123", site)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if updated.Name != "Updated Site" {
|
||||
t.Errorf("expected Name 'Updated Site', got %s", updated.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateSite_NotFound(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "site not found"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{Name: "Test"}
|
||||
_, err := client.UpdateSite("nonexistent", site)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("expected ErrNotFound, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateSite_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
site := Site{Name: "Test"}
|
||||
_, err := client.UpdateSite("site-123", site)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DeleteSite_Success(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
t.Errorf("expected DELETE, got %s", r.Method)
|
||||
}
|
||||
if r.URL.Path != "/api/v1/sites/site-123" {
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
err := client.DeleteSite("site-123")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DeleteSite_NotFound(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "site not found"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
err := client.DeleteSite("nonexistent")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("expected ErrNotFound, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_GetSite_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetSite("site-123")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_GetDevice_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetDevice("device-123")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_CreateDevice_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{SiteID: "site-123", IPAddress: "192.168.1.1"}
|
||||
_, err := client.CreateDevice(device)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_UpdateDevice_InvalidJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`invalid json`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
device := Device{SiteID: "site-123", IPAddress: "192.168.1.1"}
|
||||
_, err := client.UpdateDevice("device-123", device)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_ConnectionError(t *testing.T) {
|
||||
client := NewClient("test-token", "http://localhost:99999")
|
||||
|
||||
_, err := client.GetDevice("device-123")
|
||||
if err == nil {
|
||||
t.Fatal("expected connection error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_APIErrorWithoutJSON(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`Internal Server Error`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewClient("test-token", server.URL)
|
||||
|
||||
_, err := client.GetDevice("device-123")
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
t.Error("did not expect ErrNotFound for 500 error")
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
|
|
@ -205,6 +206,11 @@ func (r *DeviceResource) Read(ctx context.Context, req resource.ReadRequest, res
|
|||
|
||||
device, err := r.client.GetDevice(data.ID.ValueString())
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
// Device was deleted outside of Terraform, remove from state
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError("Failed to read device", err.Error())
|
||||
return
|
||||
}
|
||||
|
|
@ -289,6 +295,37 @@ func (r *DeviceResource) Update(ctx context.Context, req resource.UpdateRequest,
|
|||
|
||||
updated, err := r.client.UpdateDevice(data.ID.ValueString(), device)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
// Device was deleted outside of Terraform, recreate it
|
||||
created, createErr := r.client.CreateDevice(device)
|
||||
if createErr != nil {
|
||||
resp.Diagnostics.AddError("Failed to create device (after 404 on update)", createErr.Error())
|
||||
return
|
||||
}
|
||||
data.ID = types.StringValue(created.ID)
|
||||
data.InsertedAt = types.StringValue(created.InsertedAt)
|
||||
data.IPAddress = types.StringValue(created.IPAddress)
|
||||
if created.Name != nil {
|
||||
data.Name = types.StringValue(*created.Name)
|
||||
}
|
||||
if created.Description != nil {
|
||||
data.Description = types.StringValue(*created.Description)
|
||||
}
|
||||
if created.MonitoringEnabled != nil {
|
||||
data.MonitoringEnabled = types.BoolValue(*created.MonitoringEnabled)
|
||||
}
|
||||
if created.SNMPEnabled != nil {
|
||||
data.SNMPEnabled = types.BoolValue(*created.SNMPEnabled)
|
||||
}
|
||||
if created.SNMPVersion != nil {
|
||||
data.SNMPVersion = types.StringValue(*created.SNMPVersion)
|
||||
}
|
||||
if created.SNMPPort != nil {
|
||||
data.SNMPPort = types.Int64Value(int64(*created.SNMPPort))
|
||||
}
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError("Failed to update device", err.Error())
|
||||
return
|
||||
}
|
||||
|
|
|
|||
722
internal/provider/device_resource_test.go
Normal file
722
internal/provider/device_resource_test.go
Normal file
|
|
@ -0,0 +1,722 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDeviceResource_basic(t *testing.T) {
|
||||
var deviceID string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "test-device-id"
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "site_id", "site-123"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "192.168.1.1"),
|
||||
resource.TestCheckResourceAttrSet("towerops_device.test", "id"),
|
||||
resource.TestCheckResourceAttrSet("towerops_device.test", "inserted_at"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_withAllAttributes(t *testing.T) {
|
||||
var deviceID string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
description := "Test description"
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "test-device-id"
|
||||
name := "Full Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "10.0.0.1",
|
||||
Description: &description,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
name := "Full Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "10.0.0.1",
|
||||
Description: &description,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfigFull(server.URL),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "site_id", "site-123"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "name", "Full Device"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "10.0.0.1"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "description", "Test description"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "monitoring_enabled", "true"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "snmp_enabled", "true"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "snmp_version", "2c"),
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "snmp_port", "161"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_update(t *testing.T) {
|
||||
var deviceID string
|
||||
var currentIP string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "test-device-id"
|
||||
currentIP = "192.168.1.1"
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
var body map[string]Device
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
currentIP = body["device"].IPAddress
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "192.168.1.1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.2"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "192.168.1.2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_recreateOn404(t *testing.T) {
|
||||
var deviceID string
|
||||
var deviceDeleted bool
|
||||
var currentIP string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
var body map[string]Device
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
deviceID = "new-device-id"
|
||||
deviceDeleted = false
|
||||
currentIP = body["device"].IPAddress
|
||||
name := "Auto-discovered Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
if deviceDeleted {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "device not found"}`))
|
||||
return
|
||||
}
|
||||
name := "Auto-discovered Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
if deviceDeleted {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "device not found"}`))
|
||||
return
|
||||
}
|
||||
var body map[string]Device
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
currentIP = body["device"].IPAddress
|
||||
name := "Auto-discovered Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: currentIP,
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
deviceDeleted = true
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "192.168.1.1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
PreConfig: func() {
|
||||
mu.Lock()
|
||||
deviceDeleted = true
|
||||
mu.Unlock()
|
||||
},
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.2"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_device.test", "ip_address", "192.168.1.2"),
|
||||
resource.TestCheckResourceAttrSet("towerops_device.test", "id"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_importState(t *testing.T) {
|
||||
var deviceID string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "imported-device-id"
|
||||
name := "Imported Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/imported-device-id":
|
||||
name := "Imported Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: "imported-device-id",
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/imported-device-id":
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
},
|
||||
{
|
||||
ResourceName: "towerops_device.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDeviceResourceConfig(apiURL, siteID, ipAddress string) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = %q
|
||||
}
|
||||
|
||||
resource "towerops_device" "test" {
|
||||
site_id = %q
|
||||
ip_address = %q
|
||||
}
|
||||
`, apiURL, siteID, ipAddress)
|
||||
}
|
||||
|
||||
func testAccDeviceResourceConfigFull(apiURL string) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = %q
|
||||
}
|
||||
|
||||
resource "towerops_device" "test" {
|
||||
site_id = "site-123"
|
||||
name = "Full Device"
|
||||
ip_address = "10.0.0.1"
|
||||
description = "Test description"
|
||||
monitoring_enabled = true
|
||||
snmp_enabled = true
|
||||
snmp_version = "2c"
|
||||
snmp_port = 161
|
||||
}
|
||||
`, apiURL)
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_createError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error": "ip_address is required"}`))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "invalid"),
|
||||
ExpectError: regexp.MustCompile(`Failed to create device`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_updateError(t *testing.T) {
|
||||
var deviceID string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "test-device-id"
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error": "update failed"}`))
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
},
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.2"),
|
||||
ExpectError: regexp.MustCompile(`Failed to update device`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_deleteError(t *testing.T) {
|
||||
var deviceID string
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
deviceID = "test-device-id"
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error": "delete failed"}`))
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
Destroy: true,
|
||||
},
|
||||
},
|
||||
ErrorCheck: func(err error) error {
|
||||
if err != nil && regexp.MustCompile(`Failed to delete device`).MatchString(err.Error()) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDeviceResource_recreateOn404_createError(t *testing.T) {
|
||||
var deviceID string
|
||||
var deviceDeleted bool
|
||||
var createCount int
|
||||
var mu sync.Mutex
|
||||
monitoringEnabled := true
|
||||
snmpEnabled := true
|
||||
snmpVersion := "2c"
|
||||
snmpPort := 161
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/devices":
|
||||
createCount++
|
||||
if createCount > 1 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error": "create failed after 404"}`))
|
||||
return
|
||||
}
|
||||
deviceID = "test-device-id"
|
||||
deviceDeleted = false
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
if deviceDeleted {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "device not found"}`))
|
||||
return
|
||||
}
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.1",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
if deviceDeleted {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(`{"error": "device not found"}`))
|
||||
return
|
||||
}
|
||||
name := "Test Device"
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Device{
|
||||
ID: deviceID,
|
||||
SiteID: "site-123",
|
||||
Name: &name,
|
||||
IPAddress: "192.168.1.2",
|
||||
MonitoringEnabled: &monitoringEnabled,
|
||||
SNMPEnabled: &snmpEnabled,
|
||||
SNMPVersion: &snmpVersion,
|
||||
SNMPPort: &snmpPort,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/devices/"+deviceID:
|
||||
deviceDeleted = true
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.1"),
|
||||
},
|
||||
{
|
||||
PreConfig: func() {
|
||||
mu.Lock()
|
||||
deviceDeleted = true
|
||||
mu.Unlock()
|
||||
},
|
||||
Config: testAccDeviceResourceConfig(server.URL, "site-123", "192.168.1.2"),
|
||||
ExpectError: regexp.MustCompile(`Failed to create device \(after 404 on update\)`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
95
internal/provider/provider_test.go
Normal file
95
internal/provider/provider_test.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
||||
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func testAccProtoV6ProviderFactories(serverURL string) map[string]func() (tfprotov6.ProviderServer, error) {
|
||||
// Set the API URL for tests
|
||||
os.Setenv("TOWEROPS_TEST_API_URL", serverURL)
|
||||
|
||||
return map[string]func() (tfprotov6.ProviderServer, error){
|
||||
"towerops": providerserver.NewProtocol6WithError(New("test")()),
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_Schema(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccProviderConfig(server.URL),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvider_MissingToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(""),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: `
|
||||
provider "towerops" {
|
||||
api_url = "http://localhost"
|
||||
}
|
||||
|
||||
resource "towerops_site" "test" {
|
||||
name = "Test"
|
||||
}
|
||||
`,
|
||||
ExpectError: regexp.MustCompile(`Missing TowerOps API Token`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestProvider_EmptyToken(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(""),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: `
|
||||
provider "towerops" {
|
||||
token = ""
|
||||
api_url = "http://localhost"
|
||||
}
|
||||
|
||||
resource "towerops_site" "test" {
|
||||
name = "Test"
|
||||
}
|
||||
`,
|
||||
ExpectError: regexp.MustCompile(`Missing TowerOps API Token`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccProviderConfig(apiURL string) string {
|
||||
return `
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = "` + apiURL + `"
|
||||
}
|
||||
`
|
||||
}
|
||||
507
internal/provider/site_resource_test.go
Normal file
507
internal/provider/site_resource_test.go
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccSiteResource_basic(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Test Site"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "name", "Test Site"),
|
||||
resource.TestCheckResourceAttrSet("towerops_site.test", "id"),
|
||||
resource.TestCheckResourceAttrSet("towerops_site.test", "inserted_at"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_withLocation(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
location := "New York, NY"
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Site With Location",
|
||||
Location: &location,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Site With Location",
|
||||
Location: &location,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfigWithLocation(server.URL, "Site With Location", "New York, NY"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "name", "Site With Location"),
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "location", "New York, NY"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_update(t *testing.T) {
|
||||
var siteID string
|
||||
var currentName string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
currentName = "Original Name"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: currentName,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: currentName,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
var body map[string]Site
|
||||
json.NewDecoder(r.Body).Decode(&body)
|
||||
currentName = body["site"].Name
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: currentName,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Original Name"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "name", "Original Name"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Updated Name"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "name", "Updated Name"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_importState(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "imported-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Imported Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/imported-site-id":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: "imported-site-id",
|
||||
Name: "Imported Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/imported-site-id":
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Imported Site"),
|
||||
},
|
||||
{
|
||||
ResourceName: "towerops_site.test",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccSiteResourceConfig(apiURL, name string) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = %q
|
||||
}
|
||||
|
||||
resource "towerops_site" "test" {
|
||||
name = %q
|
||||
}
|
||||
`, apiURL, name)
|
||||
}
|
||||
|
||||
func testAccSiteResourceConfigWithLocation(apiURL, name, location string) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = %q
|
||||
}
|
||||
|
||||
resource "towerops_site" "test" {
|
||||
name = %q
|
||||
location = %q
|
||||
}
|
||||
`, apiURL, name, location)
|
||||
}
|
||||
|
||||
func TestAccSiteResource_createError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error": "name is required"}`))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, ""),
|
||||
ExpectError: regexp.MustCompile(`Failed to create site`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_readError(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
readCount := 0
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
readCount++
|
||||
if readCount > 1 {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error": "internal server error"}`))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Test Site"),
|
||||
ExpectError: regexp.MustCompile(`Failed to read site`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_updateError(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Original Name",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Original Name",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error": "update failed"}`))
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Original Name"),
|
||||
},
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Updated Name"),
|
||||
ExpectError: regexp.MustCompile(`Failed to update site`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_deleteError(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "Test Site",
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"error": "delete failed"}`))
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfig(server.URL, "Test Site"),
|
||||
Destroy: true,
|
||||
},
|
||||
},
|
||||
ErrorCheck: func(err error) error {
|
||||
if err != nil && regexp.MustCompile(`Failed to delete site`).MatchString(err.Error()) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccSiteResource_withSNMPCommunity(t *testing.T) {
|
||||
var siteID string
|
||||
var mu sync.Mutex
|
||||
community := "public"
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
switch {
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/v1/sites":
|
||||
siteID = "test-site-id"
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "SNMP Site",
|
||||
SNMPCommunity: &community,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(Site{
|
||||
ID: siteID,
|
||||
Name: "SNMP Site",
|
||||
SNMPCommunity: &community,
|
||||
InsertedAt: "2024-01-01T00:00:00Z",
|
||||
})
|
||||
|
||||
case r.Method == http.MethodDelete && r.URL.Path == "/api/v1/sites/"+siteID:
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories(server.URL),
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSiteResourceConfigWithSNMPCommunity(server.URL, "SNMP Site", "public"),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "name", "SNMP Site"),
|
||||
resource.TestCheckResourceAttr("towerops_site.test", "snmp_community", "public"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccSiteResourceConfigWithSNMPCommunity(apiURL, name, community string) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "towerops" {
|
||||
token = "test-token"
|
||||
api_url = %q
|
||||
}
|
||||
|
||||
resource "towerops_site" "test" {
|
||||
name = %q
|
||||
snmp_community = %q
|
||||
}
|
||||
`, apiURL, name, community)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue