diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..9807f44 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,3 @@ +linters-settings: + errcheck: + ignore: Set \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..77d6355 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,54 @@ +# Visit https://goreleaser.com for documentation on how to customize this +# behavior. +before: + hooks: + # this is just an example and not a requirement for provider building/publishing + - go mod tidy +builds: +- env: + # goreleaser does not work with CGO, it could also complicate + # usage by users in CI/CD systems like Terraform Cloud where + # they are unable to install libraries. + - CGO_ENABLED=0 + mod_timestamp: '{{ .CommitTimestamp }}' + flags: + - -trimpath + ldflags: + - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + goos: + - freebsd + - windows + - linux + - darwin + goarch: + - amd64 + - '386' + - arm + - arm64 + ignore: + - goos: darwin + goarch: '386' + binary: '{{ .ProjectName }}_v{{ .Version }}' +archives: +- format: zip + name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' +checksum: + name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' + algorithm: sha256 +signs: + - artifacts: checksum + args: + # if you are using this in a GitHub action or some other automated pipeline, you + # need to pass the batch flag to indicate its not interactive. + - "--batch" + - "--local-user" + - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key + - "--output" + - "${signature}" + - "--detach-sign" + - "${artifact}" +release: + # If you want to manually examine the release before its live, uncomment this line: + # draft: true +changelog: + skip: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ab7522f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -dist: trusty -sudo: required -services: -- docker -language: go -go: - - "1.11.x" - -install: -# This script is used by the Travis build to install a cookie for -# go.googlesource.com so rate limits are higher when using `go get` to fetch -# packages that live there. -# See: https://github.com/golang/go/issues/12933 -- bash scripts/gogetcookie.sh - -script: -- make test -- make vet -- make website-test - -branches: - only: - - master -matrix: - fast_finish: true - allow_failures: - - go: tip -env: - - GOFLAGS=-mod=vendor GO111MODULE=on diff --git a/GNUmakefile b/GNUmakefile index 6b88857..f2b5bfa 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -14,7 +14,7 @@ test: fmtcheck xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 testacc: fmtcheck - TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m + INFLUXDB_USERNAME=test INFLUXDB_PASSWORD=test TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m vet: @echo "go vet ." diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..0c3f611 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,12 @@ +version: "2.2" + +services: + influxdb: + image: influxdb:1.8.10 + ports: + - "8086:8086" + environment: + "DOCKER_INFLUXDB_INIT_USERNAME": "admin" + "DOCKER_INFLUXDB_INIT_PASSWORD": "password" + "DOCKER_INFLUXDB_INIT_ORG": "test" + "DOCKER_INFLUXDB_INIT_BUCKET": "test" \ No newline at end of file diff --git a/influxdb/provider.go b/influxdb/provider.go index 551af50..564d518 100644 --- a/influxdb/provider.go +++ b/influxdb/provider.go @@ -53,7 +53,7 @@ func Provider() *schema.Provider { func configure(d *schema.ResourceData) (interface{}, error) { url, err := url.Parse(d.Get("url").(string)) if err != nil { - return nil, fmt.Errorf("invalid InfluxDB URL: %s", err) + return nil, fmt.Errorf("invalid InfluxDB URL: %w", err) } config := client.Config{ @@ -70,7 +70,7 @@ func configure(d *schema.ResourceData) (interface{}, error) { _, _, err = conn.Ping() if err != nil { - return nil, fmt.Errorf("error pinging server: %s", err) + return nil, fmt.Errorf("error pinging server: %w", err) } return conn, nil diff --git a/influxdb/resource_database.go b/influxdb/resource_database.go index 1108f6e..7676c73 100644 --- a/influxdb/resource_database.go +++ b/influxdb/resource_database.go @@ -2,9 +2,9 @@ package influxdb import ( "fmt" - "strconv" "encoding/json" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/influxdata/influxdb/client" ) @@ -15,6 +15,9 @@ func resourceDatabase() *schema.Resource { Read: readDatabase, Delete: deleteDatabase, Update: updateDatabase, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, Schema: map[string]*schema.Schema{ "name": { @@ -23,7 +26,7 @@ func resourceDatabase() *schema.Resource { ForceNew: true, }, "retention_policies": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, ForceNew: false, Elem: &schema.Resource{ @@ -44,7 +47,7 @@ func resourceDatabase() *schema.Resource { "shardgroupduration": { Type: schema.TypeString, Optional: true, - Default: "", + Default: "1h0m0s", }, "default": { Type: schema.TypeBool, @@ -62,7 +65,7 @@ func createDatabase(d *schema.ResourceData, meta interface{}) error { conn := meta.(*client.Client) name := d.Get("name").(string) - queryStr := fmt.Sprintf("CREATE DATABASE %s", quoteIdentifier(name)) + queryStr := fmt.Sprintf("CREATE DATABASE %q", name) query := client.Query{ Command: queryStr, } @@ -78,7 +81,7 @@ func createDatabase(d *schema.ResourceData, meta interface{}) error { d.SetId(name) if v, ok := d.GetOk("retention_policies"); ok { - retentionPolicies := v.([]interface{}) + retentionPolicies := v.(*schema.Set).List() for _, vv := range retentionPolicies { retentionPolicy := vv.(map[string]interface{}) if err := createRetentionPolicy(conn, retentionPolicy["name"].(string), retentionPolicy["duration"].(string), retentionPolicy["replication"].(int), retentionPolicy["shardgroupduration"].(string), retentionPolicy["default"].(bool), name); err != nil { @@ -87,7 +90,7 @@ func createDatabase(d *schema.ResourceData, meta interface{}) error { } } - return nil + return readDatabase(d, meta) } func createRetentionPolicy(conn *client.Client, policyName string, duration string, replication int, shardGroupDuration string, defaultPolicy bool, database string) error { @@ -98,9 +101,9 @@ func createRetentionPolicy(conn *client.Client, policyName string, duration stri } if defaultPolicy { - return exec(conn, fmt.Sprintf("CREATE RETENTION POLICY %s ON %s DURATION %s REPLICATION %d %s DEFAULT", quoteIdentifier(policyName), quoteIdentifier(database), duration, replication, shardDuration)) + return exec(conn, fmt.Sprintf("CREATE RETENTION POLICY %q ON %q DURATION %s REPLICATION %d %s DEFAULT", policyName, database, duration, replication, shardDuration)) } else { - return exec(conn, fmt.Sprintf("CREATE RETENTION POLICY %s ON %s DURATION %s REPLICATION %d %s", quoteIdentifier(policyName), quoteIdentifier(database), duration, replication, shardDuration)) + return exec(conn, fmt.Sprintf("CREATE RETENTION POLICY %q ON %q DURATION %s REPLICATION %d %s", policyName, database, duration, replication, shardDuration)) } } @@ -112,14 +115,14 @@ func updateRetentionPolicy(conn *client.Client, policyName string, duration stri } if defaultPolicy { - return exec(conn, fmt.Sprintf("ALTER RETENTION POLICY %s ON %s DURATION %s REPLICATION %d %s DEFAULT", quoteIdentifier(policyName), quoteIdentifier(database), duration, replication, shardDuration)) + return exec(conn, fmt.Sprintf("ALTER RETENTION POLICY %q ON %q DURATION %s REPLICATION %d %s DEFAULT", policyName, database, duration, replication, shardDuration)) } else { - return exec(conn, fmt.Sprintf("ALTER RETENTION POLICY %s ON %s DURATION %s REPLICATION %d %s", quoteIdentifier(policyName), quoteIdentifier(database), duration, replication, shardDuration)) + return exec(conn, fmt.Sprintf("ALTER RETENTION POLICY %q ON %q DURATION %s REPLICATION %d %s", policyName, database, duration, replication, shardDuration)) } } func deleteRetentionPolicy(conn *client.Client, policyName string, database string) error { - return exec(conn, fmt.Sprintf("DROP RETENTION POLICY %s ON %s", quoteIdentifier(policyName), quoteIdentifier(database))) + return exec(conn, fmt.Sprintf("DROP RETENTION POLICY %q ON %q", policyName, database)) } func readDatabase(d *schema.ResourceData, meta interface{}) error { @@ -143,6 +146,7 @@ func readDatabase(d *schema.ResourceData, meta interface{}) error { for _, result := range resp.Results[0].Series[0].Values { if result[0] == name { + d.Set("name", d.Id()) readRetentionPolicies(d, meta) return nil } @@ -156,10 +160,10 @@ func readDatabase(d *schema.ResourceData, meta interface{}) error { func readRetentionPolicies(d *schema.ResourceData, meta interface{}) error { conn := meta.(*client.Client) - name := d.Get("name").(string) + name := d.Id() query := client.Query{ - Command: fmt.Sprintf("SHOW RETENTION POLICIES ON %s", name), + Command: fmt.Sprintf("SHOW RETENTION POLICIES ON %q", name), } resp, err := conn.Query(query) @@ -171,17 +175,29 @@ func readRetentionPolicies(d *schema.ResourceData, meta interface{}) error { return resp.Err } - var retentionPolicies = []map[string]string{} + retentionPolicies := []interface{}{} if resp.Results[0].Err == nil { for _, result := range resp.Results[0].Series[0].Values { - var retentionPolicy = map[string]string{ - "name": result[0].(string), + + name := result[0].(string) + if name == "autogen" { + continue + } + + replication, err := result[3].(json.Number).Int64() + if err != nil { + return err + } + + retentionPolicy := map[string]interface{}{ + "name": name, "duration": result[1].(string), - "shardGroupDuration": result[2].(string), - "replicaN": result[3].(json.Number).String(), - "default": strconv.FormatBool(result[4].(bool)), + "shardgroupduration": result[2].(string), + "replication": replication, + "default": result[4].(bool), } + retentionPolicies = append(retentionPolicies, retentionPolicy) } } @@ -194,7 +210,7 @@ func deleteDatabase(d *schema.ResourceData, meta interface{}) error { conn := meta.(*client.Client) name := d.Id() - queryStr := fmt.Sprintf("DROP DATABASE %s", quoteIdentifier(name)) + queryStr := fmt.Sprintf("DROP DATABASE %q", name) query := client.Query{ Command: queryStr, } @@ -207,8 +223,6 @@ func deleteDatabase(d *schema.ResourceData, meta interface{}) error { return resp.Err } - d.SetId("") - return nil } @@ -218,8 +232,8 @@ func updateDatabase(d *schema.ResourceData, meta interface{}) error { if d.HasChange("retention_policies") { oldRPVal, newRPVal := d.GetChange("retention_policies") - oldRPs := oldRPVal.([]interface{}) - newRPs := newRPVal.([]interface{}) + oldRPs := oldRPVal.(*schema.Set).List() + newRPs := newRPVal.(*schema.Set).List() newRPMap := make(map[string]bool) oldRPMap := make(map[string]bool) diff --git a/influxdb/resource_database_test.go b/influxdb/resource_database_test.go index 36893d3..c79d959 100644 --- a/influxdb/resource_database_test.go +++ b/influxdb/resource_database_test.go @@ -1,7 +1,6 @@ package influxdb import ( - "encoding/json" "fmt" "testing" @@ -10,50 +9,75 @@ import ( "github.com/influxdata/influxdb/client" ) -func TestAccInfluxDBDatabase(t *testing.T) { +func TestAccInfluxDBDatabase_basic(t *testing.T) { + + resourceName := "influxdb_database.test" resource.Test(t, resource.TestCase{ Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccDatabaseConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckDatabaseExists("influxdb_database.test"), - resource.TestCheckResourceAttr( - "influxdb_database.test", "name", "terraform-test", - ), + testAccCheckDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "terraform-test"), + resource.TestCheckResourceAttr(resourceName, "retention_policies.#", "0"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } -func TestAccInfluxDBDatabaseWithRPs(t *testing.T) { +func TestAccInfluxDBDatabase_retention(t *testing.T) { + + resourceName := "influxdb_database.test" resource.Test(t, resource.TestCase{ Providers: testAccProviders, Steps: []resource.TestStep{ { Config: testAccDatabaseWithRPSConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckDatabaseExists("influxdb_database.rptest"), - resource.TestCheckResourceAttr( - "influxdb_database.rptest", "name", "terraform-rp-test", - ), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "1day", "24h0m0s", "1", "", true), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "52weeks", "8736h0m0s", "1", "", false), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "1week", "168h0m0s", "1", "1h0m0s", false), + testAccCheckDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "terraform-rp-test"), + resource.TestCheckResourceAttr(resourceName, "retention_policies.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "retention_policies.*", map[string]string{ + "name": "1day", + "duration": "24h0m0s", + "default": "true", + }), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, { Config: testAccDatabaseWithRPSUpdateConfig, Check: resource.ComposeTestCheckFunc( - testAccCheckDatabaseExists("influxdb_database.rptest"), - resource.TestCheckResourceAttr( - "influxdb_database.rptest", "name", "terraform-rp-test", - ), - testAccCheckRetentionPolicyNonExisting("influxdb_database.rptest", "name", "52weeks"), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "2days", "48h0m0s", "1", "", false), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "12weeks", "2016h0m0s", "1", "", true), - testAccCheckRetentionPolicy("influxdb_database.rptest", "terraform-rp-test", "1week", "168h0m0s", "1", "1h0m0s", false), + testAccCheckDatabaseExists(resourceName), + resource.TestCheckResourceAttr("influxdb_database.test", "name", "terraform-rp-test"), + resource.TestCheckResourceAttr(resourceName, "retention_policies.#", "3"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "retention_policies.*", map[string]string{ + "name": "2days", + "duration": "48h0m0s", + "default": "false", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "retention_policies.*", map[string]string{ + "name": "12weeks", + "duration": "2016h0m0s", + "default": "true", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "retention_policies.*", map[string]string{ + "name": "1week", + "duration": "168h0m0s", + "default": "false", + "shardgroupduration": "2h0m0s", + }), ), }, }, @@ -96,87 +120,6 @@ func testAccCheckDatabaseExists(n string) resource.TestCheckFunc { } } -func testAccCheckRetentionPolicyNonExisting(n, database, policyName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No user id set") - } - - conn := testAccProvider.Meta().(*client.Client) - - query := client.Query{ - Command: fmt.Sprintf("SHOW RETENTION POLICIES ON \"%s\"", rs.Primary.Attributes["name"]), - } - - resp, err := conn.Query(query) - if err != nil { - return err - } - - if resp.Err != nil { - return resp.Err - } - - for _, result := range resp.Results[0].Series[0].Values { - if result[0].(string) == policyName { - return fmt.Errorf("Retention Policy %q on %q for %q exists", policyName, database, rs.Primary.Attributes["name"]) - } - } - - return nil - } -} - -func testAccCheckRetentionPolicy(n, database, policyName, duration, replication string, shardGroupDuration string, isDefault bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No user id set") - } - - conn := testAccProvider.Meta().(*client.Client) - - query := client.Query{ - Command: fmt.Sprintf("SHOW RETENTION POLICIES ON \"%s\"", rs.Primary.Attributes["name"]), - } - - resp, err := conn.Query(query) - if err != nil { - return err - } - - if resp.Err != nil { - return resp.Err - } - - for _, result := range resp.Results[0].Series[0].Values { - if result[0].(string) == policyName { - if result[1].(string) != duration { - return fmt.Errorf("Duration %q on retention Policy %q on %q for %q does not match", duration, policyName, database, rs.Primary.Attributes["name"]) - } else if shardGroupDuration != "" && result[2].(string) != shardGroupDuration { - return fmt.Errorf("ShardGroupDuration %q on retention Policy %q on %q for %q does not match", shardGroupDuration, policyName, database, rs.Primary.Attributes["name"]) - } else if result[3].(json.Number).String() != replication { - return fmt.Errorf("Replication %q on retention Policy %q on %q for %q does not match", replication, policyName, database, rs.Primary.Attributes["name"]) - } else if result[4].(bool) != isDefault { - return fmt.Errorf("Default %v on retention Policy %q on %q for %q does not match", isDefault, policyName, database, rs.Primary.Attributes["name"]) - } - return nil - } - } - - return fmt.Errorf("Retention Policy %q on %q for %q does not exist", policyName, database, rs.Primary.Attributes["name"]) - } -} - var testAccDatabaseConfig = ` resource "influxdb_database" "test" { @@ -186,41 +129,35 @@ resource "influxdb_database" "test" { ` var testAccDatabaseWithRPSConfig = ` -resource "influxdb_database" "rptest" { +resource "influxdb_database" "test" { name = "terraform-rp-test" retention_policies { name = "1day" - duration = "1d" + duration = "24h0m0s" default = "true" } - retention_policies { - name = "52weeks" - duration = "52w" - } - retention_policies { - name = "1week" - duration = "1w" - shardgroupduration = "1h" - } } ` var testAccDatabaseWithRPSUpdateConfig = ` -resource "influxdb_database" "rptest" { - name = "terraform-rp-test" +resource "influxdb_database" "test" { + name = "terraform-rp-test" + retention_policies { - name = "2days" - duration = "2d" + name = "2days" + duration = "48h0m0s" } + retention_policies { - name = "12weeks" - duration = "12w" - default = "true" + name = "12weeks" + duration = "2016h0m0s" + default = "true" } + retention_policies { - name = "1week" - duration = "1w" - shardgroupduration = "1h" + name = "1week" + duration = "168h0m0s" + shardgroupduration = "2h0m0s" } -} + } ` diff --git a/website/docs/r/database.html.md b/website/docs/r/database.html.md index 7c3bbb0..b249c26 100644 --- a/website/docs/r/database.html.md +++ b/website/docs/r/database.html.md @@ -17,17 +17,24 @@ resource "influxdb_database" "metrics" { name = "awesome_app" } -resource "influxdb_database" "metrics_aggregation" { - name = "testdb11" +resource "influxdb_database" "example" { + name = "example" + + retention_policies { + name = "2days" + duration = "48h0m0s" + } + retention_policies { - name = "52weeks" - duration = "52w" - default = "true" + name = "12weeks" + duration = "2016h0m0s" + default = "true" } + retention_policies { - name = "104weeks" - duration = "104w" - shardgroupduration = "3d" + name = "1week" + duration = "168h0m0s" + shardgroupduration = "2h0m0s" } } ``` @@ -42,12 +49,16 @@ The following arguments are supported: Each `retention_policies` supports the following: -* `name` - (Required) The name of the retention policy -* `duration` - (Required) The duration for retention policy, format of duration can be found at InfluxDB Documentation. -* `replication` - (Optional) Determines how many copies of data points are stored in a cluster. Not applicable for single node / Open Source version of InfluxDB. Default value of 1. -* `shardgroupduration` - (Optional) Determines how much time each shard group spans. How and why to modify can be found at InfluxDB Documentation. -* `default` - (Optional) Marks current retention policy as default. Default value is false. +* `name` - (Required) The name of the retention policy. +* `duration` - (Required) The duration for retention policy, format of duration can be found at InfluxDB Documentation. Duration has to be passed as `0h0m0s`. +* `replication` - (Optional) Determines how many copies of data points are stored in a cluster. Not applicable for single node / Open Source version of InfluxDB. Default value of `1`. +* `shardgroupduration` - (Optional) Determines how much time each shard group spans. How and why to modify can be found at InfluxDB Documentation. Defaults to `1h0m0s`. +* `default` - (Optional) Marks current retention policy as default. Default value is `false`. ## Attributes Reference -This resource exports no further attributes. +Databases can be imported using the `name`. + +```terraform +terraform import influxdb_database.example example +```