diff --git a/.copywrite.hcl b/.copywrite.hcl index bdf3892..7be7d86 100644 --- a/.copywrite.hcl +++ b/.copywrite.hcl @@ -1,9 +1,8 @@ -# NOTE: This file is for HashiCorp specific licensing automation and can be deleted after creating a new repo with this template. schema_version = 1 project { license = "MPL-2.0" - copyright_year = 2021 + copyright_year = 2024 header_ignore = [ # examples used within documentation (prose) diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 026c42c..0000000 --- a/examples/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Examples - -This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI. - -The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. - -* **provider/provider.tf** example file for the provider index page -* **data-sources/`full data source name`/data-source.tf** example file for the named data source page -* **resources/`full resource name`/resource.tf** example file for the named data source page diff --git a/examples/data-sources/scaffolding_example/data-source.tf b/examples/data-sources/scaffolding_example/data-source.tf deleted file mode 100644 index a852489..0000000 --- a/examples/data-sources/scaffolding_example/data-source.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "scaffolding_example" "example" { - configurable_attribute = "some-value" -} diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf deleted file mode 100644 index 942db45..0000000 --- a/examples/provider/provider.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "scaffolding" { - # example configuration here -} diff --git a/examples/resources/scaffolding_example/resource.tf b/examples/resources/scaffolding_example/resource.tf deleted file mode 100644 index 9ae3f57..0000000 --- a/examples/resources/scaffolding_example/resource.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "scaffolding_example" "example" { - configurable_attribute = "some-value" -} diff --git a/go.mod b/go.mod index 10f01ec..4a5eeb6 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,12 @@ -module github.com/hashicorp/terraform-provider-scaffolding-framework +module terraform-provider-spade go 1.21 require ( - github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.19.2 github.com/hashicorp/terraform-plugin-framework v1.8.0 - github.com/hashicorp/terraform-plugin-go v0.22.2 + github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-testing v1.7.0 ) require ( @@ -18,7 +16,6 @@ require ( github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect - github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect @@ -26,23 +23,20 @@ require ( github.com/cloudflare/circl v1.3.7 // indirect github.com/fatih/color v1.16.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/cli v1.1.6 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.6.4 // indirect - github.com/hashicorp/hcl/v2 v2.20.0 // indirect - github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect - github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.22.2 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect @@ -53,14 +47,11 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v1.0.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.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 github.com/yuin/goldmark v1.7.1 // indirect @@ -73,11 +64,10 @@ require ( golang.org/x/net v0.23.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2579848..83798ce 100644 --- a/go.sum +++ b/go.sum @@ -14,9 +14,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/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/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= @@ -47,17 +44,10 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= -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-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -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/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -73,8 +63,6 @@ github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuD github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= -github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= @@ -89,10 +77,6 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0= github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA= -github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4= -github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk= -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.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= @@ -101,14 +85,12 @@ github.com/hashicorp/terraform-plugin-docs v0.19.2 h1:YjdKa1vuqt9EnPYkkrv9HnGZz1 github.com/hashicorp/terraform-plugin-docs v0.19.2/go.mod h1:gad2aP6uObFKhgNE8DR9nsEuEQnibp7il0jZYYOunWY= github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer5AEvJnDuXomBgou6fD8kI= github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY= +github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0 h1:b8vZYB/SkXJT4YPbT3trzE6oJ7dPyMy68+9dEDKsJjE= +github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0/go.mod h1:tP9BC3icoXBz72evMS5UTFvi98CiKhPdXF6yLs1wS8A= github.com/hashicorp/terraform-plugin-go v0.22.2 h1:5o8uveu6eZUf5J7xGPV0eY0TPXg3qpmwX9sce03Bxnc= github.com/hashicorp/terraform-plugin-go v0.22.2/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= -github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= -github.com/hashicorp/terraform-plugin-testing v1.7.0/go.mod h1:sbAreCleJNOCz+y5vVHV8EJkIWZKi/t4ndKiUjM9vao= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -126,15 +108,10 @@ github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgf github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.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= @@ -151,10 +128,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -185,9 +158,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= -github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 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= @@ -213,18 +183,14 @@ golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N0 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -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.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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= @@ -244,10 +210,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= @@ -257,16 +221,10 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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/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-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/provider/example_data_source_test.go b/internal/provider/example_data_source_test.go deleted file mode 100644 index 6f9aa7d..0000000 --- a/internal/provider/example_data_source_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleDataSource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Read testing - { - Config: testAccExampleDataSourceConfig, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("data.scaffolding_example.test", "id", "example-id"), - ), - }, - }, - }) -} - -const testAccExampleDataSourceConfig = ` -data "scaffolding_example" "test" { - configurable_attribute = "example" -} -` diff --git a/internal/provider/example_function_test.go b/internal/provider/example_function_test.go deleted file mode 100644 index d04f99a..0000000 --- a/internal/provider/example_function_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "regexp" - "testing" - - "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/tfversion" -) - -func TestExampleFunction_Known(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0"))), - }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: ` - output "test" { - value = provider::scaffolding::example("testvalue") - } - `, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckOutput("test", "testvalue"), - ), - }, - }, - }) -} - -func TestExampleFunction_Null(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0"))), - }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: ` - output "test" { - value = provider::scaffolding::example(null) - } - `, - // The parameter does not enable AllowNullValue - ExpectError: regexp.MustCompile(`argument must not be null`), - }, - }, - }) -} - -func TestExampleFunction_Unknown(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0"))), - }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: ` - resource "terraform_data" "test" { - input = "testvalue" - } - - output "test" { - value = provider::scaffolding::example(terraform_data.test.output) - } - `, - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckOutput("test", "testvalue"), - ), - }, - }, - }) -} diff --git a/internal/provider/example_resource.go b/internal/provider/example_resource.go deleted file mode 100644 index 70e961a..0000000 --- a/internal/provider/example_resource.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "context" - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" -) - -// Ensure provider defined types fully satisfy framework interfaces. -var _ resource.Resource = &ExampleResource{} -var _ resource.ResourceWithImportState = &ExampleResource{} - -func NewExampleResource() resource.Resource { - return &ExampleResource{} -} - -// ExampleResource defines the resource implementation. -type ExampleResource struct { - client *http.Client -} - -// ExampleResourceModel describes the resource data model. -type ExampleResourceModel struct { - ConfigurableAttribute types.String `tfsdk:"configurable_attribute"` - Defaulted types.String `tfsdk:"defaulted"` - Id types.String `tfsdk:"id"` -} - -func (r *ExampleResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_example" -} - -func (r *ExampleResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - // This description is used by the documentation generator and the language server. - MarkdownDescription: "Example resource", - - Attributes: map[string]schema.Attribute{ - "configurable_attribute": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute", - Optional: true, - }, - "defaulted": schema.StringAttribute{ - MarkdownDescription: "Example configurable attribute with default value", - Optional: true, - Computed: true, - Default: stringdefault.StaticString("example value when not configured"), - }, - "id": schema.StringAttribute{ - Computed: true, - MarkdownDescription: "Example identifier", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - }, - } -} - -func (r *ExampleResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(*http.Client) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Resource Configure Type", - fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - r.client = client -} - -func (r *ExampleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data ExampleResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create example, got error: %s", err)) - // return - // } - - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. - data.Id = types.StringValue("example-id") - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log - tflog.Trace(ctx, "created a resource") - - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var data ExampleResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err)) - // return - // } - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var data ExampleResourceModel - - // Read Terraform plan data into the model - resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update example, got error: %s", err)) - // return - // } - - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func (r *ExampleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var data ExampleResourceModel - - // Read Terraform prior state data into the model - resp.Diagnostics.Append(req.State.Get(ctx, &data)...) - - if resp.Diagnostics.HasError() { - return - } - - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := r.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete example, got error: %s", err)) - // return - // } -} - -func (r *ExampleResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) -} diff --git a/internal/provider/example_resource_test.go b/internal/provider/example_resource_test.go deleted file mode 100644 index c5464d0..0000000 --- a/internal/provider/example_resource_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" -) - -func TestAccExampleResource(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Create and Read testing - { - Config: testAccExampleResourceConfig("one"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("scaffolding_example.test", "configurable_attribute", "one"), - resource.TestCheckResourceAttr("scaffolding_example.test", "defaulted", "example value when not configured"), - resource.TestCheckResourceAttr("scaffolding_example.test", "id", "example-id"), - ), - }, - // ImportState testing - { - ResourceName: "scaffolding_example.test", - ImportState: true, - ImportStateVerify: true, - // This is not normally necessary, but is here because this - // example code does not have an actual upstream service. - // Once the Read method is able to refresh information from - // the upstream service, this can be removed. - ImportStateVerifyIgnore: []string{"configurable_attribute", "defaulted"}, - }, - // Update and Read testing - { - Config: testAccExampleResourceConfig("two"), - Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("scaffolding_example.test", "configurable_attribute", "two"), - ), - }, - // Delete testing automatically occurs in TestCase - }, - }) -} - -func testAccExampleResourceConfig(configurableAttribute string) string { - return fmt.Sprintf(` -resource "scaffolding_example" "test" { - configurable_attribute = %[1]q -} -`, configurableAttribute) -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2471df6..37b7148 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,6 +5,7 @@ package provider import ( "context" + "fmt" "net/http" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -13,43 +14,55 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" ) -// Ensure ScaffoldingProvider satisfies various provider interfaces. -var _ provider.Provider = &ScaffoldingProvider{} -var _ provider.ProviderWithFunctions = &ScaffoldingProvider{} +// Ensure SpadeProvider satisfies various provider interfaces. +var _ provider.Provider = &SpadeProvider{} +var _ provider.ProviderWithFunctions = &SpadeProvider{} -// ScaffoldingProvider defines the provider implementation. -type ScaffoldingProvider struct { +// SpadeProvider defines the provider implementation. +type SpadeProvider struct { // version is set to the provider version on release, "dev" when the // provider is built and ran locally, and "test" when running acceptance // testing. version string } -// ScaffoldingProviderModel describes the provider data model. -type ScaffoldingProviderModel struct { - Endpoint types.String `tfsdk:"endpoint"` +// SpadeProviderModel describes the provider data model. +type SpadeProviderModel struct { + URL types.String `tfsdk:"url"` + Email types.String `tfsdk:"email"` + Password types.String `tfsdk:"password"` } -func (p *ScaffoldingProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { - resp.TypeName = "scaffolding" +func (p *SpadeProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "spade" resp.Version = p.version } -func (p *ScaffoldingProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { +func (p *SpadeProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "endpoint": schema.StringAttribute{ - MarkdownDescription: "Example provider attribute", - Optional: true, + "url": schema.StringAttribute{ + MarkdownDescription: "Spade URL", + Required: true, + }, + "email": schema.StringAttribute{ + MarkdownDescription: "Login email address", + Required: true, + }, + "password": schema.StringAttribute{ + MarkdownDescription: "Login password", + Required: true, + Sensitive: true, }, }, } } -func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var data ScaffoldingProviderModel +func (p *SpadeProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var data SpadeProviderModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) @@ -60,33 +73,42 @@ func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.Config // Configuration values are now available. // if data.Endpoint.IsNull() { /* ... */ } - // Example client configuration for data sources and resources - client := http.DefaultClient + // Example client configuration for data sources and resource + client := &SpadeClient{ + ApiUrl: data.URL.ValueString(), + HttpClient: http.DefaultClient, + } + err := client.Login(data.Email.ValueString(), data.Password.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Login Error", fmt.Sprintf("Login failed: %s", err)) + return + } + tflog.Info(ctx, "Logged in to Spade") resp.DataSourceData = client resp.ResourceData = client } -func (p *ScaffoldingProvider) Resources(ctx context.Context) []func() resource.Resource { +func (p *SpadeProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ - NewExampleResource, + NewSpadeExecutorResource, + NewSpadeFileProcessorResource, + NewSpadeFileFormatResource, + NewSpadeProcessResource, + NewSpadeFileResource, } } -func (p *ScaffoldingProvider) DataSources(ctx context.Context) []func() datasource.DataSource { - return []func() datasource.DataSource{ - NewExampleDataSource, - } +func (p *SpadeProvider) DataSources(ctx context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{} } -func (p *ScaffoldingProvider) Functions(ctx context.Context) []func() function.Function { - return []func() function.Function{ - NewExampleFunction, - } +func (p *SpadeProvider) Functions(ctx context.Context) []func() function.Function { + return []func() function.Function{} } func New(version string) func() provider.Provider { return func() provider.Provider { - return &ScaffoldingProvider{ + return &SpadeProvider{ version: version, } } diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go deleted file mode 100644 index ef6599b..0000000 --- a/internal/provider/provider_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package provider - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/hashicorp/terraform-plugin-go/tfprotov6" -) - -// testAccProtoV6ProviderFactories are used to instantiate a provider during -// acceptance testing. The factory function will be invoked for every Terraform -// CLI command executed to create a provider server to which the CLI can -// reattach. -var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "scaffolding": providerserver.NewProtocol6WithError(New("test")()), -} - -func testAccPreCheck(t *testing.T) { - // You can add code here to run prior to any test case execution, for example assertions - // about the appropriate environment variables being set are common to see in a pre-check - // function. -} diff --git a/internal/provider/spade_client.go b/internal/provider/spade_client.go new file mode 100644 index 0000000..751f641 --- /dev/null +++ b/internal/provider/spade_client.go @@ -0,0 +1,705 @@ +package provider + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" +) + +type SpadeClient struct { + ApiUrl string + HttpClient *http.Client + Token string +} + +type SpadeLoginRequest struct { + Email string `json:"email"` + Password string `json:"password"` +} + +type SpadeLoginResponse struct { + AccessToken string `json:"access"` + RefreshToken string `json:"refresh"` +} + +func (c *SpadeClient) Login(email, password string) error { + httpReqBody, err := json.Marshal(SpadeLoginRequest{ + Email: email, + Password: password, + }) + if err != nil { + return err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/token", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + resp := SpadeLoginResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return err + } + c.Token = resp.AccessToken + return nil +} + +type SpadeFileProcessorCreateRequest struct { + Name string `json:"name"` + Description string `json:"description"` + Callable string `json:"callable"` +} + +type SpadeFileProcessorReadResponse struct { + Id int64 `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Callable string `json:"callable"` +} + +func (c *SpadeClient) CreateFileProcessor(name, description, callable string) (*SpadeFileProcessorReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileProcessorCreateRequest{ + Name: name, + Description: description, + Callable: callable, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/fileprocessors", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileProcessorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) ReadFileProcessor(id int64) (*SpadeFileProcessorReadResponse, error) { + httpReq, err := http.NewRequest( + "GET", + c.ApiUrl+"/api/v1/fileprocessors/"+fmt.Sprint(id), + nil, + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileProcessorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) UpdateFileProcessor(id int64, name, description, callable string) (*SpadeFileProcessorReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileProcessorCreateRequest{ + Name: name, + Description: description, + Callable: callable, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "PATCH", + c.ApiUrl+"/api/v1/fileprocessors/"+fmt.Sprint(id), + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileProcessorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) DeleteFileProcessor(id int64) error { + url, err := url.Parse(c.ApiUrl + "/api/v1/fileprocessors/" + fmt.Sprint(id)) + if err != nil { + return err + } + httpReq := &http.Request{ + Method: "DELETE", + URL: url, + Header: map[string][]string{ + "Authorization": {"Bearer " + c.Token}, + "Content-Type": {"application/json"}, + }, + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + return nil +} + +type SpadeFileFormatCreateRequest struct { + Format string `json:"format"` +} + +type SpadeFileFormatReadResponse struct { + Id int64 `json:"id"` + Format string `json:"format"` +} + +func (c *SpadeClient) CreateFileFormat(format string) (*SpadeFileFormatReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileFormatCreateRequest{ + Format: format, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/fileformats", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileFormatReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) ReadFileFormat(id int64) (*SpadeFileFormatReadResponse, error) { + httpReq, err := http.NewRequest( + "GET", + c.ApiUrl+"/api/v1/fileformats/"+fmt.Sprint(id), + nil, + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileFormatReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) UpdateFileFormat(id int64, format string) (*SpadeFileFormatReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileFormatCreateRequest{ + Format: format, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "PATCH", + c.ApiUrl+"/api/v1/fileformats/"+fmt.Sprint(id), + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileFormatReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) DeleteFileFormat(id int64) error { + url, err := url.Parse(c.ApiUrl + "/api/v1/fileformats/" + fmt.Sprint(id)) + if err != nil { + return err + } + httpReq := &http.Request{ + Method: "DELETE", + URL: url, + Header: map[string][]string{ + "Authorization": {"Bearer " + c.Token}, + "Content-Type": {"application/json"}, + }, + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + return nil +} + +type SpadeExecutorCreateRequest struct { + Name string `json:"name"` + Description string `json:"description"` + Callable string `json:"callable"` + HistoryProviderCallable string `json:"history_provider_callable"` +} + +type SpadeExecutorReadResponse struct { + Id int64 `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Callable string `json:"callable"` + HistoryProviderCallable string `json:"history_provider_callable"` +} + +func (c *SpadeClient) CreateExecutor(name, description, callable, historyProviderCallable string) (*SpadeExecutorReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeExecutorCreateRequest{ + Name: name, + Description: description, + Callable: callable, + HistoryProviderCallable: historyProviderCallable, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/executors", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeExecutorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) ReadExecutor(id int64) (*SpadeExecutorReadResponse, error) { + httpReq, err := http.NewRequest( + "GET", + c.ApiUrl+"/api/v1/executors/"+fmt.Sprint(id), + nil, + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeExecutorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) UpdateExecutor(id int64, name, description, callable, historyProviderCallable string) (*SpadeExecutorReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeExecutorCreateRequest{ + Name: name, + Description: description, + Callable: callable, + HistoryProviderCallable: historyProviderCallable, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "PATCH", + c.ApiUrl+"/api/v1/executors/"+fmt.Sprint(id), + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeExecutorReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) DeleteExecutor(id int64) error { + url, err := url.Parse(c.ApiUrl + "/api/v1/executors/" + fmt.Sprint(id)) + if err != nil { + return err + } + httpReq := &http.Request{ + Method: "DELETE", + URL: url, + Header: map[string][]string{ + "Authorization": {"Bearer " + c.Token}, + "Content-Type": {"application/json"}, + }, + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + return nil +} + +type SpadeFileCreateRequest struct { + Code string `json:"code"` + Description string `json:"description"` + Tags []string `json:"tags"` + Format int64 `json:"format"` + Processor int64 `json:"processor"` + SystemParams map[string]interface{} `json:"system_params"` + UserParams map[string]interface{} `json:"user_params"` +} + +type SpadeFileReadResponse struct { + Id int64 `json:"id"` + Code string `json:"code"` + Description string `json:"description"` + Tags []string `json:"tags"` + Format int64 `json:"format"` + Processor int64 `json:"processor"` + SystemParams map[string]interface{} `json:"system_params"` + UserParams map[string]interface{} `json:"user_params"` +} + +func (c *SpadeClient) CreateFile(code, description string, tags []string, format, processor int64, systemParams, userParams map[string]interface{}) (*SpadeFileReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileCreateRequest{ + Code: code, + Description: description, + Tags: tags, + Format: format, + Processor: processor, + SystemParams: systemParams, + UserParams: userParams, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/files", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) ReadFile(id int64) (*SpadeFileReadResponse, error) { + httpReq, err := http.NewRequest( + "GET", + c.ApiUrl+"/api/v1/files/"+fmt.Sprint(id), + nil, + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) UpdateFile(id int64, code, description string, tags []string, format, processor int64, systemParams, userParams map[string]interface{}) (*SpadeFileReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeFileCreateRequest{ + Code: code, + Description: description, + Tags: tags, + Format: format, + Processor: processor, + SystemParams: systemParams, + UserParams: userParams, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "PATCH", + c.ApiUrl+"/api/v1/files/"+fmt.Sprint(id), + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeFileReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) DeleteFile(id int64) error { + url, err := url.Parse(c.ApiUrl + "/api/v1/files/" + fmt.Sprint(id)) + if err != nil { + return err + } + httpReq := &http.Request{ + Method: "DELETE", + URL: url, + Header: map[string][]string{ + "Authorization": {"Bearer " + c.Token}, + "Content-Type": {"application/json"}, + }, + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + return nil +} + +type SpadeProcessCreateRequest struct { + Code string `json:"code"` + Description string `json:"description"` + Tags []string `json:"tags"` + Executor int64 `json:"executor"` + SystemParams map[string]interface{} `json:"system_params"` + UserParams map[string]interface{} `json:"user_params"` +} + +type SpadeProcessReadResponse struct { + Id int64 `json:"id"` + Code string `json:"code"` + Description string `json:"description"` + Tags []string `json:"tags"` + Executor int64 `json:"executor"` + SystemParams map[string]interface{} `json:"system_params"` + UserParams map[string]interface{} `json:"user_params"` +} + +func (c *SpadeClient) CreateProcess(code, description string, tags []string, executor int64, systemParams, userParams map[string]interface{}) (*SpadeProcessReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeProcessCreateRequest{ + Code: code, + Description: description, + Tags: tags, + Executor: executor, + SystemParams: systemParams, + UserParams: userParams, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "POST", + c.ApiUrl+"/api/v1/processes", + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + resp := SpadeProcessReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) ReadProcess(id int64) (*SpadeProcessReadResponse, error) { + httpReq, err := http.NewRequest( + "GET", + c.ApiUrl+"/api/v1/processes/"+fmt.Sprint(id), + nil, + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + + resp := SpadeProcessReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) UpdateProcess(id int64, code, description string, tags []string, executor int64, systemParams, userParams map[string]interface{}) (*SpadeProcessReadResponse, error) { + httpReqBody, err := json.Marshal(SpadeProcessCreateRequest{ + Code: code, + Description: description, + Tags: tags, + Executor: executor, + SystemParams: systemParams, + UserParams: userParams, + }) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest( + "PATCH", + c.ApiUrl+"/api/v1/processes/"+fmt.Sprint(id), + bytes.NewBuffer(httpReqBody), + ) + httpReq.Header.Set("Authorization", "Bearer "+c.Token) + httpReq.Header.Set("Content-Type", "application/json") + if err != nil { + return nil, err + } + + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return nil, err + } + defer httpResp.Body.Close() + + resp := SpadeProcessReadResponse{} + err = json.NewDecoder(httpResp.Body).Decode(&resp) + if err != nil { + return nil, err + } + return &resp, nil +} + +func (c *SpadeClient) DeleteProcess(id int64) error { + url, err := url.Parse(c.ApiUrl + "/api/v1/processes/" + fmt.Sprint(id)) + if err != nil { + return err + } + httpReq := &http.Request{ + Method: "DELETE", + URL: url, + Header: map[string][]string{ + "Authorization": {"Bearer " + c.Token}, + "Content-Type": {"application/json"}, + }, + } + httpResp, err := c.HttpClient.Do(httpReq) + if err != nil { + return err + } + defer httpResp.Body.Close() + return nil +} diff --git a/internal/provider/spade_executor.go b/internal/provider/spade_executor.go new file mode 100644 index 0000000..ae319a0 --- /dev/null +++ b/internal/provider/spade_executor.go @@ -0,0 +1,213 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SpadeExecutorResource{} +var _ resource.ResourceWithImportState = &SpadeExecutorResource{} + +func NewSpadeExecutorResource() resource.Resource { + return &SpadeExecutorResource{} +} + +// SpadeExecutorResource defines the resource implementation. +type SpadeExecutorResource struct { + Client *SpadeClient +} + +// SpadeExecutorResourceModel describes the resource data model. +type SpadeExecutorResourceModel struct { + Id types.Int64 `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Callable types.String `tfsdk:"callable"` + HistoryProviderCallable types.String `tfsdk:"history_provider_callable"` +} + +func (r *SpadeExecutorResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_executor" +} + +func (r *SpadeExecutorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Spade executor", + + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the executor", + Required: true, + }, + "description": schema.StringAttribute{ + MarkdownDescription: "Description of the executor", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "callable": schema.StringAttribute{ + MarkdownDescription: "Python import path to the Executor class", + Required: true, + }, + "history_provider_callable": schema.StringAttribute{ + MarkdownDescription: "Python import path to the HistoryProvider class", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "id": schema.Int64Attribute{ + Computed: true, + MarkdownDescription: "Identifier of the executor", + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *SpadeExecutorResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*SpadeClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.Client = client +} + +func (r *SpadeExecutorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SpadeExecutorResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.CreateExecutor( + data.Name.ValueString(), + data.Description.ValueString(), + data.Callable.ValueString(), + data.HistoryProviderCallable.ValueString(), + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create executor, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + data.HistoryProviderCallable = types.StringValue(spadeResp.HistoryProviderCallable) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeExecutorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SpadeExecutorResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.ReadExecutor(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read executor, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + data.HistoryProviderCallable = types.StringValue(spadeResp.HistoryProviderCallable) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeExecutorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SpadeExecutorResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.UpdateExecutor( + data.Id.ValueInt64(), + data.Name.ValueString(), + data.Description.ValueString(), + data.Callable.ValueString(), + data.HistoryProviderCallable.ValueString(), + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update executor, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + data.HistoryProviderCallable = types.StringValue(spadeResp.HistoryProviderCallable) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeExecutorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SpadeExecutorResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.Client.DeleteExecutor(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete executor, got error: %s", err)) + return + } +} + +func (r *SpadeExecutorResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/spade_file.go b/internal/provider/spade_file.go new file mode 100644 index 0000000..b677a6b --- /dev/null +++ b/internal/provider/spade_file.go @@ -0,0 +1,341 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SpadeFileResource{} +var _ resource.ResourceWithImportState = &SpadeFileResource{} + +func NewSpadeFileResource() resource.Resource { + return &SpadeFileResource{} +} + +// SpadeFileResource defines the resource implementation. +type SpadeFileResource struct { + Client *SpadeClient +} + +// SpadeFileResourceModel describes the resource data model. +type SpadeFileResourceModel struct { + Id types.Int64 `tfsdk:"id"` + Code types.String `tfsdk:"code"` + Description types.String `tfsdk:"description"` + Tags types.List `tfsdk:"tags"` + Format types.Int64 `tfsdk:"format"` + Processor types.Int64 `tfsdk:"processor"` + SystemParams jsontypes.Normalized `tfsdk:"system_params"` + UserParams jsontypes.Normalized `tfsdk:"user_params"` +} + +func (r *SpadeFileResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_file" +} + +func (r *SpadeFileResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Spade file", + + Attributes: map[string]schema.Attribute{ + "code": schema.StringAttribute{ + MarkdownDescription: "Name of the processor", + Required: true, + }, + "description": schema.StringAttribute{ + MarkdownDescription: "Description of the processor", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "tags": schema.ListAttribute{ + MarkdownDescription: "Tags for the processor", + ElementType: types.StringType, + Optional: true, + Computed: true, + Default: listdefault.StaticValue(basetypes.NewListValueMust(types.StringType, []attr.Value{})), + }, + "format": schema.Int64Attribute{ + MarkdownDescription: "Identifier for file format", + Required: true, + }, + "processor": schema.Int64Attribute{ + MarkdownDescription: "Identifier for processor", + Required: true, + }, + "system_params": schema.StringAttribute{ + MarkdownDescription: "JSON of system parameters", + Optional: true, + Computed: true, + CustomType: jsontypes.NormalizedType{}, + Default: stringdefault.StaticString("{}"), + }, + "user_params": schema.StringAttribute{ + MarkdownDescription: "JSON of user parameters (JsonSchema form)", + Optional: true, + Computed: true, + CustomType: jsontypes.NormalizedType{}, + Default: stringdefault.StaticString("{}"), + }, + "id": schema.Int64Attribute{ + Computed: true, + MarkdownDescription: "Example identifier", + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *SpadeFileResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*SpadeClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.Client = client +} + +func (r *SpadeFileResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SpadeFileResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + var systemParamsJson map[string]interface{} + err := json.Unmarshal([]byte(data.SystemParams.ValueString()), &systemParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse system_params, got error: %s", err)) + return + } + var userParamsJson map[string]interface{} + err = json.Unmarshal([]byte(data.UserParams.ValueString()), &userParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse user_params, got error: %s", err)) + return + } + tags := data.Tags.Elements() + tagStrings := make([]string, len(tags)) + for i, tag := range tags { + str := tag.(types.String).ValueString() + tagStrings[i] = str + } + + spadeResp, err := r.Client.CreateFile( + data.Code.ValueString(), + data.Description.ValueString(), + tagStrings, + data.Format.ValueInt64(), + data.Processor.ValueInt64(), + systemParamsJson, + userParamsJson, + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create file, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError("Client Error", "fuck1") + return + } + data.Tags = respTags + data.Format = types.Int64Value(spadeResp.Format) + data.Processor = types.Int64Value(spadeResp.Processor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SpadeFileResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.ReadFile(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read file, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + return + } + data.Tags = respTags + data.Format = types.Int64Value(spadeResp.Format) + data.Processor = types.Int64Value(spadeResp.Processor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SpadeFileResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + var systemParamsJson map[string]interface{} + err := json.Unmarshal([]byte(data.SystemParams.ValueString()), &systemParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse system_params, got error: %s", err)) + return + } + var userParamsJson map[string]interface{} + err = json.Unmarshal([]byte(data.UserParams.ValueString()), &userParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse user_params, got error: %s", err)) + return + } + tags := data.Tags.Elements() + tagStrings := make([]string, len(tags)) + for i, tag := range tags { + str := tag.(types.String).ValueString() + tagStrings[i] = str + } + + spadeResp, err := r.Client.UpdateFile( + data.Id.ValueInt64(), + data.Code.ValueString(), + data.Description.ValueString(), + tagStrings, + data.Format.ValueInt64(), + data.Processor.ValueInt64(), + systemParamsJson, + userParamsJson, + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update file, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError("Client Error", "fuck1") + return + } + data.Tags = respTags + data.Format = types.Int64Value(spadeResp.Format) + data.Processor = types.Int64Value(spadeResp.Processor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SpadeFileResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.Client.DeleteFile(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete file, got error: %s", err)) + return + } +} + +func (r *SpadeFileResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/spade_file_format.go b/internal/provider/spade_file_format.go new file mode 100644 index 0000000..05810c9 --- /dev/null +++ b/internal/provider/spade_file_format.go @@ -0,0 +1,177 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SpadeFileFormatResource{} +var _ resource.ResourceWithImportState = &SpadeFileFormatResource{} + +func NewSpadeFileFormatResource() resource.Resource { + return &SpadeFileFormatResource{} +} + +// SpadeFileFormatResource defines the resource implementation. +type SpadeFileFormatResource struct { + Client *SpadeClient +} + +// SpadeFileFormatResourceModel describes the resource data model. +type SpadeFileFormatResourceModel struct { + Id types.Int64 `tfsdk:"id"` + Format types.String `tfsdk:"format"` +} + +func (r *SpadeFileFormatResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_file_format" +} + +func (r *SpadeFileFormatResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Spade file format", + + Attributes: map[string]schema.Attribute{ + "format": schema.StringAttribute{ + MarkdownDescription: "File format name", + Required: true, + }, + "id": schema.Int64Attribute{ + Computed: true, + MarkdownDescription: "Example identifier", + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *SpadeFileFormatResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*SpadeClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.Client = client +} + +func (r *SpadeFileFormatResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SpadeFileFormatResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.CreateFileFormat(data.Format.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create file format, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Format = types.StringValue(spadeResp.Format) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileFormatResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SpadeFileFormatResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.ReadFileFormat(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read file format, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Format = types.StringValue(spadeResp.Format) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileFormatResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SpadeFileFormatResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.UpdateFileFormat( + data.Id.ValueInt64(), + data.Format.ValueString(), + ) + + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update file format, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Format = types.StringValue(spadeResp.Format) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileFormatResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SpadeFileFormatResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.Client.DeleteFileFormat(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete file format, got error: %s", err)) + return + } +} + +func (r *SpadeFileFormatResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/spade_file_processor.go b/internal/provider/spade_file_processor.go new file mode 100644 index 0000000..6571882 --- /dev/null +++ b/internal/provider/spade_file_processor.go @@ -0,0 +1,201 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SpadeFileProcessorResource{} +var _ resource.ResourceWithImportState = &SpadeFileProcessorResource{} + +func NewSpadeFileProcessorResource() resource.Resource { + return &SpadeFileProcessorResource{} +} + +// SpadeFileProcessorResource defines the resource implementation. +type SpadeFileProcessorResource struct { + Client *SpadeClient +} + +// SpadeFileProcessorResourceModel describes the resource data model. +type SpadeFileProcessorResourceModel struct { + Id types.Int64 `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Callable types.String `tfsdk:"callable"` +} + +func (r *SpadeFileProcessorResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_file_processor" +} + +func (r *SpadeFileProcessorResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Spade file processor", + + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the file processor", + Required: true, + }, + "description": schema.StringAttribute{ + MarkdownDescription: "Description of the file processor", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "callable": schema.StringAttribute{ + MarkdownDescription: "Python import path to the FileProcessor class", + Required: true, + }, + "id": schema.Int64Attribute{ + Computed: true, + MarkdownDescription: "Example identifier", + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *SpadeFileProcessorResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*SpadeClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.Client = client +} + +func (r *SpadeFileProcessorResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SpadeFileProcessorResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.CreateFileProcessor( + data.Name.ValueString(), + data.Description.ValueString(), + data.Callable.ValueString(), + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create file processor, got error: %s", err)) + return + } + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileProcessorResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SpadeFileProcessorResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.ReadFileProcessor(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read file processor, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileProcessorResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SpadeFileProcessorResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.UpdateFileProcessor( + data.Id.ValueInt64(), + data.Name.ValueString(), + data.Description.ValueString(), + data.Callable.ValueString(), + ) + + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update file processor, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Name = types.StringValue(spadeResp.Name) + data.Description = types.StringValue(spadeResp.Description) + data.Callable = types.StringValue(spadeResp.Callable) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeFileProcessorResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SpadeFileProcessorResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.Client.DeleteFileProcessor(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete file processor, got error: %s", err)) + return + } +} + +func (r *SpadeFileProcessorResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/internal/provider/spade_process.go b/internal/provider/spade_process.go new file mode 100644 index 0000000..fcacc3b --- /dev/null +++ b/internal/provider/spade_process.go @@ -0,0 +1,331 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" +) + +// Ensure provider defined types fully satisfy framework interfaces. +var _ resource.Resource = &SpadeProcessResource{} +var _ resource.ResourceWithImportState = &SpadeProcessResource{} + +func NewSpadeProcessResource() resource.Resource { + return &SpadeProcessResource{} +} + +// SpadeProcessResource defines the resource implementation. +type SpadeProcessResource struct { + Client *SpadeClient +} + +// SpadeProcessResourceModel describes the resource data model. +type SpadeProcessResourceModel struct { + Id types.Int64 `tfsdk:"id"` + Code types.String `tfsdk:"code"` + Description types.String `tfsdk:"description"` + Tags types.List `tfsdk:"tags"` + Executor types.Int64 `tfsdk:"executor"` + SystemParams jsontypes.Normalized `tfsdk:"system_params"` + UserParams jsontypes.Normalized `tfsdk:"user_params"` +} + +func (r *SpadeProcessResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_process" +} + +func (r *SpadeProcessResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Spade executor", + + Attributes: map[string]schema.Attribute{ + "code": schema.StringAttribute{ + MarkdownDescription: "Name of the executor", + Required: true, + }, + "description": schema.StringAttribute{ + MarkdownDescription: "Description of the executor", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "tags": schema.ListAttribute{ + MarkdownDescription: "Tags for the executor", + ElementType: types.StringType, + Optional: true, + Computed: true, + Default: listdefault.StaticValue(basetypes.NewListValueMust(types.StringType, []attr.Value{})), + }, + "executor": schema.Int64Attribute{ + MarkdownDescription: "Identifier for executor", + Required: true, + }, + "system_params": schema.StringAttribute{ + MarkdownDescription: "JSON of system parameters", + Optional: true, + Computed: true, + CustomType: jsontypes.NormalizedType{}, + Default: stringdefault.StaticString("{}"), + }, + "user_params": schema.StringAttribute{ + MarkdownDescription: "JSON of user parameters (JsonSchema form)", + Optional: true, + Computed: true, + CustomType: jsontypes.NormalizedType{}, + Default: stringdefault.StaticString("{}"), + }, + "id": schema.Int64Attribute{ + Computed: true, + MarkdownDescription: "Example identifier", + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +func (r *SpadeProcessResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*SpadeClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.Client = client +} + +func (r *SpadeProcessResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data SpadeProcessResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + var systemParamsJson map[string]interface{} + err := json.Unmarshal([]byte(data.SystemParams.ValueString()), &systemParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse system_params, got error: %s", err)) + return + } + var userParamsJson map[string]interface{} + err = json.Unmarshal([]byte(data.UserParams.ValueString()), &userParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse user_params, got error: %s", err)) + return + } + tags := data.Tags.Elements() + tagStrings := make([]string, len(tags)) + for i, tag := range tags { + str := tag.(types.String).ValueString() + tagStrings[i] = str + } + + spadeResp, err := r.Client.CreateProcess( + data.Code.ValueString(), + data.Description.ValueString(), + tagStrings, + data.Executor.ValueInt64(), + systemParamsJson, + userParamsJson, + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create process, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError("Client Error", "fuck1") + return + } + data.Tags = respTags + data.Executor = types.Int64Value(spadeResp.Executor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeProcessResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data SpadeProcessResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spadeResp, err := r.Client.ReadProcess(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read process, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + return + } + data.Tags = respTags + data.Executor = types.Int64Value(spadeResp.Executor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeProcessResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data SpadeProcessResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + var systemParamsJson map[string]interface{} + err := json.Unmarshal([]byte(data.SystemParams.ValueString()), &systemParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse system_params, got error: %s", err)) + return + } + var userParamsJson map[string]interface{} + err = json.Unmarshal([]byte(data.UserParams.ValueString()), &userParamsJson) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse user_params, got error: %s", err)) + return + } + tags := data.Tags.Elements() + tagStrings := make([]string, len(tags)) + for i, tag := range tags { + str := tag.(types.String).ValueString() + tagStrings[i] = str + } + + spadeResp, err := r.Client.UpdateProcess( + data.Id.ValueInt64(), + data.Code.ValueString(), + data.Description.ValueString(), + tagStrings, + data.Executor.ValueInt64(), + systemParamsJson, + userParamsJson, + ) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update process, got error: %s", err)) + return + } + + // Update the model with the response data + data.Id = types.Int64Value(spadeResp.Id) + data.Code = types.StringValue(spadeResp.Code) + data.Description = types.StringValue(spadeResp.Description) + respTags, diag := basetypes.NewListValueFrom(ctx, types.StringType, spadeResp.Tags) + resp.Diagnostics.Append(diag...) + if resp.Diagnostics.HasError() { + resp.Diagnostics.AddError("Client Error", "fuck1") + return + } + data.Tags = respTags + data.Executor = types.Int64Value(spadeResp.Executor) + respSystemParams, err := json.Marshal(spadeResp.SystemParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal system_params, got error: %s", err)) + return + } + respUserParams, err := json.Marshal(spadeResp.UserParams) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to marshal user_params, got error: %s", err)) + return + } + data.SystemParams = jsontypes.NewNormalizedValue(string(respSystemParams)) + data.UserParams = jsontypes.NewNormalizedValue(string(respUserParams)) + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *SpadeProcessResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data SpadeProcessResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.Client.DeleteProcess(data.Id.ValueInt64()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete process, got error: %s", err)) + return + } +} + +func (r *SpadeProcessResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} diff --git a/main.go b/main.go index a95e85c..6905d65 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,8 @@ import ( "log" "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/hashicorp/terraform-provider-scaffolding-framework/internal/provider" + + "terraform-provider-spade/internal/provider" ) // Run "go generate" to format example terraform files and generate the docs for the registry/website @@ -41,7 +42,7 @@ func main() { // TODO: Update this string with the published name of your provider. // Also update the tfplugindocs generate command to either remove the // -provider-name flag or set its value to the updated provider name. - Address: "registry.terraform.io/hashicorp/scaffolding", + Address: "registry.terraform.io/crugroup/spade", Debug: debug, } diff --git a/provider-test.tf b/provider-test.tf new file mode 100644 index 0000000..dadf2b1 --- /dev/null +++ b/provider-test.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + spade = { + source = "crugroup/spade" + } + } +} + +provider "spade" { + url = "https://spade-backend-dev.crugroup.com" + email = "spade@crugroup.com" + password = "spadespadespade" +} + +resource "spade_executor" "dummy_executor" { + name = "Dummy Executor2" + callable = "spadeapp.examples.executor.ExampleExecutor" +} + +resource "spade_file_processor" "dummy_processor" { + name = "Dummy Processor2" + callable = "spadeapp.examples.processor.ExampleFileProcessor" +} + +resource "spade_file_format" "dummy_format" { + format = "json2" +} + +resource "spade_process" "test_process" { + code = "test2" + executor = spade_executor.dummy_executor.id + description = "hello world" + tags = ["Steel"] + system_params = jsonencode({ + a = "b" + }) +} + +resource "spade_file" "test_file" { + code = "test2" + format = spade_file_format.dummy_format.id + processor = spade_file_processor.dummy_processor.id + description = "hello world" + tags = ["Steel"] + system_params = jsonencode({ + a = "b" + }) +}