From 1fccbc8183ad6765355fdc62c4b377cf90c98da9 Mon Sep 17 00:00:00 2001 From: Gezi-lzq Date: Wed, 7 Aug 2024 15:04:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20update=20client=20package=20to=20includ?= =?UTF-8?q?e=20error=20handling=20in=20NewClient=20fu=E2=80=A6=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update client package to include error handling in NewClient function * feat: fix error handling in GetKafkaInstanceByName function * feat: return empty KafkaInstanceResponse when cluster is not found * chore: update automq_kafka_instance_resource_test.go * feat: update automq_kafka_instance_resource_test.go * feat: update automq_kafka_instance_resource_test.go * feat: add kafkaInstanceState scenario --- client/client.go | 20 +++-- client/kafka_instance.go | 57 ++++++++++-- client/models.go | 89 ++++++++++++------- client/path.go | 5 ++ docs/index.md | 6 +- docs/resources/kafka_instance.md | 6 +- .../resource.tf | 6 +- go.sum | 34 +++++++ .../automq_kafka_instance_resource.go | 73 ++++++++++++--- .../automq_kafka_instance_resource_test.go | 45 ++++++++-- internal/provider/provider.go | 34 +++---- internal/provider/util_wait.go | 37 ++++++++ 12 files changed, 322 insertions(+), 90 deletions(-) create mode 100644 client/path.go rename examples/resources/{scaffolding_example => kafka_instance}/resource.tf (83%) diff --git a/client/client.go b/client/client.go index 883e789..e58c615 100644 --- a/client/client.go +++ b/client/client.go @@ -22,6 +22,16 @@ type AuthStruct struct { SecretKey string `json:"secret_key"` } +type ErrorResponse struct { + Code int `json:"code"` + ErrorMessage string `json:"error_message"` + Err error `json:"error"` +} + +func (e *ErrorResponse) Error() string { + return fmt.Errorf("code: %d, message: %s, details: %v", e.Code, e.ErrorMessage, e.Err).Error() +} + func NewClient(host, token *string) (*Client, error) { c := Client{ HTTPClient: &http.Client{Timeout: 10 * time.Second}, @@ -59,18 +69,18 @@ func (c *Client) doRequest(req *http.Request, authToken *string) ([]byte, error) res, err := c.HTTPClient.Do(req) if err != nil { - return nil, err + return nil, &ErrorResponse{Code: 0, ErrorMessage: "Error sending request", Err: err} } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { - return nil, err + return nil, &ErrorResponse{Code: res.StatusCode, ErrorMessage: "Error reading response body", Err: err} } - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body) + if res.StatusCode < 200 || res.StatusCode >= 300 { + return nil, &ErrorResponse{Code: res.StatusCode, ErrorMessage: string(body), Err: nil} } - return body, err + return body, nil } diff --git a/client/kafka_instance.go b/client/kafka_instance.go index 61d00ab..abfdac1 100644 --- a/client/kafka_instance.go +++ b/client/kafka_instance.go @@ -2,6 +2,7 @@ package client import ( "encoding/json" + "fmt" "net/http" "strings" ) @@ -15,37 +16,77 @@ func (c *Client) CreateKafkaInstance(kafka KafkaInstanceRequest) (*KafkaInstance if err != nil { return nil, err } - body, err := c.doRequest(req, &c.Token) if err != nil { return nil, err } - newkafka := KafkaInstanceResponse{} err = json.Unmarshal(body, &newkafka) if err != nil { return nil, err } - return &newkafka, nil } -func (c *Client) GetKafkaInstance(id string) (*KafkaInstanceResponse, error) { - req, err := http.NewRequest("GET", c.HostURL+"/api/v1/instances/"+id, nil) +func (c *Client) GetKafkaInstance(instanceId string) (*KafkaInstanceResponse, error) { + req, err := http.NewRequest("GET", c.HostURL+"/api/v1/instances/"+instanceId, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("error creating request: %v", err) + } + body, err := c.doRequest(req, &c.Token) + if err != nil { + if err.(*ErrorResponse).Code == 404 { + return nil, nil + } + return nil, fmt.Errorf("error doing request: %v", err) } + instance := KafkaInstanceResponse{} + err = json.Unmarshal(body, &instance) + if err != nil { + return nil, fmt.Errorf("error unmarshaling response: %v", err) + } + return &instance, nil +} +func (c *Client) GetKafkaInstanceByName(name string) (*KafkaInstanceResponse, error) { + req, err := http.NewRequest("GET", c.HostURL+instancePath+"?keyword="+name, nil) + if err != nil { + return nil, err + } body, err := c.doRequest(req, &c.Token) if err != nil { return nil, err } - kafka := KafkaInstanceResponse{} err = json.Unmarshal(body, &kafka) if err != nil { return nil, err } + klist := KafkaInstanceResponseList{} + + err = json.Unmarshal(body, &klist) + if err != nil { + return nil, err + } + + var result KafkaInstanceResponse + for _, instance := range klist.List { + if instance.DisplayName == name { + result = instance + return &result, nil + } + } + return nil, fmt.Errorf("Kafka instance with name %s not found", name) +} - return &kafka, nil +func (c *Client) DeleteKafkaInstance(instanceId string) error { + req, err := http.NewRequest("DELETE", c.HostURL+instancePath+"/"+instanceId, nil) + if err != nil { + return err + } + _, err = c.doRequest(req, &c.Token) + if err != nil { + return err + } + return nil } diff --git a/client/models.go b/client/models.go index e2822d2..b43f4f7 100644 --- a/client/models.go +++ b/client/models.go @@ -12,6 +12,7 @@ type KafkaInstanceRequest struct { } type KafkaInstanceRequestSpec struct { + Version string `json:"version"` Template string `json:"template"` PaymentPlan KafkaInstanceRequestPaymentPlan `json:"paymentPlan"` Values []KafkaInstanceRequestValues `json:"values"` @@ -34,39 +35,63 @@ type KafkaInstanceRequestNetwork struct { } type KafkaInstanceResponse struct { - InstanceID string `json:"instanceId"` - GmtCreate time.Time `json:"gmtCreate"` - GmtModified time.Time `json:"gmtModified"` - DisplayName string `json:"displayName"` - Description string `json:"description"` - Status string `json:"status"` - Provider string `json:"provider"` - Region string `json:"region"` - Spec struct { - SpecID string `json:"specId"` - DisplayName string `json:"displayName"` - PaymentPlan struct { - PaymentType string `json:"paymentType"` - Unit string `json:"unit"` - Period int `json:"period"` - } `json:"paymentPlan"` - Template string `json:"template"` - Version string `json:"version"` - Values []struct { - Key string `json:"key"` - Name string `json:"name"` - Value int `json:"value"` - DisplayValue string `json:"displayValue"` - } `json:"values"` - } `json:"spec"` - Networks []struct { - Zone string `json:"zone"` - Subnets []struct { - Subnet string `json:"subnet"` - SubnetName string `json:"subnetName"` - } `json:"subnets"` - } `json:"networks"` + InstanceID string `json:"instanceId"` + GmtCreate time.Time `json:"gmtCreate"` + GmtModified time.Time `json:"gmtModified"` + DisplayName string `json:"displayName"` + Description string `json:"description"` + Status string `json:"status"` + Provider string `json:"provider"` + Region string `json:"region"` + Spec Spec `json:"spec"` + Networks []Network `json:"networks"` Metrics []interface{} `json:"metrics"` AclSupported bool `json:"aclSupported"` AclEnabled bool `json:"aclEnabled"` } + +type Spec struct { + SpecID string `json:"specId"` + DisplayName string `json:"displayName"` + PaymentPlan PaymentPlan `json:"paymentPlan"` + Template string `json:"template"` + Version string `json:"version"` + Values []Value `json:"values"` +} + +type PaymentPlan struct { + PaymentType string `json:"paymentType"` + Unit string `json:"unit"` + Period int `json:"period"` +} + +type Value struct { + Key string `json:"key"` + Name string `json:"name"` + Value int `json:"value"` + DisplayValue string `json:"displayValue"` +} + +type Network struct { + Zone string `json:"zone"` + Subnets []Subnet `json:"subnets"` +} + +type Subnet struct { + Subnet string `json:"subnet"` + SubnetName string `json:"subnetName"` +} + +type KafkaInstanceResponseList struct { + PageNum int `json:"pageNum"` + PageSize int `json:"pageSize"` + Total int `json:"total"` + List []KafkaInstanceResponse `json:"list"` + TotalPage int `json:"totalPage"` +} + +type Metric struct { + Name string `json:"name"` + DisplayName string `json:"displayName"` + Value int `json:"value"` +} diff --git a/client/path.go b/client/path.go new file mode 100644 index 0000000..82f5f2d --- /dev/null +++ b/client/path.go @@ -0,0 +1,5 @@ +package client + +const ( + instancePath = "/api/v1/instances" +) diff --git a/docs/index.md b/docs/index.md index 6e73ab3..111da0c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ provider "scaffolding" { ### Optional -- `access_key` (String) Example provider attribute -- `host` (String) Example provider attribute -- `secret_key` (String) Example provider attribute +- `byoc_access_key` (String) Example provider attribute +- `byoc_host` (String) Example provider attribute +- `byoc_secret_key` (String) Example provider attribute - `token` (String) Example provider attribute diff --git a/docs/resources/kafka_instance.md b/docs/resources/kafka_instance.md index 8d0d3dc..96e8c31 100644 --- a/docs/resources/kafka_instance.md +++ b/docs/resources/kafka_instance.md @@ -19,7 +19,7 @@ AutoMQ Kafka instance resource - `cloud_provider` (String) The cloud provider of the Kafka instance - `compute_specs` (Attributes) The compute specs of the Kafka instance (see [below for nested schema](#nestedatt--compute_specs)) -- `display_name` (String) The display name of the Kafka instance +- `name` (String) The name of the Kafka instance - `network_type` (String) The network type of the Kafka instance - `networks` (Attributes List) The networks of the Kafka instance (see [below for nested schema](#nestedatt--networks)) - `region` (String) The region of the Kafka instance @@ -39,6 +39,10 @@ Required: - `aku` (Number) The template of the compute specs +Optional: + +- `version` (String) The version of the compute specs + ### Nested Schema for `networks` diff --git a/examples/resources/scaffolding_example/resource.tf b/examples/resources/kafka_instance/resource.tf similarity index 83% rename from examples/resources/scaffolding_example/resource.tf rename to examples/resources/kafka_instance/resource.tf index 897734f..eb5eafd 100644 --- a/examples/resources/scaffolding_example/resource.tf +++ b/examples/resources/kafka_instance/resource.tf @@ -8,12 +8,12 @@ terraform { } provider "automq" { - host = "http://localhost:8081" - token = "123456" + byoc_host = "http://localhost:8081" + token = "123456" } resource "automq_kafka_instance" "example" { - display_name = "example" + name = "example" description = "example" cloud_provider = "aliyun" region = "cn-hangzhou" diff --git a/go.sum b/go.sum index abf10c0..fd58c7c 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= @@ -31,6 +32,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= @@ -44,7 +46,9 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3 github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -57,15 +61,20 @@ github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -74,9 +83,11 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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= @@ -92,6 +103,7 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ 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/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8= github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -150,17 +162,22 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -207,6 +224,7 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -216,7 +234,9 @@ github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSg github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -229,6 +249,7 @@ github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -244,6 +265,7 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -260,6 +282,7 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV github.com/wiremock/go-wiremock v1.9.0 h1:9xcU4/IoEfgCaH4TGhQTtiQyBh2eMtu9JB6ppWduK+E= github.com/wiremock/go-wiremock v1.9.0/go.mod h1:/uvO0XFheyy8XetvQqm4TbNQRsGPlByeNegzLzvXs0c= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -272,6 +295,7 @@ github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= @@ -279,13 +303,17 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1: go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -344,6 +372,7 @@ 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/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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= @@ -353,6 +382,7 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -369,6 +399,7 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= 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= @@ -380,7 +411,9 @@ google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -389,3 +422,4 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/internal/provider/automq_kafka_instance_resource.go b/internal/provider/automq_kafka_instance_resource.go index c748a4c..e5db159 100644 --- a/internal/provider/automq_kafka_instance_resource.go +++ b/internal/provider/automq_kafka_instance_resource.go @@ -19,6 +19,7 @@ const ( stateAvailable = "Available" stateChanging = "Changing" stateDeleting = "Deleting" + stateNotFound = "NotFound" stateError = "Error" stateUnexpected = "Unexpected" stateUnknown = "Unknown" @@ -40,7 +41,7 @@ type KafkaInstanceResource struct { // KafkaInstanceResourceModel describes the resource data model. type KafkaInstanceResourceModel struct { InstanceID types.String `tfsdk:"instance_id"` - DisplayName types.String `tfsdk:"display_name"` + Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` CloudProvider types.String `tfsdk:"cloud_provider"` Region types.String `tfsdk:"region"` @@ -55,7 +56,8 @@ type NetworkModel struct { } type ComputeSpecsModel struct { - Aku types.Int64 `tfsdk:"aku"` + Aku types.Int64 `tfsdk:"aku"` + Version types.String `tfsdk:"version"` } func (r *KafkaInstanceResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -75,8 +77,8 @@ func (r *KafkaInstanceResource) Schema(ctx context.Context, req resource.SchemaR stringplanmodifier.UseStateForUnknown(), }, }, - "display_name": schema.StringAttribute{ - MarkdownDescription: "The display name of the Kafka instance", + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the Kafka instance", Required: true, }, "description": schema.StringAttribute{ @@ -119,6 +121,10 @@ func (r *KafkaInstanceResource) Schema(ctx context.Context, req resource.SchemaR Required: true, Description: "The template of the compute specs", }, + "version": schema.StringAttribute{ + Optional: true, + Description: "The version of the compute specs", + }, }, }, }, @@ -157,7 +163,7 @@ func (r *KafkaInstanceResource) Create(ctx context.Context, req resource.CreateR // Generate API request body from plan request := client.KafkaInstanceRequest{ - DisplayName: data.DisplayName.ValueString(), + DisplayName: data.Name.ValueString(), Description: data.Description.ValueString(), Provider: data.CloudProvider.ValueString(), Region: data.Region.ValueString(), @@ -168,6 +174,11 @@ func (r *KafkaInstanceResource) Create(ctx context.Context, req resource.CreateR Values: []client.KafkaInstanceRequestValues{{Key: "aku", Value: fmt.Sprintf("%d", data.ComputeSpecs.Aku.ValueInt64())}}, }, } + + if !data.ComputeSpecs.Version.IsUnknown() && !data.ComputeSpecs.Version.IsNull() { + request.Spec.Version = data.ComputeSpecs.Version.ValueString() + } + for i, network := range data.Networks { request.Networks[i] = client.KafkaInstanceRequestNetwork{ Zone: network.Zone.ValueString(), @@ -218,6 +229,10 @@ func (r *KafkaInstanceResource) Update(ctx context.Context, req resource.UpdateR if resp.Diagnostics.HasError() { return } + if data.InstanceID.IsNull() && data.InstanceID.ValueString() == "" { + resp.Diagnostics.AddError("Client Error", "Instance ID is required for updating Kafka instance") + return + } // If applicable, this is a great opportunity to initialize any necessary // provider client data and make a call using it. @@ -241,13 +256,47 @@ func (r *KafkaInstanceResource) Delete(ctx context.Context, req resource.DeleteR 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 - // } + instance, err := GetKafkaInstance(&data, r.client) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get Kafka instance %q, got error: %s", data.InstanceID.ValueString(), err)) + return + } + if instance == nil { + return + } + + instanceId := data.InstanceID.ValueString() + + if instance.Status != stateDeleting { + err = r.client.DeleteKafkaInstance(instanceId) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete Kafka instance %q, got error: %s", data.InstanceID.ValueString(), err)) + return + } + } + + if err := waitForKafkaClusterToDeleted(ctx, r.client, instanceId, data.CloudProvider.ValueString()); err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Error waiting for Kafka Cluster %q to provision: %s", instanceId, createDescriptiveError(err))) + return + } +} + +func GetKafkaInstance(data *KafkaInstanceResourceModel, client *client.Client) (*client.KafkaInstanceResponse, error) { + if data.InstanceID.IsNull() && !data.Name.IsNull() { + kafka, err := client.GetKafkaInstanceByName(data.Name.ValueString()) + if err != nil { + return nil, fmt.Errorf("error getting Kafka instance by name %s: %v", data.Name.ValueString(), err) + } + return kafka, nil + } + if !data.InstanceID.IsNull() { + kafka, err := client.GetKafkaInstance(data.InstanceID.ValueString()) + if err != nil { + return nil, fmt.Errorf("error getting Kafka instance by ID %s: %v", data.InstanceID.ValueString(), err) + } + return kafka, nil + } + return nil, fmt.Errorf("both Kafka instance ID and name are null") } func (r *KafkaInstanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { diff --git a/internal/provider/automq_kafka_instance_resource_test.go b/internal/provider/automq_kafka_instance_resource_test.go index 30d557b..96e992d 100644 --- a/internal/provider/automq_kafka_instance_resource_test.go +++ b/internal/provider/automq_kafka_instance_resource_test.go @@ -7,9 +7,10 @@ import ( "net/http" "testing" + "terraform-provider-automq/client" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/wiremock/go-wiremock" - "terraform-provider-automq/client" ) const ( @@ -43,16 +44,33 @@ func TestAccKafkaInstanceResource(t *testing.T) { if err != nil { t.Fatal(err) } - + deletingResponse := testAccKafkaInstanceResponseInDeleting() + deletingResponseJson, err := json.Marshal(deletingResponse) + if err != nil { + t.Fatal(err) + } createInstanceStub := wiremock.Post(wiremock. URLPathEqualTo(createKafkaInstancePath)). WillReturnResponse(wiremock.NewResponse().WithBody(string(creatingResponseJson)).WithStatus(http.StatusOK)) _ = wiremockClient.StubFor(createInstanceStub) - getInstanceStub := wiremock.Get(wiremock. + getInstanceStubWhenStarted := wiremock.Get(wiremock. + URLPathEqualTo(fmt.Sprintf(getKafkaInstancePath, creatingResponse.InstanceID))). + WillReturnResponse(wiremock.NewResponse().WithBody(string(availableResponseJson)).WithStatus(http.StatusOK)). + InScenario("KafkaInstanceState").WhenScenarioStateIs(wiremock.ScenarioStateStarted) + _ = wiremockClient.StubFor(getInstanceStubWhenStarted) + + deleteInstanceStub := wiremock.Delete(wiremock. URLPathEqualTo(fmt.Sprintf(getKafkaInstancePath, creatingResponse.InstanceID))). - WillReturnResponse(wiremock.NewResponse().WithBody(string(availableResponseJson)).WithStatus(http.StatusOK)) - _ = wiremockClient.StubFor(getInstanceStub) + WillReturnResponse(wiremock.NewResponse().WithBody(string(deletingResponseJson)).WithStatus(http.StatusNoContent)). + InScenario("KafkaInstanceState").WillSetStateTo("Deleted") + _ = wiremockClient.StubFor(deleteInstanceStub) + + getInstanceStubWhenDeleted := wiremock.Get(wiremock. + URLPathEqualTo(fmt.Sprintf(getKafkaInstancePath, creatingResponse.InstanceID))). + WillReturnResponse(wiremock.NewResponse().WithStatus(http.StatusNotFound)). + InScenario("KafkaInstanceState").WhenScenarioStateIs("Deleted") + _ = wiremockClient.StubFor(getInstanceStubWhenDeleted) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -62,24 +80,24 @@ func TestAccKafkaInstanceResource(t *testing.T) { { Config: testAccKafkaInstanceResourceConfig(mockAutoMQTestServerUrl), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("automq_kafka_instance.test", "display_name", "test"), + resource.TestCheckResourceAttr("automq_kafka_instance.test", "name", "test"), ), }, }, }) checkStubCount(t, wiremockClient, createInstanceStub, fmt.Sprintf("POST %s", createKafkaInstancePath), expectedCountOne) - checkStubCount(t, wiremockClient, getInstanceStub, fmt.Sprintf("GET %s", getKafkaInstancePath), expectedCountOne) + checkStubCount(t, wiremockClient, deleteInstanceStub, fmt.Sprintf("DELETE %s", getKafkaInstancePath), expectedCountOne) } func testAccKafkaInstanceResourceConfig(mockServerUrl string) string { return fmt.Sprintf(` provider "automq" { - host = "%s" + byoc_host = "%s" token = "123456" } resource "automq_kafka_instance" "test" { - display_name = "test" + name = "test" description = "test" cloud_provider = "aliyun" region = "cn-hangzhou" @@ -112,3 +130,12 @@ func testAccKafkaInstanceResponseInAvailable() client.KafkaInstanceResponse { createInstanceResponse.InstanceID = "test" return createInstanceResponse } + +// Return a json string for a KafkaInstanceResponse with Available status +func testAccKafkaInstanceResponseInDeleting() client.KafkaInstanceResponse { + createInstanceResponse := client.KafkaInstanceResponse{} + createInstanceResponse.Status = stateDeleting + createInstanceResponse.DisplayName = "test" + createInstanceResponse.InstanceID = "test" + return createInstanceResponse +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 7173332..0c6db51 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -29,10 +29,10 @@ type AutoMQProvider struct { // autoMQProviderModel describes the provider data model. type autoMQProviderModel struct { - AccessKey types.String `tfsdk:"access_key"` - SecretKey types.String `tfsdk:"secret_key"` - Token types.String `tfsdk:"token"` - Host types.String `tfsdk:"host"` + BYOCAccessKey types.String `tfsdk:"byoc_access_key"` + BYOCSecretKey types.String `tfsdk:"byoc_secret_key"` + BYOCHost types.String `tfsdk:"byoc_host"` + Token types.String `tfsdk:"token"` } func (p *AutoMQProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -43,19 +43,19 @@ func (p *AutoMQProvider) Metadata(ctx context.Context, req provider.MetadataRequ func (p *AutoMQProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "access_key": schema.StringAttribute{ + "byoc_access_key": schema.StringAttribute{ MarkdownDescription: "Example provider attribute", Optional: true, }, - "secret_key": schema.StringAttribute{ + "byoc_secret_key": schema.StringAttribute{ MarkdownDescription: "Example provider attribute", Optional: true, }, - "token": schema.StringAttribute{ + "byoc_host": schema.StringAttribute{ MarkdownDescription: "Example provider attribute", Optional: true, }, - "host": schema.StringAttribute{ + "token": schema.StringAttribute{ MarkdownDescription: "Example provider attribute", Optional: true, }, @@ -72,9 +72,9 @@ func (p *AutoMQProvider) Configure(ctx context.Context, req provider.ConfigureRe return } // Configuration values are now available. - if data.Host.IsUnknown() { + if data.BYOCHost.IsUnknown() { resp.Diagnostics.AddAttributeError( - path.Root("host"), + path.Root("byoc_host"), "Unknown AutoMQ API Host", "The provider cannot create the AutoMQ API client as there is an unknown configuration value for the AutoMQ API host. "+ "Either target apply the source of the value first, set the value statically in the configuration, or use the AUTOMQ_HOST environment variable.", @@ -97,11 +97,11 @@ func (p *AutoMQProvider) Configure(ctx context.Context, req provider.ConfigureRe // Default values to environment variables, but override // with Terraform configuration value if set. - host := os.Getenv("AUTOMQ_HOST") + byco_host := os.Getenv("AUTOMQ_BYOC_HOST") token := os.Getenv("AUTOMQ_TOKEN") - if !data.Host.IsNull() { - host = data.Host.ValueString() + if !data.BYOCHost.IsNull() { + byco_host = data.BYOCHost.ValueString() } if !data.Token.IsNull() { token = data.Token.ValueString() @@ -110,9 +110,9 @@ func (p *AutoMQProvider) Configure(ctx context.Context, req provider.ConfigureRe // If any of the expected configurations are missing, return // errors with provider-specific guidance. - if host == "" { + if byco_host == "" { resp.Diagnostics.AddAttributeError( - path.Root("host"), + path.Root("byoc_host"), "Missing AutoMQ API Host", "The provider cannot create the AutoMQ API client as there is a missing or empty value for the AutoMQ API host. "+ "Set the host value in the configuration or use the AUTOMQ_HOST environment variable. "+ @@ -134,13 +134,13 @@ func (p *AutoMQProvider) Configure(ctx context.Context, req provider.ConfigureRe return } - ctx = tflog.SetField(ctx, "automq_env_host", host) + ctx = tflog.SetField(ctx, "automq_env_byoc_host", byco_host) ctx = tflog.SetField(ctx, "automq_env_token", token) ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "automq_env_token") tflog.Debug(ctx, "Creating AutoMQ client") - client, err := client.NewClient(&host, &token) + client, err := client.NewClient(&byco_host, &token) if err != nil { resp.Diagnostics.AddError( "Unable to Create AutoMQ API Client", diff --git a/internal/provider/util_wait.go b/internal/provider/util_wait.go index 3863756..147df1f 100644 --- a/internal/provider/util_wait.go +++ b/internal/provider/util_wait.go @@ -35,6 +35,24 @@ func waitForKafkaClusterToProvision(ctx context.Context, c *client.Client, clust return nil } +func waitForKafkaClusterToDeleted(ctx context.Context, c *client.Client, clusterId, cloudProvider string) error { + delay, pollInterval := getDelayAndPollInterval(5*time.Second, 1*time.Minute, false) + stateConf := &retry.StateChangeConf{ + Pending: []string{stateDeleting}, + Target: []string{stateNotFound}, + Refresh: kafkaClusterDeletedStatus(ctx, c, clusterId), + Timeout: getTimeoutFor(cloudProvider), + Delay: delay, + PollInterval: pollInterval, + } + + tflog.Debug(ctx, fmt.Sprintf("Waiting for Kafka Cluster %q provisioning status to become %q", clusterId, stateAvailable)) + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return err + } + return nil +} + func kafkaClusterProvisionStatus(ctx context.Context, c *client.Client, clusterId string) retry.StateRefreshFunc { return func() (result interface{}, s string, err error) { cluster, err := c.GetKafkaInstance(clusterId) @@ -54,6 +72,25 @@ func kafkaClusterProvisionStatus(ctx context.Context, c *client.Client, clusterI } } +func kafkaClusterDeletedStatus(ctx context.Context, c *client.Client, clusterId string) retry.StateRefreshFunc { + return func() (result interface{}, s string, err error) { + cluster, err := c.GetKafkaInstance(clusterId) + if err != nil { + tflog.Warn(ctx, fmt.Sprintf("Error reading Kafka Cluster %q: %s", clusterId, createDescriptiveError(err))) + return nil, stateUnknown, err + } + if cluster == nil { + return &client.KafkaInstanceResponse{}, stateNotFound, nil + } + tflog.Debug(ctx, fmt.Sprintf("Waiting for Kafka Cluster %q provisioning status to become %q: current status is %q", clusterId, stateDeleting, cluster.Status)) + if cluster.Status == stateError { + return nil, stateError, fmt.Errorf("kafka Cluster %q provisioning status is %q", clusterId, stateError) + } else { + return cluster, cluster.Status, nil + } + } +} + // If `isAcceptanceTestMode` is true, default wait time and poll interval are returned // If `isAcceptanceTestMode` is false, customized wait time and poll interval are returned func getDelayAndPollInterval(delayNormal, pollIntervalNormal time.Duration, isAcceptanceTestMode bool) (time.Duration, time.Duration) {