diff --git a/README.md b/README.md index b5bf7ad..aee43a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Netdata Cloud provider for Terraform +# Terraform Provider for Netdata Cloud -This is the [Netdata Cloud](https://www.netdata.cloud/) provider for [Terraform](https://www.terraform.io/). +This is the [Terraform](https://www.terraform.io/) provider for the [Netdata Cloud](https://www.netdata.cloud/). This provider allows you to install and manage Netdata Cloud resources using Terraform. @@ -24,7 +24,7 @@ TODO * from source code - * setup your CLI configuration + * setup your [CLI configuration](https://developer.hashicorp.com/terraform/cli/config/config-file#development-overrides-for-provider-developers) ```console $ cat ~/.terraformrc @@ -58,7 +58,7 @@ terraform { provider "netdata" { url = "https://app.netdata.cloud" - authtoken = "authtoken" + authtoken = "" } resource "netdata_space" "test" { diff --git a/docs/index.md b/docs/index.md index 0497b07..88c320a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,17 +15,14 @@ description: |- ```terraform provider "netdata" { url = "https://app.netdata.cloud" - authtoken = "authtoken" + authtoken = "" } ``` ## Schema -### Required - -- `authtoken` (String, Sensitive) Netdata Cloud Authentication Token. Can be also set as environment variable `NETDATA_CLOUD_AUTH_TOKEN` - ### Optional +- `authtoken` (String, Sensitive) Netdata Cloud Authentication Token. Can be also set as environment variable `NETDATA_CLOUD_AUTH_TOKEN` - `url` (String) Netdata Cloud URL Address by default is https://app.netdata.cloud. Can be also set as environment variable `NETDATA_CLOUD_URL` diff --git a/docs/resources/space.md b/docs/resources/space.md index 2e4c649..74ae55e 100644 --- a/docs/resources/space.md +++ b/docs/resources/space.md @@ -39,7 +39,7 @@ resource "netdata_space" "test" { Import is supported using the following syntax: ```shell -#!/bin/sh +#!/bin/sh terraform import netdata_space.test ee3ec76d-0180-4ef4-93ae-c94c1e7ed2f1 ``` diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index 3aeab3f..a51015c 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -1,4 +1,4 @@ provider "netdata" { url = "https://app.netdata.cloud" - authtoken = "authtoken" + authtoken = "" } diff --git a/examples/spaces/main.tf b/examples/spaces/main.tf index 0116d66..1ed65c6 100644 --- a/examples/spaces/main.tf +++ b/examples/spaces/main.tf @@ -9,7 +9,7 @@ terraform { provider "netdata" { url = "https://app.netdata.cloud" - authtoken = "authtoken" + authtoken = "" } resource "netdata_space" "test" { diff --git a/internal/client/client.go b/internal/client/client.go new file mode 100644 index 0000000..24dd13d --- /dev/null +++ b/internal/client/client.go @@ -0,0 +1,50 @@ +package client + +import ( + "errors" + "fmt" + "io" + "net/http" + "time" +) + +var ErrNotFound = errors.New("not found") + +type Client struct { + HostURL string + HTTPClient *http.Client + AuthToken string +} + +func NewClient(url, authtoken string) *Client { + c := Client{ + HostURL: url, + AuthToken: "Bearer " + authtoken, + HTTPClient: &http.Client{Timeout: 10 * time.Second}, + } + + return &c +} + +func (c *Client) doRequest(req *http.Request) ([]byte, error) { + req.Header.Set("Authorization", c.AuthToken) + req.Header.Set("Accept", "application/json") + + res, err := c.HTTPClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + statusOK := res.StatusCode >= 200 && res.StatusCode < 300 + if !statusOK { + return nil, fmt.Errorf("status: %d, body: %s", res.StatusCode, body) + } + + return body, err +} diff --git a/internal/client/models.go b/internal/client/models.go new file mode 100644 index 0000000..ff5c3f9 --- /dev/null +++ b/internal/client/models.go @@ -0,0 +1,7 @@ +package client + +type SpaceInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` +} diff --git a/internal/client/spaces.go b/internal/client/spaces.go new file mode 100644 index 0000000..86b4181 --- /dev/null +++ b/internal/client/spaces.go @@ -0,0 +1,112 @@ +package client + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" +) + +func (c *Client) GetSpaces() (*[]SpaceInfo, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v3/spaces", c.HostURL), nil) + if err != nil { + return nil, err + } + + body, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var spaces []SpaceInfo + err = json.Unmarshal(body, &spaces) + if err != nil { + return nil, err + } + + return &spaces, nil +} + +func (c *Client) GetSpaceByID(id string) (*SpaceInfo, error) { + spaces, err := c.GetSpaces() + if err != nil { + return nil, err + } + for _, space := range *spaces { + if space.ID == id { + return &space, nil + } + } + return nil, ErrNotFound +} + +func (c *Client) CreateSpace(name, description string) (*SpaceInfo, error) { + reqBody, err := json.Marshal(map[string]string{"name": name}) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v1/spaces", c.HostURL), strings.NewReader(string(reqBody))) + if err != nil { + return nil, err + } + + respBody, err := c.doRequest(req) + if err != nil { + return nil, err + } + + var space SpaceInfo + err = json.Unmarshal(respBody, &space) + if err != nil { + return nil, err + } + + err = c.UpdateSpaceByID(space.ID, name, description) + if err != nil { + return nil, err + } + space.Name = name + space.Description = description + + return &space, nil +} + +func (c *Client) UpdateSpaceByID(id, name, description string) error { + if id == "" { + return fmt.Errorf("id is empty") + } + reqBody, err := json.Marshal(map[string]string{ + "name": name, + "description": description, + }) + if err != nil { + return err + } + req, err := http.NewRequest("PATCH", fmt.Sprintf("%s/api/v1/spaces/%s", c.HostURL, id), strings.NewReader(string(reqBody))) + if err != nil { + return fmt.Errorf("req %+v", req) + } + _, err = c.doRequest(req) + if err != nil { + return err + } + return nil +} + +func (c *Client) DeleteSpaceByID(id string) error { + if id == "" { + return fmt.Errorf("id is empty") + } + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/spaces/%s", c.HostURL, id), nil) + if err != nil { + return err + } + + _, err = c.doRequest(req) + if err != nil { + return err + } + + return nil +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b18e629..e36a3ba 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/netdata/terraform-provider-netdata/pkg/client" + "github.com/netdata/terraform-provider-netdata/internal/client" ) var _ provider.Provider = &netdataCloudProvider{} diff --git a/internal/provider/space_data_source.go b/internal/provider/space_data_source.go index 9b8ebf5..1c18a06 100644 --- a/internal/provider/space_data_source.go +++ b/internal/provider/space_data_source.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/netdata/terraform-provider-netdata/pkg/client" + "github.com/netdata/terraform-provider-netdata/internal/client" ) var ( diff --git a/internal/provider/space_resource.go b/internal/provider/space_resource.go index fc232ab..d933e7d 100644 --- a/internal/provider/space_resource.go +++ b/internal/provider/space_resource.go @@ -13,7 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/netdata/terraform-provider-netdata/pkg/client" + "github.com/netdata/terraform-provider-netdata/internal/client" ) var (