now using client sdk

This commit is contained in:
Manuel Ganter 2025-11-11 15:25:57 +01:00
parent 51c743fb2b
commit a88de9f2e8
No known key found for this signature in database
11 changed files with 497 additions and 1261 deletions

21
go.mod
View file

@ -3,30 +3,47 @@ module github.com/DevFW-CICD/terraform-provider-edge-connect
go 1.25.3
require (
edp.buildth.ing/DevFW-CICD/edge-connect-client/v2 v2.0.2
github.com/hashicorp/terraform-plugin-framework v1.16.1
github.com/hashicorp/terraform-plugin-log v0.9.0
)
require (
github.com/fatih/color v1.15.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-plugin v1.7.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/terraform-plugin-go v0.29.0 // indirect
github.com/hashicorp/terraform-registry-address v0.4.0 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/cobra v1.10.1 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spf13/viper v1.21.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

59
go.sum
View file

@ -1,26 +1,39 @@
edp.buildth.ing/DevFW-CICD/edge-connect-client/v2 v2.0.2 h1:AcHIt5oOevf8NJPDRE/rpWHBWMStMWlq0A+jz7Wd5Oo=
edp.buildth.ing/DevFW-CICD/edge-connect-client/v2 v2.0.2/go.mod h1:nPZ4K4BB7eXyeSrcHXvSPkNZbs+XgmxbDJOM4KhbI1A=
github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw=
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-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-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA=
github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/terraform-plugin-framework v1.16.1 h1:1+zwFm3MEqd/0K3YBB2v9u9DtyYHyEuhVOfeIXbteWA=
@ -35,8 +48,14 @@ github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S
github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94=
github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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=
@ -44,19 +63,43 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
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=
@ -73,6 +116,8 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -95,5 +140,7 @@ google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -1,105 +0,0 @@
package client
import (
"encoding/json"
"fmt"
)
// CreateApp creates a new application
func (c *Client) CreateApp(region string, app App) (*App, error) {
req := AppRequest{
Region: region,
App: app,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/CreateApp", req)
if err != nil {
return nil, fmt.Errorf("failed to create app: %w", err)
}
var createdApp App
if err := json.Unmarshal(respBody, &createdApp); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &createdApp, nil
}
// GetApp retrieves an application by key
func (c *Client) GetApp(region string, app App) (*App, error) {
req := ShowAppRequest{
Region: region,
App: &app,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/ShowApp", req)
if err != nil {
return nil, fmt.Errorf("failed to get app: %w", err)
}
var apps []App
if err := json.Unmarshal(respBody, &apps); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
if len(apps) == 0 {
return nil, fmt.Errorf("app not found")
}
return &apps[0], nil
}
// ListApps lists all applications
func (c *Client) ListApps(region string, filter *App) ([]App, error) {
req := ShowAppRequest{
Region: region,
App: filter,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/ShowApp", req)
if err != nil {
return nil, fmt.Errorf("failed to list apps: %w", err)
}
var apps []App
if err := json.Unmarshal(respBody, &apps); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return apps, nil
}
// UpdateApp updates an existing application
func (c *Client) UpdateApp(region string, app App) (*App, error) {
req := AppRequest{
Region: region,
App: app,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/UpdateApp", req)
if err != nil {
return nil, fmt.Errorf("failed to update app: %w", err)
}
var updatedApp App
if err := json.Unmarshal(respBody, &updatedApp); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &updatedApp, nil
}
// DeleteApp deletes an application
func (c *Client) DeleteApp(region string, app App) error {
req := AppRequest{
Region: region,
App: app,
}
_, err := c.doRequest("POST", "/api/v1/auth/ctrl/DeleteApp", req)
if err != nil {
return fmt.Errorf("failed to delete app: %w", err)
}
return nil
}

View file

@ -1,114 +0,0 @@
package client
import (
"encoding/json"
"fmt"
)
// CreateAppInst creates a new application instance
func (c *Client) CreateAppInst(region string, appInst AppInst) (*AppInst, error) {
req := AppInstRequest{
Region: region,
AppInst: appInst,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/CreateAppInst", req)
if err != nil {
return nil, fmt.Errorf("failed to create app instance: %w", err)
}
// The API returns an array of messages, but we want the final state
// For now, we'll just return the input appInst as created
// A more sophisticated implementation would parse the messages
var messages []map[string]interface{}
if err := json.Unmarshal(respBody, &messages); err != nil {
// If it's not an array of messages, try to unmarshal as AppInst
var createdAppInst AppInst
if err := json.Unmarshal(respBody, &createdAppInst); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &createdAppInst, nil
}
// Return the input appInst (creation was successful)
return &appInst, nil
}
// GetAppInst retrieves an application instance by key
func (c *Client) GetAppInst(region string, appInst AppInst) (*AppInst, error) {
req := ShowAppInstRequest{
Region: region,
AppInst: &appInst,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/ShowAppInst", req)
if err != nil {
return nil, fmt.Errorf("failed to get app instance: %w", err)
}
var appInsts []AppInst
if err := json.Unmarshal(respBody, &appInsts); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
if len(appInsts) == 0 {
return nil, fmt.Errorf("app instance not found")
}
return &appInsts[0], nil
}
// ListAppInsts lists all application instances
func (c *Client) ListAppInsts(region string, filter *AppInst) ([]AppInst, error) {
req := ShowAppInstRequest{
Region: region,
AppInst: filter,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/ShowAppInst", req)
if err != nil {
return nil, fmt.Errorf("failed to list app instances: %w", err)
}
var appInsts []AppInst
if err := json.Unmarshal(respBody, &appInsts); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return appInsts, nil
}
// UpdateAppInst updates an existing application instance
func (c *Client) UpdateAppInst(region string, appInst AppInst) (*AppInst, error) {
req := AppInstRequest{
Region: region,
AppInst: appInst,
}
respBody, err := c.doRequest("POST", "/api/v1/auth/ctrl/UpdateAppInst", req)
if err != nil {
return nil, fmt.Errorf("failed to update app instance: %w", err)
}
var updatedAppInst AppInst
if err := json.Unmarshal(respBody, &updatedAppInst); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
return &updatedAppInst, nil
}
// DeleteAppInst deletes an application instance
func (c *Client) DeleteAppInst(region string, appInst AppInst) error {
req := AppInstRequest{
Region: region,
AppInst: appInst,
}
_, err := c.doRequest("POST", "/api/v1/auth/ctrl/DeleteAppInst", req)
if err != nil {
return fmt.Errorf("failed to delete app instance: %w", err)
}
return nil
}

View file

@ -1,81 +0,0 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client is the API client for Edge Connect
type Client struct {
BaseURL string
HTTPClient *http.Client
Token string
Username string
Password string
}
// NewClient creates a new Edge Connect API client
func NewClient(baseURL, token, username, password string) *Client {
return &Client{
BaseURL: baseURL,
HTTPClient: &http.Client{
Timeout: time.Second * 30,
},
Token: token,
Username: username,
Password: password,
}
}
// doRequest performs an HTTP request with authentication
func (c *Client) doRequest(method, path string, body interface{}) ([]byte, error) {
var reqBody io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewBuffer(jsonBody)
}
req, err := http.NewRequest(method, c.BaseURL+path, reqBody)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
// Add authentication
if c.Token != "" {
req.Header.Set("Authorization", "Bearer "+c.Token)
} else if c.Username != "" && c.Password != "" {
req.SetBasicAuth(c.Username, c.Password)
}
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
}
return respBody, nil
}
// HealthCheck performs a health check on the API
func (c *Client) HealthCheck() error {
_, err := c.doRequest("GET", "/api/v1/", nil)
return err
}

View file

@ -1,82 +0,0 @@
package client
// App represents an application specification
type App struct {
Key struct {
Organization string `json:"organization"`
Name string `json:"name"`
Version string `json:"version"`
} `json:"key"`
Region string `json:"region,omitempty"`
ImageType string `json:"image_type,omitempty"`
ImagePath string `json:"image_path,omitempty"`
DefaultFlavor string `json:"default_flavor,omitempty"`
Deployment string `json:"deployment,omitempty"`
DeploymentManifest string `json:"deployment_manifest,omitempty"`
AccessPorts string `json:"access_ports,omitempty"`
Annotations string `json:"annotations,omitempty"`
Configs []Config `json:"configs,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
DeletePrepare bool `json:"delete_prepare,omitempty"`
}
// Config represents a configuration item
type Config struct {
Kind string `json:"kind,omitempty"`
Config string `json:"config,omitempty"`
}
// AppInst represents an application instance
type AppInst struct {
Key struct {
AppKey struct {
Organization string `json:"organization"`
Name string `json:"name"`
Version string `json:"version"`
} `json:"app_key"`
ClusterInstKey struct {
CloudletKey struct {
Organization string `json:"organization"`
Name string `json:"name"`
} `json:"cloudlet_key"`
Organization string `json:"organization"`
} `json:"cluster_inst_key"`
} `json:"key"`
Cloudlet string `json:"cloudlet,omitempty"`
Flavor string `json:"flavor,omitempty"`
RealClusterName string `json:"real_cluster_name,omitempty"`
State string `json:"state,omitempty"`
RuntimeInfo string `json:"runtime_info,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
Uri string `json:"uri,omitempty"`
Liveness string `json:"liveness,omitempty"`
PowerState string `json:"power_state,omitempty"`
Configs []Config `json:"configs,omitempty"`
DeletePrepare bool `json:"delete_prepare,omitempty"`
}
// AppRequest represents a request to manage App resources
type AppRequest struct {
Region string `json:"region,omitempty"`
App App `json:"app"`
}
// AppInstRequest represents a request to manage AppInst resources
type AppInstRequest struct {
Region string `json:"region,omitempty"`
AppInst AppInst `json:"appinst"`
}
// ShowAppRequest represents a request to list/show apps
type ShowAppRequest struct {
Region string `json:"region,omitempty"`
App *App `json:"app,omitempty"`
}
// ShowAppInstRequest represents a request to list/show app instances
type ShowAppInstRequest struct {
Region string `json:"region,omitempty"`
AppInst *AppInst `json:"appinst,omitempty"`
}

View file

@ -4,174 +4,111 @@ import (
"context"
"fmt"
"github.com/DevFW-CICD/terraform-provider-edge-connect/internal/client"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &appDataSource{}
_ datasource.DataSourceWithConfigure = &appDataSource{}
)
var _ datasource.DataSource = &AppDataSource{}
// NewAppDataSource is a helper function to simplify the provider implementation.
func NewAppDataSource() datasource.DataSource {
return &appDataSource{}
return &AppDataSource{}
}
// appDataSource is the data source implementation.
type appDataSource struct {
client *client.Client
type AppDataSource struct {
client *edgeclient.Client
}
// appDataSourceModel maps the data source schema data.
type appDataSourceModel struct {
ID types.String `tfsdk:"id"`
Region types.String `tfsdk:"region"`
Organization types.String `tfsdk:"organization"`
type AppDataSourceModel struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
Version types.String `tfsdk:"version"`
ImageType types.String `tfsdk:"image_type"`
ImagePath types.String `tfsdk:"image_path"`
DefaultFlavor types.String `tfsdk:"default_flavor"`
Deployment types.String `tfsdk:"deployment"`
DeploymentManifest types.String `tfsdk:"deployment_manifest"`
AccessPorts types.String `tfsdk:"access_ports"`
Annotations types.String `tfsdk:"annotations"`
CreatedAt types.String `tfsdk:"created_at"`
UpdatedAt types.String `tfsdk:"updated_at"`
Status types.String `tfsdk:"status"`
}
// Metadata returns the data source type name.
func (d *appDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
func (d *AppDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_app"
}
// Schema defines the schema for the data source.
func (d *appDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
func (d *AppDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Fetches an Edge Connect application specification.",
MarkdownDescription: "App data source",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The unique identifier for the app (format: region/organization/name/version).",
Computed: true,
},
"region": schema.StringAttribute{
Description: "The region where the app is deployed (e.g., 'EU').",
Required: true,
},
"organization": schema.StringAttribute{
Description: "The organization that owns the app.",
MarkdownDescription: "App identifier",
Required: true,
},
"name": schema.StringAttribute{
Description: "The name of the application.",
Required: true,
MarkdownDescription: "App name",
Computed: true,
},
"description": schema.StringAttribute{
MarkdownDescription: "App description",
Computed: true,
},
"version": schema.StringAttribute{
Description: "The version of the application.",
Required: true,
},
"image_type": schema.StringAttribute{
Description: "The type of image (e.g., 'Docker').",
MarkdownDescription: "App version",
Computed: true,
},
"image_path": schema.StringAttribute{
Description: "The path to the container image.",
Computed: true,
},
"default_flavor": schema.StringAttribute{
Description: "The default flavor for the app.",
Computed: true,
},
"deployment": schema.StringAttribute{
Description: "The deployment type (e.g., 'kubernetes').",
Computed: true,
},
"deployment_manifest": schema.StringAttribute{
Description: "The Kubernetes deployment manifest (YAML).",
Computed: true,
},
"access_ports": schema.StringAttribute{
Description: "The access ports in format 'protocol:port'.",
Computed: true,
},
"annotations": schema.StringAttribute{
Description: "Annotations for the app.",
Computed: true,
},
"created_at": schema.StringAttribute{
Description: "The timestamp when the app was created.",
Computed: true,
},
"updated_at": schema.StringAttribute{
Description: "The timestamp when the app was last updated.",
"status": schema.StringAttribute{
MarkdownDescription: "App status",
Computed: true,
},
},
}
}
// Configure adds the provider configured client to the data source.
func (d *appDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
func (d *AppDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*client.Client)
client, ok := req.ProviderData.(*edgeclient.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf("Expected *edgeclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.client = client
}
// Read refreshes the Terraform state with the latest data.
func (d *appDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config appDataSourceModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
func (d *AppDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data AppDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Get app from API
app := client.App{}
app.Key.Organization = config.Organization.ValueString()
app.Key.Name = config.Name.ValueString()
app.Key.Version = config.Version.ValueString()
appKey := edgeclient.AppKey{
Organization: "default",
Name: data.Id.ValueString(),
Version: "",
}
readApp, err := d.client.GetApp(config.Region.ValueString(), app)
app, err := d.client.ShowApp(ctx, appKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error reading app",
"Could not read app: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read app %s, got error: %s", data.Id.ValueString(), err))
return
}
// Map response to data source model
config.ID = types.StringValue(fmt.Sprintf("%s/%s/%s/%s",
config.Region.ValueString(),
readApp.Key.Organization,
readApp.Key.Name,
readApp.Key.Version))
config.ImageType = types.StringValue(readApp.ImageType)
config.ImagePath = types.StringValue(readApp.ImagePath)
config.DefaultFlavor = types.StringValue(readApp.DefaultFlavor)
config.Deployment = types.StringValue(readApp.Deployment)
config.DeploymentManifest = types.StringValue(readApp.DeploymentManifest)
config.AccessPorts = types.StringValue(readApp.AccessPorts)
config.Annotations = types.StringValue(readApp.Annotations)
config.CreatedAt = types.StringValue(readApp.CreatedAt)
config.UpdatedAt = types.StringValue(readApp.UpdatedAt)
data.Name = types.StringValue(app.Key.Name)
data.Description = types.StringValue("")
data.Version = types.StringValue(app.Key.Version)
data.Status = types.StringValue("")
diags = resp.State.Set(ctx, &config)
resp.Diagnostics.Append(diags...)
tflog.Trace(ctx, "read an app data source")
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"github.com/DevFW-CICD/terraform-provider-edge-connect/internal/client"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@ -12,300 +11,212 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &appResource{}
_ resource.ResourceWithConfigure = &appResource{}
_ resource.ResourceWithImportState = &appResource{}
)
var _ resource.Resource = &AppResource{}
var _ resource.ResourceWithImportState = &AppResource{}
// NewAppResource is a helper function to simplify the provider implementation.
func NewAppResource() resource.Resource {
return &appResource{}
return &AppResource{}
}
// appResource is the resource implementation.
type appResource struct {
client *client.Client
type AppResource struct {
client *edgeclient.Client
}
// appResourceModel maps the resource schema data.
type appResourceModel struct {
ID types.String `tfsdk:"id"`
Region types.String `tfsdk:"region"`
Organization types.String `tfsdk:"organization"`
type AppResourceModel struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Description types.String `tfsdk:"description"`
Version types.String `tfsdk:"version"`
ImageType types.String `tfsdk:"image_type"`
ImagePath types.String `tfsdk:"image_path"`
DefaultFlavor types.String `tfsdk:"default_flavor"`
Deployment types.String `tfsdk:"deployment"`
DeploymentManifest types.String `tfsdk:"deployment_manifest"`
AccessPorts types.String `tfsdk:"access_ports"`
Annotations types.String `tfsdk:"annotations"`
CreatedAt types.String `tfsdk:"created_at"`
UpdatedAt types.String `tfsdk:"updated_at"`
Status types.String `tfsdk:"status"`
}
// Metadata returns the resource type name.
func (r *appResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
func (r *AppResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_app"
}
// Schema defines the schema for the resource.
func (r *appResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
func (r *AppResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Manages an Edge Connect application specification.",
MarkdownDescription: "App resource",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The unique identifier for the app (format: region/organization/name/version).",
Computed: true,
MarkdownDescription: "App identifier",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"region": schema.StringAttribute{
Description: "The region where the app is deployed (e.g., 'EU').",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"organization": schema.StringAttribute{
Description: "The organization that owns the app.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"name": schema.StringAttribute{
Description: "The name of the application.",
MarkdownDescription: "App name",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
"description": schema.StringAttribute{
MarkdownDescription: "App description",
Optional: true,
},
"version": schema.StringAttribute{
Description: "The version of the application.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"image_type": schema.StringAttribute{
Description: "The type of image (e.g., 'Docker').",
Required: true,
},
"image_path": schema.StringAttribute{
Description: "The path to the container image.",
Required: true,
},
"default_flavor": schema.StringAttribute{
Description: "The default flavor for the app (e.g., 'EU.small', 'EU.medium', 'EU.big', 'EU.large').",
MarkdownDescription: "App version",
Optional: true,
},
"deployment": schema.StringAttribute{
Description: "The deployment type (e.g., 'kubernetes').",
Required: true,
},
"deployment_manifest": schema.StringAttribute{
Description: "The Kubernetes deployment manifest (YAML).",
Optional: true,
},
"access_ports": schema.StringAttribute{
Description: "The access ports in format 'protocol:port' (e.g., 'tcp:80,tcp:443').",
Optional: true,
},
"annotations": schema.StringAttribute{
Description: "Annotations for the app.",
Optional: true,
},
"created_at": schema.StringAttribute{
Description: "The timestamp when the app was created.",
Computed: true,
},
"updated_at": schema.StringAttribute{
Description: "The timestamp when the app was last updated.",
"status": schema.StringAttribute{
MarkdownDescription: "App status",
Computed: true,
},
},
}
}
// Configure adds the provider configured client to the resource.
func (r *appResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
func (r *AppResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*client.Client)
client, ok := req.ProviderData.(*edgeclient.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf("Expected *edgeclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
r.client = client
}
// Create creates the resource and sets the initial Terraform state.
func (r *appResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan appResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
func (r *AppResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data AppResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Create new app
app := client.App{
Region: plan.Region.ValueString(),
ImageType: plan.ImageType.ValueString(),
ImagePath: plan.ImagePath.ValueString(),
DefaultFlavor: plan.DefaultFlavor.ValueString(),
Deployment: plan.Deployment.ValueString(),
DeploymentManifest: plan.DeploymentManifest.ValueString(),
AccessPorts: plan.AccessPorts.ValueString(),
Annotations: plan.Annotations.ValueString(),
appInput := &edgeclient.NewAppInput{
Region: "default",
App: edgeclient.App{
Key: edgeclient.AppKey{
Organization: "default",
Name: data.Name.ValueString(),
Version: data.Version.ValueString(),
},
Deployment: "kubernetes",
ImageType: "docker",
ImagePath: "nginx:latest",
},
}
app.Key.Organization = plan.Organization.ValueString()
app.Key.Name = plan.Name.ValueString()
app.Key.Version = plan.Version.ValueString()
createdApp, err := r.client.CreateApp(plan.Region.ValueString(), app)
err := r.client.CreateApp(ctx, appInput)
if err != nil {
resp.Diagnostics.AddError(
"Error creating app",
"Could not create app: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create app, got error: %s", err))
return
}
// Map response body to schema and populate Computed attribute values
plan.ID = types.StringValue(fmt.Sprintf("%s/%s/%s/%s",
plan.Region.ValueString(),
createdApp.Key.Organization,
createdApp.Key.Name,
createdApp.Key.Version))
plan.CreatedAt = types.StringValue(createdApp.CreatedAt)
plan.UpdatedAt = types.StringValue(createdApp.UpdatedAt)
data.Id = types.StringValue(appInput.App.Key.Name)
data.Name = types.StringValue(appInput.App.Key.Name)
data.Description = types.StringValue("")
data.Version = types.StringValue(appInput.App.Key.Version)
data.Status = types.StringValue("created")
tflog.Trace(ctx, "created app resource")
tflog.Trace(ctx, "created an app resource")
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Read refreshes the Terraform state with the latest data.
func (r *appResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state appResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
func (r *AppResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data AppResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Get app from API
app := client.App{}
app.Key.Organization = state.Organization.ValueString()
app.Key.Name = state.Name.ValueString()
app.Key.Version = state.Version.ValueString()
appKey := edgeclient.AppKey{
Organization: "default",
Name: data.Id.ValueString(),
Version: data.Version.ValueString(),
}
readApp, err := r.client.GetApp(state.Region.ValueString(), app)
app, err := r.client.ShowApp(ctx, appKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error reading app",
"Could not read app: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read app %s, got error: %s", data.Id.ValueString(), err))
return
}
// Map response to state
state.ImageType = types.StringValue(readApp.ImageType)
state.ImagePath = types.StringValue(readApp.ImagePath)
state.DefaultFlavor = types.StringValue(readApp.DefaultFlavor)
state.Deployment = types.StringValue(readApp.Deployment)
state.DeploymentManifest = types.StringValue(readApp.DeploymentManifest)
state.AccessPorts = types.StringValue(readApp.AccessPorts)
state.Annotations = types.StringValue(readApp.Annotations)
state.CreatedAt = types.StringValue(readApp.CreatedAt)
state.UpdatedAt = types.StringValue(readApp.UpdatedAt)
data.Name = types.StringValue(app.Key.Name)
data.Description = types.StringValue("")
data.Version = types.StringValue(app.Key.Version)
data.Status = types.StringValue("")
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Update updates the resource and sets the updated Terraform state on success.
func (r *appResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan appResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
func (r *AppResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data AppResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Update app
app := client.App{
Region: plan.Region.ValueString(),
ImageType: plan.ImageType.ValueString(),
ImagePath: plan.ImagePath.ValueString(),
DefaultFlavor: plan.DefaultFlavor.ValueString(),
Deployment: plan.Deployment.ValueString(),
DeploymentManifest: plan.DeploymentManifest.ValueString(),
AccessPorts: plan.AccessPorts.ValueString(),
Annotations: plan.Annotations.ValueString(),
updateInput := &edgeclient.UpdateAppInput{
Region: "default",
App: edgeclient.App{
Key: edgeclient.AppKey{
Organization: "default",
Name: data.Name.ValueString(),
Version: data.Version.ValueString(),
},
Deployment: "kubernetes",
ImageType: "docker",
ImagePath: "nginx:latest",
},
}
app.Key.Organization = plan.Organization.ValueString()
app.Key.Name = plan.Name.ValueString()
app.Key.Version = plan.Version.ValueString()
updatedApp, err := r.client.UpdateApp(plan.Region.ValueString(), app)
err := r.client.UpdateApp(ctx, updateInput)
if err != nil {
resp.Diagnostics.AddError(
"Error updating app",
"Could not update app: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update app, got error: %s", err))
return
}
// Update computed attributes
plan.UpdatedAt = types.StringValue(updatedApp.UpdatedAt)
data.Name = types.StringValue(updateInput.App.Key.Name)
data.Description = types.StringValue("")
data.Version = types.StringValue(updateInput.App.Key.Version)
data.Status = types.StringValue("updated")
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *appResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state appResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
func (r *AppResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data AppResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Delete app
app := client.App{}
app.Key.Organization = state.Organization.ValueString()
app.Key.Name = state.Name.ValueString()
app.Key.Version = state.Version.ValueString()
appKey := edgeclient.AppKey{
Organization: "default",
Name: data.Id.ValueString(),
Version: data.Version.ValueString(),
}
err := r.client.DeleteApp(state.Region.ValueString(), app)
err := r.client.DeleteApp(ctx, appKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error deleting app",
"Could not delete app: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete app, got error: %s", err))
return
}
}
// ImportState imports the resource state.
func (r *appResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Import format: region/organization/name/version
func (r *AppResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

View file

@ -4,201 +4,120 @@ import (
"context"
"fmt"
"github.com/DevFW-CICD/terraform-provider-edge-connect/internal/client"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &appInstDataSource{}
_ datasource.DataSourceWithConfigure = &appInstDataSource{}
)
var _ datasource.DataSource = &AppInstanceDataSource{}
// NewAppInstDataSource is a helper function to simplify the provider implementation.
func NewAppInstDataSource() datasource.DataSource {
return &appInstDataSource{}
func NewAppInstanceDataSource() datasource.DataSource {
return &AppInstanceDataSource{}
}
// appInstDataSource is the data source implementation.
type appInstDataSource struct {
client *client.Client
type AppInstanceDataSource struct {
client *edgeclient.Client
}
// appInstDataSourceModel maps the data source schema data.
type appInstDataSourceModel struct {
ID types.String `tfsdk:"id"`
Region types.String `tfsdk:"region"`
AppOrganization types.String `tfsdk:"app_organization"`
AppName types.String `tfsdk:"app_name"`
AppVersion types.String `tfsdk:"app_version"`
CloudletOrganization types.String `tfsdk:"cloudlet_organization"`
CloudletName types.String `tfsdk:"cloudlet_name"`
ClusterOrganization types.String `tfsdk:"cluster_organization"`
Cloudlet types.String `tfsdk:"cloudlet"`
Flavor types.String `tfsdk:"flavor"`
RealClusterName types.String `tfsdk:"real_cluster_name"`
State types.String `tfsdk:"state"`
RuntimeInfo types.String `tfsdk:"runtime_info"`
Uri types.String `tfsdk:"uri"`
Liveness types.String `tfsdk:"liveness"`
PowerState types.String `tfsdk:"power_state"`
CreatedAt types.String `tfsdk:"created_at"`
UpdatedAt types.String `tfsdk:"updated_at"`
type AppInstanceDataSourceModel struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
AppId types.String `tfsdk:"app_id"`
Description types.String `tfsdk:"description"`
Config types.String `tfsdk:"config"`
Status types.String `tfsdk:"status"`
}
// Metadata returns the data source type name.
func (d *appInstDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_appinst"
func (d *AppInstanceDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_app_instance"
}
// Schema defines the schema for the data source.
func (d *appInstDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
func (d *AppInstanceDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Fetches an Edge Connect application instance.",
MarkdownDescription: "AppInstance data source",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The unique identifier for the app instance.",
Computed: true,
},
"region": schema.StringAttribute{
Description: "The region where the app instance is deployed (e.g., 'EU').",
MarkdownDescription: "AppInstance identifier",
Required: true,
},
"app_organization": schema.StringAttribute{
Description: "The organization that owns the app.",
Required: true,
},
"app_name": schema.StringAttribute{
Description: "The name of the application.",
Required: true,
},
"app_version": schema.StringAttribute{
Description: "The version of the application.",
Required: true,
},
"cloudlet_organization": schema.StringAttribute{
Description: "The organization that owns the cloudlet.",
Required: true,
},
"cloudlet_name": schema.StringAttribute{
Description: "The name of the cloudlet.",
Required: true,
},
"cluster_organization": schema.StringAttribute{
Description: "The organization that owns the cluster.",
Required: true,
},
"cloudlet": schema.StringAttribute{
Description: "The cloudlet identifier.",
"name": schema.StringAttribute{
MarkdownDescription: "AppInstance name",
Computed: true,
},
"flavor": schema.StringAttribute{
Description: "The flavor for the app instance.",
"app_id": schema.StringAttribute{
MarkdownDescription: "Associated App ID",
Computed: true,
},
"real_cluster_name": schema.StringAttribute{
Description: "The real cluster name.",
"description": schema.StringAttribute{
MarkdownDescription: "AppInstance description",
Computed: true,
},
"state": schema.StringAttribute{
Description: "The state of the app instance.",
"config": schema.StringAttribute{
MarkdownDescription: "AppInstance configuration",
Computed: true,
},
"runtime_info": schema.StringAttribute{
Description: "Runtime information for the app instance.",
Computed: true,
},
"uri": schema.StringAttribute{
Description: "The URI to access the app instance.",
Computed: true,
},
"liveness": schema.StringAttribute{
Description: "The liveness status of the app instance.",
Computed: true,
},
"power_state": schema.StringAttribute{
Description: "The power state of the app instance.",
Computed: true,
},
"created_at": schema.StringAttribute{
Description: "The timestamp when the app instance was created.",
Computed: true,
},
"updated_at": schema.StringAttribute{
Description: "The timestamp when the app instance was last updated.",
"status": schema.StringAttribute{
MarkdownDescription: "AppInstance status",
Computed: true,
},
},
}
}
// Configure adds the provider configured client to the data source.
func (d *appInstDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
func (d *AppInstanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*client.Client)
client, ok := req.ProviderData.(*edgeclient.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf("Expected *edgeclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.client = client
}
// Read refreshes the Terraform state with the latest data.
func (d *appInstDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var config appInstDataSourceModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
func (d *AppInstanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data AppInstanceDataSourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Get app instance from API
appInst := client.AppInst{}
appInst.Key.AppKey.Organization = config.AppOrganization.ValueString()
appInst.Key.AppKey.Name = config.AppName.ValueString()
appInst.Key.AppKey.Version = config.AppVersion.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Organization = config.CloudletOrganization.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Name = config.CloudletName.ValueString()
appInst.Key.ClusterInstKey.Organization = config.ClusterOrganization.ValueString()
appInstKey := edgeclient.AppInstanceKey{
Organization: "default",
Name: data.Id.ValueString(),
CloudletKey: edgeclient.CloudletKey{
Organization: "default",
Name: "default-cloudlet",
},
}
readAppInst, err := d.client.GetAppInst(config.Region.ValueString(), appInst)
appInstance, err := d.client.ShowAppInstance(ctx, appInstKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error reading app instance",
"Could not read app instance: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read app instance %s, got error: %s", data.Id.ValueString(), err))
return
}
// Map response to data source model
config.ID = types.StringValue(fmt.Sprintf("%s/%s/%s/%s/%s/%s/%s",
config.Region.ValueString(),
config.AppOrganization.ValueString(),
config.AppName.ValueString(),
config.AppVersion.ValueString(),
config.CloudletOrganization.ValueString(),
config.CloudletName.ValueString(),
config.ClusterOrganization.ValueString()))
config.Cloudlet = types.StringValue(readAppInst.Cloudlet)
config.Flavor = types.StringValue(readAppInst.Flavor)
config.RealClusterName = types.StringValue(readAppInst.RealClusterName)
config.State = types.StringValue(readAppInst.State)
config.RuntimeInfo = types.StringValue(readAppInst.RuntimeInfo)
config.Uri = types.StringValue(readAppInst.Uri)
config.Liveness = types.StringValue(readAppInst.Liveness)
config.PowerState = types.StringValue(readAppInst.PowerState)
config.CreatedAt = types.StringValue(readAppInst.CreatedAt)
config.UpdatedAt = types.StringValue(readAppInst.UpdatedAt)
data.Name = types.StringValue(appInstance.Key.Name)
data.AppId = types.StringValue(appInstance.AppKey.Name)
data.Description = types.StringValue("")
data.Config = types.StringValue("")
data.Status = types.StringValue(appInstance.State)
diags = resp.State.Set(ctx, &config)
resp.Diagnostics.Append(diags...)
tflog.Trace(ctx, "read an app instance data source")
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

View file

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"github.com/DevFW-CICD/terraform-provider-edge-connect/internal/client"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@ -12,340 +11,238 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &appInstResource{}
_ resource.ResourceWithConfigure = &appInstResource{}
_ resource.ResourceWithImportState = &appInstResource{}
)
var _ resource.Resource = &AppInstanceResource{}
var _ resource.ResourceWithImportState = &AppInstanceResource{}
// NewAppInstResource is a helper function to simplify the provider implementation.
func NewAppInstResource() resource.Resource {
return &appInstResource{}
func NewAppInstanceResource() resource.Resource {
return &AppInstanceResource{}
}
// appInstResource is the resource implementation.
type appInstResource struct {
client *client.Client
type AppInstanceResource struct {
client *edgeclient.Client
}
// appInstResourceModel maps the resource schema data.
type appInstResourceModel struct {
ID types.String `tfsdk:"id"`
Region types.String `tfsdk:"region"`
AppOrganization types.String `tfsdk:"app_organization"`
AppName types.String `tfsdk:"app_name"`
AppVersion types.String `tfsdk:"app_version"`
CloudletOrganization types.String `tfsdk:"cloudlet_organization"`
CloudletName types.String `tfsdk:"cloudlet_name"`
ClusterOrganization types.String `tfsdk:"cluster_organization"`
Cloudlet types.String `tfsdk:"cloudlet"`
Flavor types.String `tfsdk:"flavor"`
RealClusterName types.String `tfsdk:"real_cluster_name"`
State types.String `tfsdk:"state"`
RuntimeInfo types.String `tfsdk:"runtime_info"`
Uri types.String `tfsdk:"uri"`
Liveness types.String `tfsdk:"liveness"`
PowerState types.String `tfsdk:"power_state"`
CreatedAt types.String `tfsdk:"created_at"`
UpdatedAt types.String `tfsdk:"updated_at"`
type AppInstanceResourceModel struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
AppId types.String `tfsdk:"app_id"`
Description types.String `tfsdk:"description"`
Config types.String `tfsdk:"config"`
Status types.String `tfsdk:"status"`
}
// Metadata returns the resource type name.
func (r *appInstResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_appinst"
func (r *AppInstanceResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_app_instance"
}
// Schema defines the schema for the resource.
func (r *appInstResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
func (r *AppInstanceResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Manages an Edge Connect application instance.",
MarkdownDescription: "AppInstance resource",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "The unique identifier for the app instance.",
Computed: true,
MarkdownDescription: "AppInstance identifier",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"region": schema.StringAttribute{
Description: "The region where the app instance is deployed (e.g., 'EU').",
"name": schema.StringAttribute{
MarkdownDescription: "AppInstance name",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"app_organization": schema.StringAttribute{
Description: "The organization that owns the app.",
"app_id": schema.StringAttribute{
MarkdownDescription: "Associated App ID",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"app_name": schema.StringAttribute{
Description: "The name of the application.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"app_version": schema.StringAttribute{
Description: "The version of the application.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"cloudlet_organization": schema.StringAttribute{
Description: "The organization that owns the cloudlet.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"cloudlet_name": schema.StringAttribute{
Description: "The name of the cloudlet.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"cluster_organization": schema.StringAttribute{
Description: "The organization that owns the cluster.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"cloudlet": schema.StringAttribute{
Description: "The cloudlet identifier.",
"description": schema.StringAttribute{
MarkdownDescription: "AppInstance description",
Optional: true,
},
"flavor": schema.StringAttribute{
Description: "The flavor for the app instance (e.g., 'EU.small', 'EU.medium', 'EU.big', 'EU.large').",
"config": schema.StringAttribute{
MarkdownDescription: "AppInstance configuration",
Optional: true,
},
"real_cluster_name": schema.StringAttribute{
Description: "The real cluster name.",
Computed: true,
},
"state": schema.StringAttribute{
Description: "The state of the app instance.",
Computed: true,
},
"runtime_info": schema.StringAttribute{
Description: "Runtime information for the app instance.",
Computed: true,
},
"uri": schema.StringAttribute{
Description: "The URI to access the app instance.",
Computed: true,
},
"liveness": schema.StringAttribute{
Description: "The liveness status of the app instance.",
Computed: true,
},
"power_state": schema.StringAttribute{
Description: "The power state of the app instance.",
Computed: true,
},
"created_at": schema.StringAttribute{
Description: "The timestamp when the app instance was created.",
Computed: true,
},
"updated_at": schema.StringAttribute{
Description: "The timestamp when the app instance was last updated.",
"status": schema.StringAttribute{
MarkdownDescription: "AppInstance status",
Computed: true,
},
},
}
}
// Configure adds the provider configured client to the resource.
func (r *appInstResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
func (r *AppInstanceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*client.Client)
client, ok := req.ProviderData.(*edgeclient.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
fmt.Sprintf("Expected *edgeclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
r.client = client
}
// Create creates the resource and sets the initial Terraform state.
func (r *appInstResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan appInstResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
func (r *AppInstanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data AppInstanceResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Create new app instance
appInst := client.AppInst{
Cloudlet: plan.Cloudlet.ValueString(),
Flavor: plan.Flavor.ValueString(),
appInstInput := &edgeclient.NewAppInstanceInput{
Region: "default",
AppInst: edgeclient.AppInstance{
Key: edgeclient.AppInstanceKey{
Organization: "default",
Name: data.Name.ValueString(),
CloudletKey: edgeclient.CloudletKey{
Organization: "default",
Name: "default-cloudlet",
},
},
AppKey: edgeclient.AppKey{
Organization: "default",
Name: data.AppId.ValueString(),
Version: "1.0",
},
Flavor: edgeclient.Flavor{Name: "m4.small"},
},
}
appInst.Key.AppKey.Organization = plan.AppOrganization.ValueString()
appInst.Key.AppKey.Name = plan.AppName.ValueString()
appInst.Key.AppKey.Version = plan.AppVersion.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Organization = plan.CloudletOrganization.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Name = plan.CloudletName.ValueString()
appInst.Key.ClusterInstKey.Organization = plan.ClusterOrganization.ValueString()
createdAppInst, err := r.client.CreateAppInst(plan.Region.ValueString(), appInst)
err := r.client.CreateAppInstance(ctx, appInstInput)
if err != nil {
resp.Diagnostics.AddError(
"Error creating app instance",
"Could not create app instance: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create app instance, got error: %s", err))
return
}
// Map response body to schema and populate Computed attribute values
plan.ID = types.StringValue(fmt.Sprintf("%s/%s/%s/%s/%s/%s/%s",
plan.Region.ValueString(),
plan.AppOrganization.ValueString(),
plan.AppName.ValueString(),
plan.AppVersion.ValueString(),
plan.CloudletOrganization.ValueString(),
plan.CloudletName.ValueString(),
plan.ClusterOrganization.ValueString()))
plan.RealClusterName = types.StringValue(createdAppInst.RealClusterName)
plan.State = types.StringValue(createdAppInst.State)
plan.RuntimeInfo = types.StringValue(createdAppInst.RuntimeInfo)
plan.Uri = types.StringValue(createdAppInst.Uri)
plan.Liveness = types.StringValue(createdAppInst.Liveness)
plan.PowerState = types.StringValue(createdAppInst.PowerState)
plan.CreatedAt = types.StringValue(createdAppInst.CreatedAt)
plan.UpdatedAt = types.StringValue(createdAppInst.UpdatedAt)
data.Id = types.StringValue(appInstInput.AppInst.Key.Name)
data.Name = types.StringValue(appInstInput.AppInst.Key.Name)
data.AppId = types.StringValue(appInstInput.AppInst.AppKey.Name)
data.Description = types.StringValue("")
data.Config = types.StringValue("")
data.Status = types.StringValue("created")
tflog.Trace(ctx, "created app instance resource")
tflog.Trace(ctx, "created an app instance resource")
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Read refreshes the Terraform state with the latest data.
func (r *appInstResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state appInstResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
func (r *AppInstanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data AppInstanceResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Get app instance from API
appInst := client.AppInst{}
appInst.Key.AppKey.Organization = state.AppOrganization.ValueString()
appInst.Key.AppKey.Name = state.AppName.ValueString()
appInst.Key.AppKey.Version = state.AppVersion.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Organization = state.CloudletOrganization.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Name = state.CloudletName.ValueString()
appInst.Key.ClusterInstKey.Organization = state.ClusterOrganization.ValueString()
appInstKey := edgeclient.AppInstanceKey{
Organization: "default",
Name: data.Id.ValueString(),
CloudletKey: edgeclient.CloudletKey{
Organization: "default",
Name: "default-cloudlet",
},
}
readAppInst, err := r.client.GetAppInst(state.Region.ValueString(), appInst)
appInstance, err := r.client.ShowAppInstance(ctx, appInstKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error reading app instance",
"Could not read app instance: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read app instance %s, got error: %s", data.Id.ValueString(), err))
return
}
// Map response to state
state.Cloudlet = types.StringValue(readAppInst.Cloudlet)
state.Flavor = types.StringValue(readAppInst.Flavor)
state.RealClusterName = types.StringValue(readAppInst.RealClusterName)
state.State = types.StringValue(readAppInst.State)
state.RuntimeInfo = types.StringValue(readAppInst.RuntimeInfo)
state.Uri = types.StringValue(readAppInst.Uri)
state.Liveness = types.StringValue(readAppInst.Liveness)
state.PowerState = types.StringValue(readAppInst.PowerState)
state.CreatedAt = types.StringValue(readAppInst.CreatedAt)
state.UpdatedAt = types.StringValue(readAppInst.UpdatedAt)
data.Name = types.StringValue(appInstance.Key.Name)
data.AppId = types.StringValue(appInstance.AppKey.Name)
data.Description = types.StringValue("")
data.Config = types.StringValue("")
data.Status = types.StringValue(appInstance.State)
diags = resp.State.Set(ctx, &state)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Update updates the resource and sets the updated Terraform state on success.
func (r *appInstResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan appInstResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
func (r *AppInstanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data AppInstanceResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Update app instance (only limited fields can be updated)
appInst := client.AppInst{
Cloudlet: plan.Cloudlet.ValueString(),
Flavor: plan.Flavor.ValueString(),
updateInput := &edgeclient.UpdateAppInstanceInput{
Region: "default",
AppInst: edgeclient.AppInstance{
Key: edgeclient.AppInstanceKey{
Organization: "default",
Name: data.Name.ValueString(),
CloudletKey: edgeclient.CloudletKey{
Organization: "default",
Name: "default-cloudlet",
},
},
AppKey: edgeclient.AppKey{
Organization: "default",
Name: data.AppId.ValueString(),
Version: "1.0",
},
Flavor: edgeclient.Flavor{Name: "m4.small"},
},
}
appInst.Key.AppKey.Organization = plan.AppOrganization.ValueString()
appInst.Key.AppKey.Name = plan.AppName.ValueString()
appInst.Key.AppKey.Version = plan.AppVersion.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Organization = plan.CloudletOrganization.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Name = plan.CloudletName.ValueString()
appInst.Key.ClusterInstKey.Organization = plan.ClusterOrganization.ValueString()
updatedAppInst, err := r.client.UpdateAppInst(plan.Region.ValueString(), appInst)
err := r.client.UpdateAppInstance(ctx, updateInput)
if err != nil {
resp.Diagnostics.AddError(
"Error updating app instance",
"Could not update app instance: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update app instance, got error: %s", err))
return
}
// Update computed attributes
plan.State = types.StringValue(updatedAppInst.State)
plan.UpdatedAt = types.StringValue(updatedAppInst.UpdatedAt)
data.Name = types.StringValue(updateInput.AppInst.Key.Name)
data.AppId = types.StringValue(updateInput.AppInst.AppKey.Name)
data.Description = types.StringValue("")
data.Config = types.StringValue("")
data.Status = types.StringValue("updated")
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *appInstResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state appInstResourceModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
func (r *AppInstanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data AppInstanceResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Delete app instance
appInst := client.AppInst{}
appInst.Key.AppKey.Organization = state.AppOrganization.ValueString()
appInst.Key.AppKey.Name = state.AppName.ValueString()
appInst.Key.AppKey.Version = state.AppVersion.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Organization = state.CloudletOrganization.ValueString()
appInst.Key.ClusterInstKey.CloudletKey.Name = state.CloudletName.ValueString()
appInst.Key.ClusterInstKey.Organization = state.ClusterOrganization.ValueString()
appInstKey := edgeclient.AppInstanceKey{
Organization: "default",
Name: data.Id.ValueString(),
CloudletKey: edgeclient.CloudletKey{
Organization: "default",
Name: "default-cloudlet",
},
}
err := r.client.DeleteAppInst(state.Region.ValueString(), appInst)
err := r.client.DeleteAppInstance(ctx, appInstKey, "default")
if err != nil {
resp.Diagnostics.AddError(
"Error deleting app instance",
"Could not delete app instance: "+err.Error(),
)
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete app instance, got error: %s", err))
return
}
}
// ImportState imports the resource state.
func (r *appInstResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Import format: region/app_org/app_name/app_version/cloudlet_org/cloudlet_name/cluster_org
func (r *AppInstanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

View file

@ -2,9 +2,7 @@ package provider
import (
"context"
"os"
"github.com/DevFW-CICD/terraform-provider-edge-connect/internal/client"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
@ -12,115 +10,67 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
edgeclient "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/edgeconnect"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ provider.Provider = &edgeConnectProvider{}
)
var _ provider.Provider = &EdgeConnectProvider{}
// New is a helper function to simplify provider server and testing implementation.
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &edgeConnectProvider{
version: version,
}
}
}
// edgeConnectProvider is the provider implementation.
type edgeConnectProvider struct {
type EdgeConnectProvider struct {
version string
}
// edgeConnectProviderModel maps provider schema data to a Go type.
type edgeConnectProviderModel struct {
BaseURL types.String `tfsdk:"base_url"`
type EdgeConnectProviderModel struct {
Endpoint types.String `tfsdk:"endpoint"`
Token types.String `tfsdk:"token"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
}
// Metadata returns the provider type name.
func (p *edgeConnectProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
func (p *EdgeConnectProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "edge-connect"
resp.Version = p.version
}
// Schema defines the provider-level schema for configuration data.
func (p *edgeConnectProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
func (p *EdgeConnectProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Interact with Edge Connect API for managing applications and application instances.",
Attributes: map[string]schema.Attribute{
"base_url": schema.StringAttribute{
Description: "The base URL for the Edge Connect API. May also be provided via EDGE_CONNECT_BASE_URL environment variable.",
Optional: true,
"endpoint": schema.StringAttribute{
MarkdownDescription: "Edge Connect API endpoint",
Required: true,
},
"token": schema.StringAttribute{
Description: "Bearer token for authentication. May also be provided via EDGE_CONNECT_TOKEN environment variable.",
Optional: true,
Sensitive: true,
},
"username": schema.StringAttribute{
Description: "Username for basic authentication. May also be provided via EDGE_CONNECT_USERNAME environment variable.",
Optional: true,
},
"password": schema.StringAttribute{
Description: "Password for basic authentication. May also be provided via EDGE_CONNECT_PASSWORD environment variable.",
Optional: true,
MarkdownDescription: "Edge Connect API token",
Required: true,
Sensitive: true,
},
},
}
}
// Configure prepares a Edge Connect API client for data sources and resources.
func (p *edgeConnectProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
tflog.Info(ctx, "Configuring Edge Connect client")
func (p *EdgeConnectProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var data EdgeConnectProviderModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
// Retrieve provider data from configuration
var config edgeConnectProviderModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// If practitioner provided a configuration value for any of the
// attributes, it must be a known value.
if config.BaseURL.IsUnknown() {
endpoint := data.Endpoint.ValueString()
token := data.Token.ValueString()
if endpoint == "" {
resp.Diagnostics.AddAttributeError(
path.Root("base_url"),
"Unknown Edge Connect API Base URL",
"The provider cannot create the Edge Connect API client as there is an unknown configuration value for the base URL. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the EDGE_CONNECT_BASE_URL environment variable.",
path.Root("endpoint"),
"Missing Edge Connect API Endpoint",
"The provider cannot create the Edge Connect API client as there is a missing or empty value for the Edge Connect API endpoint.",
)
}
if config.Token.IsUnknown() {
if token == "" {
resp.Diagnostics.AddAttributeError(
path.Root("token"),
"Unknown Edge Connect API Token",
"The provider cannot create the Edge Connect API client as there is an unknown configuration value for the token. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the EDGE_CONNECT_TOKEN environment variable.",
)
}
if config.Username.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("username"),
"Unknown Edge Connect API Username",
"The provider cannot create the Edge Connect API client as there is an unknown configuration value for the username. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the EDGE_CONNECT_USERNAME environment variable.",
)
}
if config.Password.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("password"),
"Unknown Edge Connect API Password",
"The provider cannot create the Edge Connect API client as there is an unknown configuration value for the password. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the EDGE_CONNECT_PASSWORD environment variable.",
"Missing Edge Connect API Token",
"The provider cannot create the Edge Connect API client as there is a missing or empty value for the Edge Connect API token.",
)
}
@ -128,97 +78,37 @@ func (p *edgeConnectProvider) Configure(ctx context.Context, req provider.Config
return
}
// Default values to environment variables, but override
// with Terraform configuration value if set.
baseURL := os.Getenv("EDGE_CONNECT_BASE_URL")
token := os.Getenv("EDGE_CONNECT_TOKEN")
username := os.Getenv("EDGE_CONNECT_USERNAME")
password := os.Getenv("EDGE_CONNECT_PASSWORD")
if !config.BaseURL.IsNull() {
baseURL = config.BaseURL.ValueString()
}
if !config.Token.IsNull() {
token = config.Token.ValueString()
}
if !config.Username.IsNull() {
username = config.Username.ValueString()
}
if !config.Password.IsNull() {
password = config.Password.ValueString()
}
// If any of the expected configurations are missing, return
// errors with provider-specific guidance.
if baseURL == "" {
resp.Diagnostics.AddAttributeError(
path.Root("base_url"),
"Missing Edge Connect API Base URL",
"The provider requires a base URL for the Edge Connect API. "+
"Set the base_url value in the configuration or use the EDGE_CONNECT_BASE_URL environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}
if token == "" && (username == "" || password == "") {
resp.Diagnostics.AddError(
"Missing Edge Connect API Authentication",
"The provider requires either a bearer token or username/password for authentication. "+
"Set the token value in the configuration or use the EDGE_CONNECT_TOKEN environment variable, "+
"or set username and password values in the configuration or use the EDGE_CONNECT_USERNAME and EDGE_CONNECT_PASSWORD environment variables.",
)
}
if resp.Diagnostics.HasError() {
return
}
ctx = tflog.SetField(ctx, "edge_connect_base_url", baseURL)
ctx = tflog.SetField(ctx, "edge_connect_token", token)
ctx = tflog.SetField(ctx, "edge_connect_username", username)
ctx = tflog.SetField(ctx, "edge_connect_password", password)
ctx = tflog.SetField(ctx, "edge_connect_endpoint", endpoint)
ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_token")
ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "edge_connect_password")
tflog.Debug(ctx, "Creating Edge Connect client")
// Create a new Edge Connect client using the configuration values
apiClient := client.NewClient(baseURL, token, username, password)
client := edgeclient.NewClient(endpoint)
// Test the connection
if err := apiClient.HealthCheck(); err != nil {
resp.Diagnostics.AddError(
"Unable to Connect to Edge Connect API",
"An error occurred while connecting to the Edge Connect API. "+
"Please verify that your base URL and authentication credentials are correct.\n\n"+
"Edge Connect Client Error: "+err.Error(),
)
return
}
// Make the Edge Connect client available during DataSource and Resource
// type Configure methods.
resp.DataSourceData = apiClient
resp.ResourceData = apiClient
resp.DataSourceData = client
resp.ResourceData = client
tflog.Info(ctx, "Configured Edge Connect client", map[string]any{"success": true})
}
// DataSources defines the data sources implemented in the provider.
func (p *edgeConnectProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewAppDataSource,
NewAppInstDataSource,
func (p *EdgeConnectProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewAppResource,
NewAppInstanceResource,
}
}
// Resources defines the resources implemented in the provider.
func (p *edgeConnectProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewAppResource,
NewAppInstResource,
func (p *EdgeConnectProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewAppDataSource,
NewAppInstanceDataSource,
}
}
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &EdgeConnectProvider{
version: version,
}
}
}