diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index bc6dbcb..4d7e836 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -33,4 +33,4 @@ jobs:
install-only: true
- name: Run GoReleaser
- run: goreleaser build --clean --skip=validate
+ run: goreleaser build --clean --skip=validate --snapshot
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..4c6bb5a
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,27 @@
+# Visit https://golangci-lint.run/ for usage documentation
+# and information on other useful linters
+issues:
+ max-per-linter: 0
+ max-same-issues: 0
+
+linters:
+ disable-all: true
+ enable:
+ - durationcheck
+ - errcheck
+ - exportloopref
+ - forcetypeassert
+ - godot
+ - gofmt
+ - gosimple
+ - ineffassign
+ - makezero
+ - misspell
+ - nilerr
+ - predeclared
+ - staticcheck
+ - tenv
+ - unconvert
+ - unparam
+ - unused
+ - vet
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100644
index 0000000..9bb0aa7
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,60 @@
+# 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:
+ extra_files:
+ - glob: 'terraform-registry-manifest.json'
+ name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
+ 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:
+ extra_files:
+ - glob: 'terraform-registry-manifest.json'
+ name_template: '{{ .ProjectName }}_{{ .Version }}_manifest.json'
+ # If you want to manually examine the release before its live, uncomment this line:
+ # draft: true
+changelog:
+ skip: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e891996
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,11 @@
+## [0.1.0] - 2024-05-01
+
+### Added:
+
+* **New Data Source:** `influxdb3_database`
+* **New Data Source:** `influxdb3_databases`
+* **New Data Source:** `influxdb3_token`
+* **New Data Source:** `influxdb3_tokens`
+
+* **New Resource:** `influxdb3_database`
+* **New Resource:** `influxdb3_token`
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ab61c7b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+default: testacc
+
+# Run acceptance tests
+.PHONY: testacc
+testacc:
+ TF_ACC=1 go test ./... -v $(TESTARGS) -timeout 120m
+
+.PHONY: lint
+lint:
+ golangci-lint run
+
+.PHONY: docs
+docs:
+ go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
+ tfplugindocs generate
diff --git a/README.md b/README.md
index 92130a8..fc2e553 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,86 @@
# terraform-provider-influxdb3
Terraform provider to manage InfluxDB V3
+
+## Requirements
+
+- [Terraform](https://developer.hashicorp.com/terraform/downloads) >= 1.0
+- [Go](https://golang.org/doc/install) >= 1.20
+
+## Building The Provider
+
+1. Clone the repository
+1. Enter the repository directory
+1. Build the provider using the Go `install` command:
+
+```shell
+go install
+```
+
+## Adding Dependencies
+
+This provider uses [Go modules](https://github.com/golang/go/wiki/Modules).
+Please see the Go documentation for the most up to date information about using Go modules.
+
+To add a new dependency `github.com/author/dependency` to your Terraform provider:
+
+```shell
+go get github.com/author/dependency
+go mod tidy
+```
+
+Then commit the changes to `go.mod` and `go.sum`.
+
+## Using the provider
+
+Add the below code to your configuration.
+
+```terraform
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+```
+
+Initialize the provider
+
+```terraform
+provider "influxdb3" {
+ account_id = "*******"
+ cluster_id = "*******"
+ token = "*******"
+ url = "https://console.influxdata.com"
+}
+```
+
+## Available functionalities
+
+### Data Sources
+
+* `influxdb3_database`
+* `influxdb3_databases`
+* `influxdb3_token`
+* `influxdb3_tokens`
+
+### Resources
+
+* `influxdb3_database`
+* `influxdb3_token`
+
+## Developing the Provider
+
+If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above).
+
+To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory.
+
+To generate or update documentation, run `make docs`.
+
+In order to run the full suite of Acceptance tests, run `make testacc`.
+
+*Note:* Acceptance tests create real resources, and often cost money to run.
+
+```shell
+make testacc
+```
diff --git a/docs/data-sources/database.md b/docs/data-sources/database.md
new file mode 100644
index 0000000..23d2f6a
--- /dev/null
+++ b/docs/data-sources/database.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_database Data Source - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Retrieves a database. Use this data source to retrieve information for a specific database.
+---
+
+# influxdb3_database (Data Source)
+
+Retrieves a database. Use this data source to retrieve information for a specific database.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters.
+
+### Read-Only
+
+- `account_id` (String) The ID of the account that the cluster belongs to.
+- `cluster_id` (String) The ID of the cluster that you want to manage.
+- `max_columns_per_table` (Number) The maximum number of columns per table for the cluster database. The default is `200`
+- `max_tables` (Number) The maximum number of tables for the cluster database. The default is `500`
+- `partition_template` (Attributes List) A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database. (see [below for nested schema](#nestedatt--partition_template))
+- `retention_period` (Number) The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.
+
+
+### Nested Schema for `partition_template`
+
+Read-Only:
+
+- `type` (String) The type of the template part.
+- `value` (String) The value of the template part.
diff --git a/docs/data-sources/databases.md b/docs/data-sources/databases.md
new file mode 100644
index 0000000..c5ab730
--- /dev/null
+++ b/docs/data-sources/databases.md
@@ -0,0 +1,41 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_databases Data Source - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Gets all databases for a cluster.
+---
+
+# influxdb3_databases (Data Source)
+
+Gets all databases for a cluster.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `databases` (Attributes List) (see [below for nested schema](#nestedatt--databases))
+
+
+### Nested Schema for `databases`
+
+Read-Only:
+
+- `account_id` (String) The ID of the account that the cluster belongs to.
+- `cluster_id` (String) The ID of the cluster that you want to manage.
+- `max_columns_per_table` (Number) The maximum number of columns per table for the cluster database. The default is `200`
+- `max_tables` (Number) The maximum number of tables for the cluster database. The default is `500`
+- `name` (String) The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters.
+- `partition_template` (Attributes List) A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database. (see [below for nested schema](#nestedatt--databases--partition_template))
+- `retention_period` (Number) The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.
+
+
+### Nested Schema for `databases.partition_template`
+
+Read-Only:
+
+- `type` (String) The type of the template part.
+- `value` (String) The value of the template part.
diff --git a/docs/data-sources/token.md b/docs/data-sources/token.md
new file mode 100644
index 0000000..d3a5d5d
--- /dev/null
+++ b/docs/data-sources/token.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_token Data Source - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Gets a database token. Use this data source to retrieve information about a database token, including the token's permissions.
+---
+
+# influxdb3_token (Data Source)
+
+Gets a database token. Use this data source to retrieve information about a database token, including the token's permissions.
+
+
+
+
+## Schema
+
+### Required
+
+- `id` (String) The ID of the database token.
+
+### Read-Only
+
+- `access_token` (String, Sensitive) The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.
+- `account_id` (String) The ID of the account that the database token belongs to.
+- `cluster_id` (String) The ID of the cluster that the database token belongs to.
+- `created_at` (String) The date and time that the database token was created. Uses RFC3339 format.
+- `description` (String) The description of the database token.
+- `permissions` (Attributes List) The list of permissions the database token allows. (see [below for nested schema](#nestedatt--permissions))
+
+
+### Nested Schema for `permissions`
+
+Read-Only:
+
+- `action` (String) The action the database token permission allows. Valid values are `read` or `write`.
+- `resource` (String) The resource the database token permission applies to. `*` refers to all databases.
diff --git a/docs/data-sources/tokens.md b/docs/data-sources/tokens.md
new file mode 100644
index 0000000..483c256
--- /dev/null
+++ b/docs/data-sources/tokens.md
@@ -0,0 +1,44 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_tokens Data Source - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Gets all database tokens for a cluster.
+---
+
+# influxdb3_tokens (Data Source)
+
+Gets all database tokens for a cluster.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `tokens` (Attributes List) (see [below for nested schema](#nestedatt--tokens))
+
+
+### Nested Schema for `tokens`
+
+Required:
+
+- `id` (String) The ID of the database token.
+
+Read-Only:
+
+- `access_token` (String, Sensitive) The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.
+- `account_id` (String) The ID of the account that the database token belongs to.
+- `cluster_id` (String) The ID of the cluster that the database token belongs to.
+- `created_at` (String) The date and time that the database token was created. Uses RFC3339 format.
+- `description` (String) The description of the database token.
+- `permissions` (Attributes List) The list of permissions the database token allows. (see [below for nested schema](#nestedatt--tokens--permissions))
+
+
+### Nested Schema for `tokens.permissions`
+
+Read-Only:
+
+- `action` (String) The action the database token permission allows. Valid values are `read` or `write`.
+- `resource` (String) The resource the database token permission applies to. `*` refers to all databases.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..131a8c5
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,52 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "InfluxDB V3 Provider"
+subcategory: ""
+description: |-
+ Use the InfluxDB V3 provider to deploy and manage resources supported by InfluxDB V3. You must configure the provider with the proper credentials before you can use it.
+---
+
+# InfluxDB V3 Provider
+
+Use the InfluxDB V3 provider to deploy and manage resources supported by InfluxDB V3. You must configure the provider with the proper credentials before you can use it.
+
+## Example Usage
+
+```terraform
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+provider "influxdb3" {}
+```
+
+## Environment Variables
+
+Credentials can be provided by using the `INFLUXDB3_ACCOUNT_ID` and `INFLUXDB3_CLUSTER_ID` and `INFLUXDB3_TOKEN` and `INFLUXDB3_URL`.
+
+### Example
+
+```terraform
+export INFLUXDB3_ACCOUNT_ID="*******"
+export INFLUXDB3_CLUSTER_ID="*******"
+export INFLUXDB3_TOKEN="*******"
+export INFLUXDB3_URL="https://console.influxdata.com"
+
+provider "influxdb3" {}
+
+terraform plan
+```
+
+
+## Schema
+
+### Optional
+
+- `account_id` (String, Sensitive) The ID of the account that the cluster belongs to
+- `cluster_id` (String, Sensitive) The ID of the cluster that you want to manage
+- `token` (String, Sensitive) The InfluxDB management token
+- `url` (String) The InfluxDB Cloud Dedicated URL
diff --git a/docs/resources/database.md b/docs/resources/database.md
new file mode 100644
index 0000000..c22163b
--- /dev/null
+++ b/docs/resources/database.md
@@ -0,0 +1,40 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_database Resource - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Creates and manages a database.
+---
+
+# influxdb3_database (Resource)
+
+Creates and manages a database.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters. **Note:** After a database is deleted, you cannot [reuse](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/databases/delete/#cannot-reuse-database-names) the same name for a new database.
+
+### Optional
+
+- `max_columns_per_table` (Number) The maximum number of columns per table for the cluster database. The default is `200`
+- `max_tables` (Number) The maximum number of tables for the cluster database. The default is `500`
+- `partition_template` (Attributes List) A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database. API does not support updating partition template, so updating this will force resource replacement. (see [below for nested schema](#nestedatt--partition_template))
+- `retention_period` (Number) The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.
+
+### Read-Only
+
+- `account_id` (String) The ID of the account that the cluster belongs to.
+- `cluster_id` (String) The ID of the cluster that you want to manage.
+
+
+### Nested Schema for `partition_template`
+
+Required:
+
+- `type` (String) The type of the template part.
+- `value` (String) The value of the template part.
diff --git a/docs/resources/token.md b/docs/resources/token.md
new file mode 100644
index 0000000..fd9ab7a
--- /dev/null
+++ b/docs/resources/token.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "influxdb3_token Resource - terraform-provider-influxdb3"
+subcategory: ""
+description: |-
+ Creates and manages a token and returns the generated database token. Use this resource to create/manage a token, which generates an database token with permissions to read or write to a specific database.
+---
+
+# influxdb3_token (Resource)
+
+Creates and manages a token and returns the generated database token. Use this resource to create/manage a token, which generates an database token with permissions to read or write to a specific database.
+
+
+
+
+## Schema
+
+### Required
+
+- `description` (String) The description of the database token.
+- `permissions` (Attributes List) The list of permissions the database token allows. (see [below for nested schema](#nestedatt--permissions))
+
+### Read-Only
+
+- `access_token` (String, Sensitive) The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.
+- `account_id` (String) The ID of the account that the database token belongs to.
+- `cluster_id` (String) The ID of the cluster that the database token belongs to.
+- `created_at` (String) The date and time that the database token was created. Uses RFC3339 format.
+- `id` (String) The ID of the database token.
+
+
+### Nested Schema for `permissions`
+
+Required:
+
+- `action` (String) The action the database token permission allows. Valid values are `read` or `write`.
+- `resource` (String) The resource the database token permission applies to. `*` refers to all databases.
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..026c42c
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,9 @@
+# Examples
+
+This directory contains examples that are mostly used for documentation, but can also be run/tested manually via the Terraform CLI.
+
+The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation.
+
+* **provider/provider.tf** example file for the provider index page
+* **data-sources/`full data source name`/data-source.tf** example file for the named data source page
+* **resources/`full resource name`/resource.tf** example file for the named data source page
diff --git a/examples/data-sources/database/data-source.tf b/examples/data-sources/database/data-source.tf
new file mode 100644
index 0000000..4808e18
--- /dev/null
+++ b/examples/data-sources/database/data-source.tf
@@ -0,0 +1,15 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+data "influxdb3_database" "signals" {
+ name = "signals"
+}
+
+output "signals_database" {
+ value = data.influxdb3_database.signals
+}
diff --git a/examples/data-sources/databases/data-source.tf b/examples/data-sources/databases/data-source.tf
new file mode 100644
index 0000000..0bd4563
--- /dev/null
+++ b/examples/data-sources/databases/data-source.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+data "influxdb3_databases" "all" {}
+
+output "all_databases" {
+ value = data.influxdb3_databases.all
+}
diff --git a/examples/data-sources/token/data-source.tf b/examples/data-sources/token/data-source.tf
new file mode 100644
index 0000000..9088032
--- /dev/null
+++ b/examples/data-sources/token/data-source.tf
@@ -0,0 +1,15 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+data "influxdb3_token" "signals_token" {
+ id = "7f7fa77d-b77e-77ba-7777-77cd077d0f7c"
+}
+
+output "signals_token" {
+ value = data.influxdb3_token.signals_token.description
+}
diff --git a/examples/data-sources/tokens/data-source.tf b/examples/data-sources/tokens/data-source.tf
new file mode 100644
index 0000000..0b6cb3b
--- /dev/null
+++ b/examples/data-sources/tokens/data-source.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+data "influxdb3_tokens" "all" {}
+
+output "all_tokens" {
+ value = data.influxdb3_tokens.all.tokens[*].id
+}
diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf
new file mode 100644
index 0000000..738d843
--- /dev/null
+++ b/examples/provider/provider.tf
@@ -0,0 +1,9 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+provider "influxdb3" {}
diff --git a/examples/resources/database/resource.tf b/examples/resources/database/resource.tf
new file mode 100644
index 0000000..17ae457
--- /dev/null
+++ b/examples/resources/database/resource.tf
@@ -0,0 +1,24 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+provider "influxdb3" {}
+
+resource "influxdb3_database" "signals" {
+ name = "signals"
+ retention_period = 604800
+ partition_template = [
+ {
+ type = "time"
+ value = "%M"
+ }
+ ]
+}
+
+output "signals_database" {
+ value = influxdb3_database.signals
+}
diff --git a/examples/resources/token/resource.tf b/examples/resources/token/resource.tf
new file mode 100644
index 0000000..29064eb
--- /dev/null
+++ b/examples/resources/token/resource.tf
@@ -0,0 +1,32 @@
+terraform {
+ required_providers {
+ influxdb3 = {
+ source = "komminarlabs/influxdb3"
+ }
+ }
+}
+
+provider "influxdb3" {}
+
+data "influxdb3_database" "signals" {
+ name = "signals"
+}
+
+resource "influxdb3_token" "signals" {
+ description = "Access signals database"
+
+ permissions = [
+ {
+ action = "read"
+ resource = data.influxdb3_database.signals.name
+ },
+ {
+ action = "write"
+ resource = data.influxdb3_database.signals.name
+ }
+ ]
+}
+
+output "sample_token" {
+ value = influxdb3_token.signals.id
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..a19c84c
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,85 @@
+module github.com/komminarlabs/terraform-provider-influxdb3
+
+go 1.22
+
+require (
+ github.com/hashicorp/terraform-plugin-docs v0.19.1
+ github.com/hashicorp/terraform-plugin-framework v1.8.0
+ github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
+ github.com/hashicorp/terraform-plugin-go v0.22.2
+ github.com/hashicorp/terraform-plugin-log v0.9.0
+ github.com/hashicorp/terraform-plugin-testing v1.7.0
+)
+
+require (
+ github.com/BurntSushi/toml v1.3.2 // indirect
+ github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver/v3 v3.2.0 // indirect
+ github.com/Masterminds/sprig/v3 v3.2.3 // indirect
+ github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
+ github.com/agext/levenshtein v1.2.2 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
+ github.com/armon/go-radix v1.0.0 // indirect
+ github.com/bgentry/speakeasy v0.1.0 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
+ github.com/cloudflare/circl v1.3.7 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/hashicorp/cli v1.1.6 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
+ github.com/hashicorp/go-checkpoint v0.5.0 // indirect
+ github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
+ github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
+ github.com/hashicorp/go-hclog v1.6.2 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/hashicorp/go-plugin v1.6.0 // indirect
+ github.com/hashicorp/go-uuid v1.0.3 // indirect
+ github.com/hashicorp/go-version v1.6.0 // indirect
+ github.com/hashicorp/hc-install v0.6.4 // indirect
+ github.com/hashicorp/hcl/v2 v2.20.0 // indirect
+ github.com/hashicorp/logutils v1.0.0 // indirect
+ github.com/hashicorp/terraform-exec v0.20.0 // indirect
+ github.com/hashicorp/terraform-json v0.21.0 // indirect
+ github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 // indirect
+ github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
+ github.com/hashicorp/terraform-svchost v0.1.1 // indirect
+ github.com/hashicorp/yamux v0.1.1 // indirect
+ github.com/huandu/xstrings v1.3.3 // indirect
+ github.com/imdario/mergo v0.3.15 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.9 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/mitchellh/go-wordwrap v1.0.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/oklog/run v1.0.0 // indirect
+ github.com/posener/complete v1.2.3 // indirect
+ github.com/shopspring/decimal v1.3.1 // indirect
+ github.com/spf13/cast v1.5.0 // indirect
+ github.com/stretchr/testify v1.8.4 // indirect
+ github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
+ github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
+ github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+ github.com/yuin/goldmark v1.7.0 // indirect
+ github.com/yuin/goldmark-meta v1.1.0 // indirect
+ github.com/zclconf/go-cty v1.14.4 // indirect
+ go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
+ golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect
+ golang.org/x/mod v0.16.0 // indirect
+ golang.org/x/net v0.23.0 // indirect
+ golang.org/x/sys v0.18.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ golang.org/x/tools v0.13.0 // indirect
+ google.golang.org/appengine v1.6.8 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
+ google.golang.org/grpc v1.63.2 // indirect
+ google.golang.org/protobuf v1.33.0 // indirect
+ gopkg.in/yaml.v2 v2.3.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5ccd6bb
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,283 @@
+dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
+github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
+github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
+github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
+github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
+github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+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/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
+github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+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=
+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/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-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
+github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+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/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=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
+github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI=
+github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs=
+github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
+github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A=
+github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+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/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
+github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0=
+github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA=
+github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
+github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
+github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo=
+github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw=
+github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U=
+github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk=
+github.com/hashicorp/terraform-plugin-docs v0.19.1 h1:XYIlGCfnUDVTyKPIHFKRDfB4INU+pyPKk6VZ/1apPIc=
+github.com/hashicorp/terraform-plugin-docs v0.19.1/go.mod h1:NPfKCSfzTtq+YCFHr2qTAMknWUxR8C4KgTbGkHULSV8=
+github.com/hashicorp/terraform-plugin-framework v1.8.0 h1:P07qy8RKLcoBkCrY2RHJer5AEvJnDuXomBgou6fD8kI=
+github.com/hashicorp/terraform-plugin-framework v1.8.0/go.mod h1:/CpTukO88PcL/62noU7cuyaSJ4Rsim+A/pa+3rUVufY=
+github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
+github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
+github.com/hashicorp/terraform-plugin-go v0.22.2 h1:5o8uveu6eZUf5J7xGPV0eY0TPXg3qpmwX9sce03Bxnc=
+github.com/hashicorp/terraform-plugin-go v0.22.2/go.mod h1:drq8Snexp9HsbFZddvyLHN6LuWHHndSQg+gV+FPkcIM=
+github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
+github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
+github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8=
+github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A=
+github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8=
+github.com/hashicorp/terraform-plugin-testing v1.7.0/go.mod h1:sbAreCleJNOCz+y5vVHV8EJkIWZKi/t4ndKiUjM9vao=
+github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
+github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
+github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
+github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
+github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
+github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
+github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+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/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+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=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
+github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
+github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
+github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
+github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
+github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
+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=
+go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
+go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U=
+golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
+google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
+google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/provider/database_data_source.go b/internal/provider/database_data_source.go
new file mode 100644
index 0000000..527473f
--- /dev/null
+++ b/internal/provider/database_data_source.go
@@ -0,0 +1,150 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &DatabaseDataSource{}
+ _ datasource.DataSourceWithConfigure = &DatabaseDataSource{}
+)
+
+// NewDatabaseDataSource is a helper function to simplify the provider implementation.
+func NewDatabaseDataSource() datasource.DataSource {
+ return &DatabaseDataSource{}
+}
+
+// DatabasesDataSource is the data source implementation.
+type DatabaseDataSource struct {
+ client influxdb3.Client
+}
+
+// Metadata returns the data source type name.
+func (d *DatabaseDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_database"
+}
+
+// Schema defines the schema for the data source.
+func (d *DatabaseDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ Description: "Retrieves a database. Use this data source to retrieve information for a specific database.",
+
+ Attributes: map[string]schema.Attribute{
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the cluster belongs to.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that you want to manage.",
+ },
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters.",
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(1, 64),
+ },
+ },
+ "max_tables": schema.Int64Attribute{
+ Computed: true,
+ Description: "The maximum number of tables for the cluster database. The default is `500`",
+ },
+ "max_columns_per_table": schema.Int64Attribute{
+ Computed: true,
+ Description: "The maximum number of columns per table for the cluster database. The default is `200`",
+ },
+ "retention_period": schema.Int64Attribute{
+ Computed: true,
+ Description: "The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.",
+ },
+ "partition_template": schema.ListNestedAttribute{
+ Computed: true,
+ MarkdownDescription: "A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database.",
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "type": schema.StringAttribute{
+ Computed: true,
+ Description: "The type of the template part.",
+ },
+ "value": schema.StringAttribute{
+ Computed: true,
+ Description: "The value of the template part.",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *DatabaseDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.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 *DatabaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var state DatabaseModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ databaseName := state.Name
+ if databaseName.IsNull() {
+ resp.Diagnostics.AddError(
+ "Name is empty",
+ "Must set name",
+ )
+ return
+ }
+
+ readDatabase, err := d.client.DatabaseAPI().GetDatabaseByName(ctx, databaseName.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Database not found",
+ err.Error(),
+ )
+ return
+ }
+
+ // Map response body to model
+ state.AccountId = types.StringValue(readDatabase.AccountId)
+ state.ClusterId = types.StringValue(readDatabase.ClusterId)
+ state.Name = types.StringValue(readDatabase.Name)
+ state.MaxTables = types.Int64Value(readDatabase.MaxTables)
+ state.MaxColumnsPerTable = types.Int64Value(readDatabase.MaxColumnsPerTable)
+ state.RetentionPeriod = types.Int64Value(readDatabase.RetentionPeriod)
+ state.PartitionTemplate = getPartitionTemplate(readDatabase.PartitionTemplate)
+
+ // Set state
+ diags := resp.State.Set(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/internal/provider/database_data_source_test.go b/internal/provider/database_data_source_test.go
new file mode 100644
index 0000000..01b1e40
--- /dev/null
+++ b/internal/provider/database_data_source_test.go
@@ -0,0 +1,33 @@
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDatabaseDataSource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Read testing
+ {
+ Config: providerConfig + testAccDatabaseDataSourceConfig("_monitoring"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("data.influxdb3_database.test", "name", "_monitoring"),
+ resource.TestCheckResourceAttr("data.influxdb3_database.test", "type", "system"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccDatabaseDataSourceConfig(name string) string {
+ return fmt.Sprintf(`
+data "influxdb3_database" "test" {
+ name = %[1]q
+}
+`, name)
+}
diff --git a/internal/provider/database_model.go b/internal/provider/database_model.go
new file mode 100644
index 0000000..191f478
--- /dev/null
+++ b/internal/provider/database_model.go
@@ -0,0 +1,31 @@
+package provider
+
+import (
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+)
+
+// DatabaseModel maps InfluxDB database schema data.
+type DatabaseModel struct {
+ AccountId types.String `tfsdk:"account_id"`
+ ClusterId types.String `tfsdk:"cluster_id"`
+ Name types.String `tfsdk:"name"`
+ MaxTables types.Int64 `tfsdk:"max_tables"`
+ MaxColumnsPerTable types.Int64 `tfsdk:"max_columns_per_table"`
+ RetentionPeriod types.Int64 `tfsdk:"retention_period"`
+ PartitionTemplate []DatabasePartitionTemplateModel `tfsdk:"partition_template"`
+}
+
+// DatabasePartitionTemplateModel maps InfluxDB database partition template schema data.
+type DatabasePartitionTemplateModel struct {
+ Type types.String `tfsdk:"type"`
+ Value types.String `tfsdk:"value"`
+}
+
+// GetAttrType returns the attribute type for the DatabasePartitionTemplateModel.
+func (d DatabasePartitionTemplateModel) GetAttrType() attr.Type {
+ return types.ObjectType{AttrTypes: map[string]attr.Type{
+ "type": types.StringType,
+ "value": types.StringType,
+ }}
+}
diff --git a/internal/provider/database_resource.go b/internal/provider/database_resource.go
new file mode 100644
index 0000000..4621b10
--- /dev/null
+++ b/internal/provider/database_resource.go
@@ -0,0 +1,300 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure provider defined types fully satisfy framework interfaces.
+var (
+ _ resource.Resource = &DatabaseResource{}
+ _ resource.ResourceWithImportState = &DatabaseResource{}
+ _ resource.ResourceWithImportState = &DatabaseResource{}
+)
+
+// NewDatabaseResource is a helper function to simplify the provider implementation.
+func NewDatabaseResource() resource.Resource {
+ return &DatabaseResource{}
+}
+
+// DatabaseResource defines the resource implementation.
+type DatabaseResource struct {
+ client influxdb3.Client
+}
+
+// Metadata returns the resource type name.
+func (r *DatabaseResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_database"
+}
+
+// Schema defines the schema for the resource.
+func (r *DatabaseResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ MarkdownDescription: "Creates and manages a database.",
+
+ Attributes: map[string]schema.Attribute{
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the cluster belongs to.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that you want to manage.",
+ },
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters. **Note:** After a database is deleted, you cannot [reuse](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/databases/delete/#cannot-reuse-database-names) the same name for a new database.",
+ Validators: []validator.String{
+ stringvalidator.LengthBetween(1, 64),
+ },
+ },
+ "max_tables": schema.Int64Attribute{
+ Computed: true,
+ Optional: true,
+ Default: int64default.StaticInt64(500),
+ Description: "The maximum number of tables for the cluster database. The default is `500`",
+ },
+ "max_columns_per_table": schema.Int64Attribute{
+ Computed: true,
+ Optional: true,
+ Default: int64default.StaticInt64(200),
+ Description: "The maximum number of columns per table for the cluster database. The default is `200`",
+ },
+ "retention_period": schema.Int64Attribute{
+ Computed: true,
+ Optional: true,
+ Default: int64default.StaticInt64(0),
+ Description: "The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.",
+ },
+ "partition_template": schema.ListNestedAttribute{
+ Computed: true,
+ Optional: true,
+ Default: listdefault.StaticValue(types.ListValueMust(DatabasePartitionTemplateModel{}.GetAttrType(), []attr.Value{})),
+ MarkdownDescription: "A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database. API does not support updating partition template, so updating this will force resource replacement.",
+ Validators: []validator.List{
+ listvalidator.UniqueValues(),
+ },
+ PlanModifiers: []planmodifier.List{
+ listplanmodifier.UseStateForUnknown(),
+ listplanmodifier.RequiresReplace(), // API does not support updating partition template
+ },
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "type": schema.StringAttribute{
+ Required: true,
+ Description: "The type of the template part.",
+ },
+ "value": schema.StringAttribute{
+ Required: true,
+ Description: "The value of the template part.",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *DatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan DatabaseModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ partitions := []influxdb3.PartitionTemplate{}
+ for _, partitionData := range plan.PartitionTemplate {
+ permission := influxdb3.PartitionTemplate{
+ Type: partitionData.Type.ValueString(),
+ Value: partitionData.Value.ValueString(),
+ }
+ partitions = append(partitions, permission)
+ }
+
+ createDatabase := influxdb3.DatabaseParams{
+ Name: plan.Name.ValueString(),
+ MaxTables: int(plan.MaxTables.ValueInt64()),
+ MaxColumnsPerTable: int(plan.MaxColumnsPerTable.ValueInt64()),
+ RetentionPeriod: plan.RetentionPeriod.ValueInt64(),
+ PartitionTemplate: partitions,
+ }
+
+ apiResponse, err := r.client.DatabaseAPI().CreateDatabase(ctx, &createDatabase)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating database",
+ "Could not create database, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Map response body to schema and populate Computed attribute values
+ plan.AccountId = types.StringValue(apiResponse.AccountId)
+ plan.ClusterId = types.StringValue(apiResponse.ClusterId)
+ plan.Name = types.StringValue(apiResponse.Name)
+ plan.MaxTables = types.Int64Value(apiResponse.MaxTables)
+ plan.MaxColumnsPerTable = types.Int64Value(apiResponse.MaxColumnsPerTable)
+ plan.RetentionPeriod = types.Int64Value(apiResponse.RetentionPeriod)
+ plan.PartitionTemplate = getPartitionTemplate(apiResponse.PartitionTemplate)
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *DatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ // Get current state
+ var state DatabaseModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Get refreshed database value from InfluxDB
+ readDatabase, err := r.client.DatabaseAPI().GetDatabaseByName(ctx, state.Name.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Database not found",
+ err.Error(),
+ )
+ return
+ }
+
+ // Overwrite items with refreshed state
+ state.AccountId = types.StringValue(readDatabase.AccountId)
+ state.ClusterId = types.StringValue(readDatabase.ClusterId)
+ state.Name = types.StringValue(readDatabase.Name)
+ state.MaxTables = types.Int64Value(readDatabase.MaxTables)
+ state.MaxColumnsPerTable = types.Int64Value(readDatabase.MaxColumnsPerTable)
+ state.RetentionPeriod = types.Int64Value(readDatabase.RetentionPeriod)
+ state.PartitionTemplate = getPartitionTemplate(readDatabase.PartitionTemplate)
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *DatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan DatabaseModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ updateDatabase := influxdb3.DatabaseParams{
+ Name: plan.Name.ValueString(),
+ MaxTables: int(plan.MaxTables.ValueInt64()),
+ MaxColumnsPerTable: int(plan.MaxColumnsPerTable.ValueInt64()),
+ RetentionPeriod: plan.RetentionPeriod.ValueInt64(),
+ }
+
+ // Update existing database
+ apiResponse, err := r.client.DatabaseAPI().UpdateDatabase(ctx, &updateDatabase)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating database",
+ "Could not update database, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Map response body to schema and populate Computed attribute values
+ plan.AccountId = types.StringValue(apiResponse.AccountId)
+ plan.ClusterId = types.StringValue(apiResponse.ClusterId)
+ plan.Name = types.StringValue(apiResponse.Name)
+ plan.MaxTables = types.Int64Value(apiResponse.MaxTables)
+ plan.MaxColumnsPerTable = types.Int64Value(apiResponse.MaxColumnsPerTable)
+ plan.RetentionPeriod = types.Int64Value(apiResponse.RetentionPeriod)
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *DatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state DatabaseModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Delete existing database
+ err := r.client.DatabaseAPI().DeleteDatabase(ctx, state.Name.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting database",
+ "Could not delete database, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *DatabaseResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ r.client = client
+}
+
+func (r *DatabaseResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("name"), req, resp)
+}
+
+func getPartitionTemplate(partitionTemplate []influxdb3.PartitionTemplate) []DatabasePartitionTemplateModel {
+ partitions := []DatabasePartitionTemplateModel{}
+ for _, partitionData := range partitionTemplate {
+ partition := DatabasePartitionTemplateModel{
+ Type: types.StringValue(partitionData.Type),
+ Value: types.StringValue(partitionData.Value),
+ }
+ partitions = append(partitions, partition)
+ }
+ return partitions
+}
diff --git a/internal/provider/database_resource_test.go b/internal/provider/database_resource_test.go
new file mode 100644
index 0000000..78aaf6d
--- /dev/null
+++ b/internal/provider/database_resource_test.go
@@ -0,0 +1,63 @@
+package provider
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDatabaseResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: providerConfig + testAccDatabaseResourceWithRetentionConfig("test", "test database", "0"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("influxdb3_database.test", "name", "test"),
+ resource.TestCheckResourceAttr("influxdb3_database.test", "description", "test database"),
+ resource.TestCheckResourceAttr("influxdb3_database.test", "retention_period", "0"),
+ ),
+ },
+ // ImportState testing
+ {
+ ResourceName: "influxdb3_database.test",
+ ImportState: true,
+ },
+ // Update and Read testing
+ {
+ Config: providerConfig + testAccDatabaseResourceConfig("test-database", "test-database"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("influxdb3_database.test", "name", "test-database"),
+ resource.TestCheckResourceAttr("influxdb3_database.test", "description", "test-database"),
+ resource.TestCheckResourceAttr("influxdb3_database.test", "retention_period", "2592000"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
+
+func testAccDatabaseResourceWithRetentionConfig(name string, description string, retention_period string) string {
+ return fmt.Sprintf(`
+resource "influxdb3_database" "test" {
+ name = %[1]q
+ description = %[2]q
+ retention_period = %[3]q
+ org_id = "`+os.Getenv("INFLUXDB_ORG_ID")+`"
+}
+`, name, description, retention_period)
+}
+
+func testAccDatabaseResourceConfig(name string, description string) string {
+ return fmt.Sprintf(`
+resource "influxdb3_database" "test" {
+ name = %[1]q
+ description = %[2]q
+ org_id = "`+os.Getenv("INFLUXDB_ORG_ID")+`"
+}
+`, name, description)
+}
diff --git a/internal/provider/databases_data_source.go b/internal/provider/databases_data_source.go
new file mode 100644
index 0000000..13753b5
--- /dev/null
+++ b/internal/provider/databases_data_source.go
@@ -0,0 +1,159 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &DatabasesDataSource{}
+ _ datasource.DataSourceWithConfigure = &DatabasesDataSource{}
+)
+
+// NewDatabasesDataSource is a helper function to simplify the provider implementation.
+func NewDatabasesDataSource() datasource.DataSource {
+ return &DatabasesDataSource{}
+}
+
+// DatabasesDataSource is the data source implementation.
+type DatabasesDataSource struct {
+ client influxdb3.Client
+}
+
+// DatabasesDataSourceModel describes the data source data model.
+type DatabasesDataSourceModel struct {
+ Databases []DatabaseModel `tfsdk:"databases"`
+}
+
+// Metadata returns the data source type name.
+func (d *DatabasesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_databases"
+}
+
+// Schema defines the schema for the data source.
+func (d *DatabasesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ Description: "Gets all databases for a cluster.",
+
+ Attributes: map[string]schema.Attribute{
+ "databases": schema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the cluster belongs to.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that you want to manage.",
+ },
+ "name": schema.StringAttribute{
+ Computed: true,
+ Description: "The name of the cluster database. The Length should be between `[ 1 .. 64 ]` characters.",
+ },
+ "max_tables": schema.Int64Attribute{
+ Computed: true,
+ Description: "The maximum number of tables for the cluster database. The default is `500`",
+ },
+ "max_columns_per_table": schema.Int64Attribute{
+ Computed: true,
+ Description: "The maximum number of columns per table for the cluster database. The default is `200`",
+ },
+ "retention_period": schema.Int64Attribute{
+ Computed: true,
+ Description: "The retention period of the cluster database in nanoseconds. The default is `0`. If the retention period is not set or is set to `0`, the database will have infinite retention.",
+ },
+ "partition_template": schema.ListNestedAttribute{
+ Computed: true,
+ MarkdownDescription: "A [template](https://docs.influxdata.com/influxdb/cloud-dedicated/admin/custom-partitions/partition-templates/) for partitioning a cluster database.",
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "type": schema.StringAttribute{
+ Computed: true,
+ Description: "The type of the template part.",
+ },
+ "value": schema.StringAttribute{
+ Computed: true,
+ Description: "The value of the template part.",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *DatabasesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.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 *DatabasesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var state DatabasesDataSourceModel
+
+ readDatabases, err := d.client.DatabaseAPI().GetDatabases(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to list databases",
+ err.Error(),
+ )
+ return
+ }
+
+ // Map response body to model
+ for _, database := range readDatabases {
+ var partitionTemplateState []DatabasePartitionTemplateModel
+ for _, permissionData := range database.PartitionTemplate {
+ partition := DatabasePartitionTemplateModel{
+ Type: types.StringValue(permissionData.Type),
+ Value: types.StringValue(permissionData.Value),
+ }
+ partitionTemplateState = append(partitionTemplateState, partition)
+ }
+
+ databaseState := DatabaseModel{
+ AccountId: types.StringValue(database.AccountId),
+ ClusterId: types.StringValue(database.ClusterId),
+ Name: types.StringValue(database.Name),
+ MaxTables: types.Int64Value(database.MaxTables),
+ MaxColumnsPerTable: types.Int64Value(database.MaxColumnsPerTable),
+ RetentionPeriod: types.Int64Value(database.RetentionPeriod),
+ PartitionTemplate: partitionTemplateState,
+ }
+ state.Databases = append(state.Databases, databaseState)
+ }
+
+ // Set state
+ diags := resp.State.Set(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/internal/provider/databases_data_source_test.go b/internal/provider/databases_data_source_test.go
new file mode 100644
index 0000000..7859495
--- /dev/null
+++ b/internal/provider/databases_data_source_test.go
@@ -0,0 +1,27 @@
+package provider
+
+import (
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccDatabasesDataSource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Read testing
+ {
+ Config: providerConfig + testAccDatabasesDataSourceConfig,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("data.influxdb3_databases.all", "databases.#", "3"),
+ ),
+ },
+ },
+ })
+}
+
+const testAccDatabasesDataSourceConfig = `
+data "influxdb3_databases" "all" {}
+`
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
new file mode 100644
index 0000000..fd1b1f5
--- /dev/null
+++ b/internal/provider/provider.go
@@ -0,0 +1,253 @@
+package provider
+
+import (
+ "context"
+ "os"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/provider"
+ "github.com/hashicorp/terraform-plugin-framework/provider/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var _ provider.Provider = &InfluxDBProvider{}
+
+// InfluxDBProvider defines the provider implementation.
+type InfluxDBProvider struct {
+ // version is set to the provider version on release, "dev" when the
+ // provider is built and ran locally, and "test" when running acceptance
+ // testing.
+ version string
+}
+
+// InfluxDBProviderModel maps provider schema data to a Go type.
+type InfluxDBProviderModel struct {
+ AccountID types.String `tfsdk:"account_id"`
+ ClusterID types.String `tfsdk:"cluster_id"`
+ Token types.String `tfsdk:"token"`
+ URL types.String `tfsdk:"url"`
+}
+
+// Metadata returns the provider type name.
+func (p *InfluxDBProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
+ resp.TypeName = "influxdb3"
+ resp.Version = p.version
+}
+
+// Schema defines the provider-level schema for configuration data.
+func (p *InfluxDBProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: "InfluxDB provider to deploy and manage resources supported by InfluxDB V3.",
+
+ Attributes: map[string]schema.Attribute{
+ "account_id": schema.StringAttribute{
+ Description: "The ID of the account that the cluster belongs to",
+ Optional: true,
+ Sensitive: true,
+ },
+ "cluster_id": schema.StringAttribute{
+ Description: "The ID of the cluster that you want to manage",
+ Optional: true,
+ Sensitive: true,
+ },
+ "token": schema.StringAttribute{
+ Description: "The InfluxDB management token",
+ Optional: true,
+ Sensitive: true,
+ },
+ "url": schema.StringAttribute{
+ Description: "The InfluxDB Cloud Dedicated URL",
+ Optional: true,
+ },
+ },
+ }
+}
+
+// Configure prepares a InfluxDB API client for data sources and resources.
+func (p *InfluxDBProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
+ // Retrieve provider data from configuration
+ var config InfluxDBProviderModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // If practitioner provided a configuration value for any of the
+ // attributes, it must be a known value.
+
+ if config.AccountID.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("account_id"),
+ "Unknown InfluxDB V3 Account ID",
+ "The provider cannot create the InfluxDB client as there is an unknown configuration value for the InfluxDB V3 Account ID. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the INFLUXDB3_ACCOUNT_ID environment variable.",
+ )
+ }
+
+ if config.ClusterID.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("cluster_id"),
+ "Unknown InfluxDB V3 Cluster ID",
+ "The provider cannot create the InfluxDB client as there is an unknown configuration value for the InfluxDB V3 Cluster ID. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the INFLUXDB3_CLUSTER_ID environment variable.",
+ )
+ }
+
+ if config.Token.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("token"),
+ "Unknown InfluxDB V3 Management Token",
+ "The provider cannot create the InfluxDB client as there is an unknown configuration value for the InfluxDB V3 Management Token. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the INFLUXDB3_TOKEN environment variable.",
+ )
+ }
+
+ if config.URL.IsUnknown() {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("url"),
+ "Unknown InfluxDB V3 Cloud Dedicated URL",
+ "The provider cannot create the InfluxDB client as there is an unknown configuration value for the InfluxDB V3 Cloud Dedicated URL. "+
+ "Either target apply the source of the value first, set the value statically in the configuration, or use the INFLUXDB3_URL environment variable.",
+ )
+ }
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Default values to environment variables, but override
+ // with Terraform configuration value if set.
+
+ accountID := os.Getenv("INFLUXDB3_ACCOUNT_ID")
+ clusterID := os.Getenv("INFLUXDB3_CLUSTER_ID")
+ token := os.Getenv("INFLUXDB3_TOKEN")
+ url := os.Getenv("INFLUXDB3_URL")
+
+ if !config.AccountID.IsNull() {
+ accountID = config.AccountID.ValueString()
+ }
+
+ if !config.ClusterID.IsNull() {
+ clusterID = config.ClusterID.ValueString()
+ }
+
+ if !config.Token.IsNull() {
+ token = config.Token.ValueString()
+ }
+
+ if !config.URL.IsNull() {
+ url = config.URL.ValueString()
+ }
+
+ // If any of the expected configurations are missing, return
+ // errors with provider-specific guidance.
+
+ if accountID == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("accountID"),
+ "Missing InfluxDB V3 Account ID",
+ "The provider cannot create the InfluxDB client as there is a missing or empty value for the InfluxDB V3 Account ID. "+
+ "Set the host value in the configuration or use the INFLUXDB3_ACCOUNT_ID environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if clusterID == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("clusterID"),
+ "Missing InfluxDB V3 Cluster ID",
+ "The provider cannot create the InfluxDB client as there is a missing or empty value for the InfluxDB V3 Cluster ID. "+
+ "Set the host value in the configuration or use the INFLUXDB3_CLUSTER_ID environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if token == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("token"),
+ "Missing InfluxDB Management Token",
+ "The provider cannot create the InfluxDB client as there is a missing or empty value for the InfluxDB V3 Management Token. "+
+ "Set the host value in the configuration or use the INFLUXDB3_TOKEN environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if url == "" {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("url"),
+ "Missing InfluxDB Cloud Dedicated URL",
+ "The provider cannot create the InfluxDB client as there is a missing or empty value for the InfluxDB V3 Cloud Dedicated URL. "+
+ "Set the host value in the configuration or use the INFLUXDB3_URL environment variable. "+
+ "If either is already set, ensure the value is not empty.",
+ )
+ }
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ ctx = tflog.SetField(ctx, "INFLUXDB3_ACCOUNT_ID", accountID)
+ ctx = tflog.SetField(ctx, "INFLUXDB3_CLUSTER_ID", clusterID)
+ ctx = tflog.SetField(ctx, "INFLUXDB3_TOKEN", token)
+ ctx = tflog.SetField(ctx, "INFLUXDB3_URL", url)
+ ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "INFLUXDB3_TOKEN")
+
+ tflog.Debug(ctx, "Creating InfluxDB V3 client")
+
+ // Create a new InfluxDB client using the configuration values
+ client, err := influxdb3.New(&influxdb3.ClientConfig{
+ AccountID: accountID,
+ ClusterID: clusterID,
+ Host: url,
+ Token: token,
+ })
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Unable to Create InfluxDB V3 Client",
+ "An unexpected error occurred when creating the InfluxDB V3 client. "+
+ "If the error is not clear, please contact the provider developers.\n\n"+
+ "InfluxDB V3 Client Error: "+err.Error(),
+ )
+ return
+ }
+
+ // Make the InfluxDB client available during DataSource and Resource
+ // type Configure methods.
+ resp.DataSourceData = client
+ resp.ResourceData = client
+
+ tflog.Info(ctx, "Configured InfluxDB V3 client", map[string]any{"success": true})
+}
+
+// Resources defines the resources implemented in the provider.
+func (p *InfluxDBProvider) Resources(ctx context.Context) []func() resource.Resource {
+ return []func() resource.Resource{
+ NewTokenResource,
+ NewDatabaseResource,
+ }
+}
+
+// DataSources defines the data sources implemented in the provider.
+func (p *InfluxDBProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
+ return []func() datasource.DataSource{
+ NewTokenDataSource,
+ NewTokensDataSource,
+ NewDatabaseDataSource,
+ NewDatabasesDataSource,
+ }
+}
+
+// New is a helper function to simplify provider server and testing implementation.
+func New(version string) func() provider.Provider {
+ return func() provider.Provider {
+ return &InfluxDBProvider{
+ version: version,
+ }
+ }
+}
diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go
new file mode 100644
index 0000000..b29fe95
--- /dev/null
+++ b/internal/provider/provider_test.go
@@ -0,0 +1,40 @@
+package provider
+
+import (
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+ "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+)
+
+const (
+ // providerConfig is a shared configuration to combine with the actual
+ // test configuration so the HashiCups client is properly configured.
+ providerConfig = `
+ provider "influxdb3" {}
+ `
+)
+
+// testAccProtoV6ProviderFactories are used to instantiate a provider during
+// acceptance testing. The factory function will be invoked for every Terraform
+// CLI command executed to create a provider server to which the CLI can
+// reattach.
+var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
+ "influxdb3": providerserver.NewProtocol6WithError(New("test")()),
+}
+
+func testAccPreCheck(t *testing.T) {
+ if v := os.Getenv("INFLUXDB3_ACCOUNT_ID"); v == "" {
+ t.Fatal("INFLUXDB3_ACCOUNT_ID must be set for acceptance tests")
+ }
+ if v := os.Getenv("INFLUXDB3_CLUSTER_ID"); v == "" {
+ t.Fatal("INFLUXDB3_CLUSTER_ID must be set for acceptance tests")
+ }
+ if v := os.Getenv("INFLUXDB3_TOKEN"); v == "" {
+ t.Fatal("INFLUXDB3_TOKEN must be set for acceptance tests")
+ }
+ if v := os.Getenv("INFLUXDB3_URL"); v == "" {
+ t.Fatal("INFLUXDB3_URL must be set for acceptance tests")
+ }
+}
diff --git a/internal/provider/token_data_source.go b/internal/provider/token_data_source.go
new file mode 100644
index 0000000..6ebbf72
--- /dev/null
+++ b/internal/provider/token_data_source.go
@@ -0,0 +1,148 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &TokenDataSource{}
+ _ datasource.DataSourceWithConfigure = &TokenDataSource{}
+)
+
+// NewTokenDataSource is a helper function to simplify the provider implementation.
+func NewTokenDataSource() datasource.DataSource {
+ return &TokenDataSource{}
+}
+
+// TokensDataSource is the data source implementation.
+type TokenDataSource struct {
+ client influxdb3.Client
+}
+
+// Metadata returns the data source type name.
+func (d *TokenDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_token"
+}
+
+// Schema defines the schema for the data source.
+func (d *TokenDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ Description: "Gets a database token. Use this data source to retrieve information about a database token, including the token's permissions.",
+
+ Attributes: map[string]schema.Attribute{
+ "access_token": schema.StringAttribute{
+ Computed: true,
+ Description: "The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.",
+ Sensitive: true,
+ },
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the database token belongs to.",
+ },
+ "created_at": schema.StringAttribute{
+ Computed: true,
+ Description: "The date and time that the database token was created. Uses RFC3339 format.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that the database token belongs to.",
+ },
+ "description": schema.StringAttribute{
+ Computed: true,
+ Description: "The description of the database token.",
+ },
+ "id": schema.StringAttribute{
+ Required: true,
+ Description: "The ID of the database token.",
+ },
+ "permissions": schema.ListNestedAttribute{
+ Computed: true,
+ Description: "The list of permissions the database token allows.",
+ Validators: []validator.List{
+ listvalidator.UniqueValues(),
+ },
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "action": schema.StringAttribute{
+ Computed: true,
+ Description: "The action the database token permission allows. Valid values are `read` or `write`.",
+ Validators: []validator.String{
+ stringvalidator.OneOf([]string{"read", "write"}...),
+ },
+ },
+ "resource": schema.StringAttribute{
+ Computed: true,
+ Description: "The resource the database token permission applies to. `*` refers to all databases.",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *TokenDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.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 *TokenDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var state TokenModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ readToken, err := d.client.TokenAPI().GetTokenByID(ctx, state.Id.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error getting Tokens",
+ err.Error(),
+ )
+ return
+ }
+
+ // Overwrite items with refreshed state
+ state.AccessToken = types.StringValue(readToken.AccessToken)
+ state.AccountId = types.StringValue(readToken.AccountId)
+ state.CreatedAt = types.StringValue(readToken.CreatedAt)
+ state.ClusterId = types.StringValue(readToken.ClusterId)
+ state.Description = types.StringValue(readToken.Description)
+ state.Id = types.StringValue(readToken.Id)
+ state.Permissions = getPermissions(readToken.Permissions)
+
+ // Set state
+ diags := resp.State.Set(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/internal/provider/token_data_source_test.go b/internal/provider/token_data_source_test.go
new file mode 100644
index 0000000..2942022
--- /dev/null
+++ b/internal/provider/token_data_source_test.go
@@ -0,0 +1,60 @@
+package provider
+
+import (
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccTokenDataSource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Read testing
+ {
+ Config: providerConfig + testAccTokenDataSourceConfig(),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("data.influxdb3_token.test", "permissions.#", "2"),
+ resource.TestCheckResourceAttr("data.influxdb3_token.test", "description", "Access test bucket"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccTokenDataSourceConfig() string {
+ return `
+resource "influxdb3_bucket" "test" {
+ name = "test"
+ org_id = "` + os.Getenv("INFLUXDB_ORG_ID") + `"
+ }
+
+resource "influxdb3_token" "test" {
+ org_id = "` + os.Getenv("INFLUXDB_ORG_ID") + `"
+ description = "Access test bucket"
+
+ permissions = [{
+ action = "read"
+ resource = {
+ id = influxdb3_bucket.test.id
+ org_id = "` + os.Getenv("INFLUXDB_ORG_ID") + `"
+ type = "buckets"
+ }
+ },
+ {
+ action = "write"
+ resource = {
+ id = influxdb3_bucket.test.id
+ org_id = "` + os.Getenv("INFLUXDB_ORG_ID") + `"
+ type = "buckets"
+ }
+ }]
+ }
+
+ data "influxdb3_token" "test" {
+ id = influxdb3_token.test.id
+ }
+`
+}
diff --git a/internal/provider/token_model.go b/internal/provider/token_model.go
new file mode 100644
index 0000000..819d26d
--- /dev/null
+++ b/internal/provider/token_model.go
@@ -0,0 +1,20 @@
+package provider
+
+import "github.com/hashicorp/terraform-plugin-framework/types"
+
+// TokenModel maps InfluxDB database token schema data.
+type TokenModel struct {
+ AccessToken types.String `tfsdk:"access_token"`
+ AccountId types.String `tfsdk:"account_id"`
+ CreatedAt types.String `tfsdk:"created_at"`
+ ClusterId types.String `tfsdk:"cluster_id"`
+ Description types.String `tfsdk:"description"`
+ Id types.String `tfsdk:"id"`
+ Permissions []TokenPermissionModel `tfsdk:"permissions"`
+}
+
+// TokenPermissionModel maps InfluxDB database token permission schema data.
+type TokenPermissionModel struct {
+ Action types.String `tfsdk:"action"`
+ Resource types.String `tfsdk:"resource"`
+}
diff --git a/internal/provider/token_resource.go b/internal/provider/token_resource.go
new file mode 100644
index 0000000..78a701d
--- /dev/null
+++ b/internal/provider/token_resource.go
@@ -0,0 +1,296 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure provider defined types fully satisfy framework interfaces.
+var (
+ _ resource.Resource = &TokenResource{}
+ _ resource.ResourceWithImportState = &TokenResource{}
+ _ resource.ResourceWithImportState = &TokenResource{}
+)
+
+// NewTokenResource is a helper function to simplify the provider implementation.
+func NewTokenResource() resource.Resource {
+ return &TokenResource{}
+}
+
+// TokenResource defines the resource implementation.
+type TokenResource struct {
+ client influxdb3.Client
+}
+
+// Metadata returns the resource type name.
+func (r *TokenResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_token"
+}
+
+// Schema defines the schema for the resource.
+func (r *TokenResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ Description: "Creates and manages a token and returns the generated database token. Use this resource to create/manage a token, which generates an database token with permissions to read or write to a specific database.",
+
+ Attributes: map[string]schema.Attribute{
+ "access_token": schema.StringAttribute{
+ Computed: true,
+ Description: "The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.",
+ Sensitive: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the database token belongs to.",
+ },
+ "created_at": schema.StringAttribute{
+ Computed: true,
+ Description: "The date and time that the database token was created. Uses RFC3339 format.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that the database token belongs to.",
+ },
+ "description": schema.StringAttribute{
+ Required: true,
+ Description: "The description of the database token.",
+ },
+ "id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the database token.",
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "permissions": schema.ListNestedAttribute{
+ Required: true,
+ Description: "The list of permissions the database token allows.",
+ Validators: []validator.List{
+ listvalidator.UniqueValues(),
+ },
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "action": schema.StringAttribute{
+ Required: true,
+ Description: "The action the database token permission allows. Valid values are `read` or `write`.",
+ Validators: []validator.String{
+ stringvalidator.OneOf([]string{"read", "write"}...),
+ },
+ },
+ "resource": schema.StringAttribute{
+ Required: true,
+ Description: "The resource the database token permission applies to. `*` refers to all databases.",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Create creates the resource and sets the initial Terraform state.
+func (r *TokenResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan TokenModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ var permissions []influxdb3.Permission
+ for _, permissionData := range plan.Permissions {
+ permission := influxdb3.Permission{
+ Action: permissionData.Action.ValueString(),
+ Resource: permissionData.Resource.ValueString(),
+ }
+ permissions = append(permissions, permission)
+ }
+
+ createToken := influxdb3.TokenParams{
+ Description: plan.Description.ValueString(),
+ Permissions: permissions,
+ }
+
+ apiResponse, err := r.client.TokenAPI().CreateToken(ctx, &createToken)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating token",
+ "Could not create token, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Map response body to schema and populate Computed attribute values
+ plan.AccessToken = types.StringValue(apiResponse.AccessToken)
+ plan.AccountId = types.StringValue(apiResponse.AccountId)
+ plan.CreatedAt = types.StringValue(apiResponse.CreatedAt)
+ plan.ClusterId = types.StringValue(apiResponse.ClusterId)
+ plan.Description = types.StringValue(apiResponse.Description)
+ plan.Id = types.StringValue(apiResponse.Id)
+ plan.Permissions = getPermissions(apiResponse.Permissions)
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (r *TokenResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ // Get current state
+ var state TokenModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Get refreshed token value from InfluxDB
+ readToken, err := r.client.TokenAPI().GetTokenByID(ctx, state.Id.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error getting Tokens",
+ err.Error(),
+ )
+ return
+ }
+
+ // Overwrite items with refreshed state
+ state.AccountId = types.StringValue(readToken.AccountId)
+ state.CreatedAt = types.StringValue(readToken.CreatedAt)
+ state.ClusterId = types.StringValue(readToken.ClusterId)
+ state.Description = types.StringValue(readToken.Description)
+ state.Id = types.StringValue(readToken.Id)
+ state.Permissions = getPermissions(readToken.Permissions)
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Update updates the resource and sets the updated Terraform state on success.
+func (r *TokenResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan TokenModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Generate API request body from plan
+ var permissions []influxdb3.Permission
+ for _, permissionData := range plan.Permissions {
+ permission := influxdb3.Permission{
+ Action: permissionData.Action.ValueString(),
+ Resource: permissionData.Resource.ValueString(),
+ }
+ permissions = append(permissions, permission)
+ }
+
+ updateToken := influxdb3.TokenParams{
+ Description: plan.Description.ValueString(),
+ Permissions: permissions,
+ }
+
+ // Update existing token
+ apiResponse, err := r.client.TokenAPI().UpdateToken(ctx, plan.Id.ValueString(), &updateToken)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating token",
+ "Could not update token, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ // Overwrite items with refreshed state
+ plan.AccountId = types.StringValue(apiResponse.AccountId)
+ plan.CreatedAt = types.StringValue(apiResponse.CreatedAt)
+ plan.ClusterId = types.StringValue(apiResponse.ClusterId)
+ plan.Description = types.StringValue(apiResponse.Description)
+ plan.Id = types.StringValue(apiResponse.Id)
+ plan.Permissions = getPermissions(apiResponse.Permissions)
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+// Delete deletes the resource and removes the Terraform state on success.
+func (r *TokenResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state TokenModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Delete existing token
+ err := r.client.TokenAPI().DeleteToken(ctx, state.Id.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting token",
+ "Could not delete token, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
+
+// Configure adds the provider configured client to the resource.
+func (r *TokenResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = client
+}
+
+func (r *TokenResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func getPermissions(permissions []influxdb3.Permission) []TokenPermissionModel {
+ permissionsState := []TokenPermissionModel{}
+ for _, permission := range permissions {
+ permissionState := TokenPermissionModel{
+ Action: types.StringValue(permission.Action),
+ Resource: types.StringValue(permission.Resource),
+ }
+ permissionsState = append(permissionsState, permissionState)
+ }
+ return permissionsState
+}
diff --git a/internal/provider/token_resource_test.go b/internal/provider/token_resource_test.go
new file mode 100644
index 0000000..07ee1c8
--- /dev/null
+++ b/internal/provider/token_resource_test.go
@@ -0,0 +1,69 @@
+package provider
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccTokenResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: providerConfig + testAccTokenResourceConfig("Access test bucket"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("influxdb3_token.test", "permissions.#", "2"),
+ resource.TestCheckResourceAttr("influxdb3_token.test", "description", "Access test bucket"),
+ ),
+ },
+ // ImportState testing
+ {
+ ResourceName: "influxdb3_token.test",
+ ImportState: true,
+ },
+ // Update and Read testing
+ {
+ Config: providerConfig + testAccTokenResourceConfig("RW access test bucket"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("influxdb3_token.test", "permissions.#", "2"),
+ resource.TestCheckResourceAttr("influxdb3_token.test", "description", "RW access test bucket"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
+
+func testAccTokenResourceConfig(description string) string {
+ return fmt.Sprintf(`
+resource "influxdb3_bucket" "test" {
+ name = "test"
+ org_id = "`+os.Getenv("INFLUXDB_ORG_ID")+`"
+ }
+
+resource "influxdb3_token" "test" {
+ org_id = "`+os.Getenv("INFLUXDB_ORG_ID")+`"
+ description = %[1]q
+
+ permissions = [{
+ action = "read"
+ resource = {
+ id = influxdb3_bucket.test.id
+ type = "buckets"
+ }
+ },
+ {
+ action = "write"
+ resource = {
+ id = influxdb3_bucket.test.id
+ type = "buckets"
+ }
+ }]
+ }
+`, description)
+}
diff --git a/internal/provider/tokens_data_source.go b/internal/provider/tokens_data_source.go
new file mode 100644
index 0000000..373483b
--- /dev/null
+++ b/internal/provider/tokens_data_source.go
@@ -0,0 +1,173 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/sdk/influxdb3"
+)
+
+// Ensure the implementation satisfies the expected interfaces.
+var (
+ _ datasource.DataSource = &TokensDataSource{}
+ _ datasource.DataSourceWithConfigure = &TokensDataSource{}
+)
+
+// NewTokensDataSource is a helper function to simplify the provider implementation.
+func NewTokensDataSource() datasource.DataSource {
+ return &TokensDataSource{}
+}
+
+// TokensDataSource is the data source implementation.
+type TokensDataSource struct {
+ client influxdb3.Client
+}
+
+// TokensDataSourceModel describes the data source data model.
+type TokensDataSourceModel struct {
+ Tokens []TokenModel `tfsdk:"tokens"`
+}
+
+// Metadata returns the data source type name.
+func (d *TokensDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_tokens"
+}
+
+// Schema defines the schema for the data source.
+func (d *TokensDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ Description: "Gets all database tokens for a cluster.",
+
+ Attributes: map[string]schema.Attribute{
+ "tokens": schema.ListNestedAttribute{
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "access_token": schema.StringAttribute{
+ Computed: true,
+ Description: "The access token that can be used to authenticate query and write requests to the cluster. The access token is never stored by InfluxDB and is only returned once when the token is created. If the access token is lost, a new token must be created.",
+ Sensitive: true,
+ },
+ "account_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the account that the database token belongs to.",
+ },
+ "created_at": schema.StringAttribute{
+ Computed: true,
+ Description: "The date and time that the database token was created. Uses RFC3339 format.",
+ },
+ "cluster_id": schema.StringAttribute{
+ Computed: true,
+ Description: "The ID of the cluster that the database token belongs to.",
+ },
+ "description": schema.StringAttribute{
+ Computed: true,
+ Description: "The description of the database token.",
+ },
+ "id": schema.StringAttribute{
+ Required: true,
+ Description: "The ID of the database token.",
+ },
+ "permissions": schema.ListNestedAttribute{
+ Computed: true,
+ Description: "The list of permissions the database token allows.",
+ Validators: []validator.List{
+ listvalidator.UniqueValues(),
+ },
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "action": schema.StringAttribute{
+ Computed: true,
+ Description: "The action the database token permission allows. Valid values are `read` or `write`.",
+ Validators: []validator.String{
+ stringvalidator.OneOf([]string{"read", "write"}...),
+ },
+ },
+ "resource": schema.StringAttribute{
+ Computed: true,
+ Description: "The resource the database token permission applies to. `*` refers to all databases.",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *TokensDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(influxdb3.Client)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected influxdb3.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 *TokensDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var state TokensDataSourceModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ readTokens, err := d.client.TokenAPI().GetTokens(ctx)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error getting Tokenss",
+ err.Error(),
+ )
+ return
+ }
+
+ // Map response body to model
+ for _, token := range readTokens {
+ var permissions []TokenPermissionModel
+ for _, permissionData := range token.Permissions {
+ permissionState := TokenPermissionModel{
+ Action: types.StringValue(permissionData.Action),
+ Resource: types.StringValue(permissionData.Resource),
+ }
+ permissions = append(permissions, permissionState)
+ }
+
+ tokenState := TokenModel{
+ AccessToken: types.StringValue(token.AccessToken),
+ AccountId: types.StringValue(token.AccountId),
+ CreatedAt: types.StringValue(token.CreatedAt),
+ ClusterId: types.StringValue(token.ClusterId),
+ Description: types.StringValue(token.Description),
+ Id: types.StringValue(token.Id),
+ Permissions: permissions,
+ }
+ state.Tokens = append(state.Tokens, tokenState)
+ }
+
+ // Set state
+ diags := resp.State.Set(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/internal/provider/tokens_data_source_test.go b/internal/provider/tokens_data_source_test.go
new file mode 100644
index 0000000..3365a06
--- /dev/null
+++ b/internal/provider/tokens_data_source_test.go
@@ -0,0 +1,27 @@
+package provider
+
+import (
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccTokensDataSource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Read testing
+ {
+ Config: providerConfig + testAccTokensDataSourceConfig,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("data.influxdb3_tokens.all", "tokens.#", "1"),
+ ),
+ },
+ },
+ })
+}
+
+const testAccTokensDataSourceConfig = `
+data "influxdb3_tokens" "all" {}
+`
diff --git a/internal/sdk/README.md b/internal/sdk/README.md
new file mode 100644
index 0000000..a16cbe5
--- /dev/null
+++ b/internal/sdk/README.md
@@ -0,0 +1,3 @@
+# InfluxDB V3 Management SDK go
+
+This directory temporarily holds the SDK for InfluxDB V3 management API until it is implemented in the official [influxdb3-go](https://github.com/InfluxCommunity/influxdb3-go) SDK.
diff --git a/internal/sdk/influxdb3/client.go b/internal/sdk/influxdb3/client.go
new file mode 100644
index 0000000..1576a85
--- /dev/null
+++ b/internal/sdk/influxdb3/client.go
@@ -0,0 +1,84 @@
+package influxdb3
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+ "time"
+)
+
+type Client interface {
+ DatabaseAPI() DatabaseAPI
+ TokenAPI() TokenAPI
+ Close()
+}
+
+type client struct {
+ config ClientConfig
+ authorization string
+ apiURL *url.URL
+}
+
+func New(config *ClientConfig) (Client, error) {
+ var err error
+ c := &client{config: *config}
+
+ hostAddress := config.Host
+ if !strings.HasSuffix(config.Host, "/") {
+ hostAddress = config.Host + "/"
+ }
+
+ c.apiURL, err = url.Parse(hostAddress)
+ if err != nil {
+ return nil, fmt.Errorf("parsing host URL: %w", err)
+ }
+
+ c.apiURL.Path = path.Join(c.apiURL.Path, fmt.Sprintf("/api/v0/accounts/%s/clusters/%s", c.config.AccountID, c.config.ClusterID)) + "/"
+ c.authorization = "Bearer " + c.config.Token
+
+ if c.config.HTTPClient == nil {
+ c.config.HTTPClient = &http.Client{Timeout: 10 * time.Second}
+ }
+ return c, nil
+}
+
+func (c *client) Close() {
+ c.config.HTTPClient.CloseIdleConnections()
+}
+
+func (c *client) DatabaseAPI() DatabaseAPI {
+ return c
+}
+
+func (c *client) TokenAPI() TokenAPI {
+ return c
+}
+
+func (c *client) makeAPICall(httpMethod, path string, body io.Reader) ([]byte, error) {
+ req, err := http.NewRequest(httpMethod, c.apiURL.String()+path, body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Accept", "application/json")
+ req.Header.Set("Authorization", c.authorization)
+ req.Header.Set("Content-Type", "application/json")
+ resp, err := c.config.HTTPClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+ }
+
+ respBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("error reading response body: %w", err)
+ }
+ return respBody, nil
+}
diff --git a/internal/sdk/influxdb3/config.go b/internal/sdk/influxdb3/config.go
new file mode 100644
index 0000000..519fa28
--- /dev/null
+++ b/internal/sdk/influxdb3/config.go
@@ -0,0 +1,11 @@
+package influxdb3
+
+import "net/http"
+
+type ClientConfig struct {
+ AccountID string
+ ClusterID string
+ Host string
+ HTTPClient *http.Client
+ Token string
+}
diff --git a/internal/sdk/influxdb3/database.go b/internal/sdk/influxdb3/database.go
new file mode 100644
index 0000000..119d020
--- /dev/null
+++ b/internal/sdk/influxdb3/database.go
@@ -0,0 +1,127 @@
+package influxdb3
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "path"
+)
+
+type DatabaseAPI interface {
+ CreateDatabase(ctx context.Context, databaseParams *DatabaseParams) (*database, error)
+ DeleteDatabase(ctx context.Context, databaseName string) error
+ GetDatabases(ctx context.Context) ([]database, error)
+ GetDatabaseByName(ctx context.Context, databaseName string) (*database, error)
+ UpdateDatabase(ctx context.Context, databaseParams *DatabaseParams) (*database, error)
+}
+
+const (
+ DatabaseAPIPath = "databases"
+)
+
+type database struct {
+ AccountId string `json:"accountId"`
+ ClusterId string `json:"clusterId"`
+ Name string `json:"name"`
+ MaxTables int64 `json:"maxTables"`
+ MaxColumnsPerTable int64 `json:"maxColumnsPerTable"`
+ RetentionPeriod int64 `json:"retentionPeriod"`
+ PartitionTemplate []PartitionTemplate `json:"partitionTemplate"`
+}
+
+type DatabaseParams struct {
+ Name string `json:"name"`
+ MaxTables int `json:"maxTables"`
+ MaxColumnsPerTable int `json:"maxColumnsPerTable"`
+ RetentionPeriod int64 `json:"retentionPeriod"`
+ PartitionTemplate []PartitionTemplate `json:"partitionTemplate"`
+}
+
+type PartitionTemplate struct {
+ Type string `json:"type"`
+ Value string `json:"value"`
+}
+
+func (c *client) CreateDatabase(ctx context.Context, databaseParams *DatabaseParams) (*database, error) {
+ reqBody, err := json.Marshal(databaseParams)
+ if err != nil {
+ return nil, err
+ }
+
+ respBody, err := c.makeAPICall(http.MethodPost, DatabaseAPIPath, bytes.NewBuffer(reqBody))
+ if err != nil {
+ if err.Error() == "unexpected status code: 400" {
+ return nil, fmt.Errorf("bad request, check your input")
+ }
+ return nil, err
+ }
+
+ database := database{}
+ err = json.Unmarshal(respBody, &database)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+
+ return &database, nil
+}
+
+func (c *client) DeleteDatabase(ctx context.Context, databaseName string) error {
+ _, err := c.makeAPICall(http.MethodDelete, path.Join(DatabaseAPIPath, databaseName), nil)
+ if err != nil {
+ if err.Error() == "unexpected status code: 204" {
+ return nil
+ }
+ return fmt.Errorf("error deleting database: %w", err)
+ }
+ return nil
+}
+
+func (c *client) GetDatabases(ctx context.Context) ([]database, error) {
+ databases := []database{}
+ body, err := c.makeAPICall(http.MethodGet, DatabaseAPIPath, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ err = json.Unmarshal(body, &databases)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+ return databases, nil
+}
+
+func (c *client) GetDatabaseByName(ctx context.Context, databaseName string) (*database, error) {
+ databases, err := c.GetDatabases(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, db := range databases {
+ if db.Name == databaseName {
+ return &db, nil
+ }
+ }
+ return nil, fmt.Errorf("error getting database: %s not found", databaseName)
+}
+
+func (c *client) UpdateDatabase(ctx context.Context, databaseParams *DatabaseParams) (*database, error) {
+ reqBody, err := json.Marshal(databaseParams)
+ if err != nil {
+ return nil, err
+ }
+
+ respBody, err := c.makeAPICall(http.MethodPatch, path.Join(DatabaseAPIPath, databaseParams.Name), bytes.NewBuffer(reqBody))
+ if err != nil {
+ return nil, err
+ }
+
+ database := database{}
+ err = json.Unmarshal(respBody, &database)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+
+ return &database, nil
+}
diff --git a/internal/sdk/influxdb3/token.go b/internal/sdk/influxdb3/token.go
new file mode 100644
index 0000000..ae72759
--- /dev/null
+++ b/internal/sdk/influxdb3/token.go
@@ -0,0 +1,124 @@
+package influxdb3
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "path"
+)
+
+type TokenAPI interface {
+ CreateToken(ctx context.Context, tokenParams *TokenParams) (*token, error)
+ DeleteToken(ctx context.Context, tokenID string) error
+ GetTokens(ctx context.Context) ([]token, error)
+ GetTokenByID(ctx context.Context, tokenID string) (*token, error)
+ UpdateToken(ctx context.Context, tokenID string, tokenParams *TokenParams) (*token, error)
+}
+
+const (
+ TokenAPIPath = "tokens"
+)
+
+type TokenParams struct {
+ Description string `json:"description"`
+ Permissions []Permission `json:"permissions"`
+}
+
+type token struct {
+ AccessToken string `json:"accessToken"`
+ AccountId string `json:"accountId"`
+ CreatedAt string `json:"createdAt"`
+ ClusterId string `json:"clusterId"`
+ Description string `json:"description"`
+ Id string `json:"id"`
+ Permissions []Permission `json:"permissions"`
+}
+
+type Permission struct {
+ Action string `json:"action"`
+ Resource string `json:"resource"`
+}
+
+func (c *client) CreateToken(ctx context.Context, tokenParams *TokenParams) (*token, error) {
+ reqBody, err := json.Marshal(tokenParams)
+ if err != nil {
+ return nil, err
+ }
+
+ respBody, err := c.makeAPICall(http.MethodPost, TokenAPIPath, bytes.NewBuffer(reqBody))
+ if err != nil {
+ return nil, err
+ }
+
+ token := token{}
+ err = json.Unmarshal(respBody, &token)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+
+ return &token, nil
+}
+
+func (c *client) DeleteToken(ctx context.Context, tokenID string) error {
+ _, err := c.makeAPICall(http.MethodDelete, path.Join(TokenAPIPath, tokenID), nil)
+ if err != nil {
+ if err.Error() == "unexpected status code: 204" {
+ return nil
+ }
+ return fmt.Errorf("error deleting token: %w", err)
+ }
+ return nil
+}
+
+func (c *client) GetTokens(ctx context.Context) ([]token, error) {
+ tokens := []token{}
+ body, err := c.makeAPICall(http.MethodGet, TokenAPIPath, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ err = json.Unmarshal(body, &tokens)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+ return tokens, nil
+}
+
+func (c *client) GetTokenByID(ctx context.Context, tokenID string) (*token, error) {
+ token := token{}
+ body, err := c.makeAPICall(http.MethodGet, path.Join(TokenAPIPath, tokenID), nil)
+ if err != nil {
+ if err.Error() == "unexpected status code: 404" {
+ return nil, fmt.Errorf("error getting token: %s not found", tokenID)
+ }
+ return nil, err
+ }
+
+ err = json.Unmarshal(body, &token)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+ return &token, nil
+}
+
+func (c *client) UpdateToken(ctx context.Context, tokenID string, tokenParams *TokenParams) (*token, error) {
+ reqBody, err := json.Marshal(tokenParams)
+ if err != nil {
+ return nil, err
+ }
+
+ respBody, err := c.makeAPICall(http.MethodPatch, path.Join(TokenAPIPath, tokenID), bytes.NewBuffer(reqBody))
+ if err != nil {
+ return nil, err
+ }
+
+ token := token{}
+ err = json.Unmarshal(respBody, &token)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling JSON: %w", err)
+ }
+
+ return &token, nil
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..3344175
--- /dev/null
+++ b/main.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "log"
+
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+
+ "github.com/komminarlabs/terraform-provider-influxdb3/internal/provider"
+)
+
+var (
+ // these will be set by the goreleaser configuration
+ // to appropriate values for the compiled binary.
+ version string = "dev"
+
+ // goreleaser can pass other information to the main package, such as the specific commit
+ // https://goreleaser.com/cookbooks/using-main.version/
+)
+
+func main() {
+ var debug bool
+
+ flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
+ flag.Parse()
+
+ opts := providerserver.ServeOpts{
+ Address: "registry.terraform.io/komminarlabs/influxdb3",
+ Debug: debug,
+ }
+
+ err := providerserver.Serve(context.Background(), provider.New(version), opts)
+
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+}
diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl
new file mode 100644
index 0000000..f56976e
--- /dev/null
+++ b/templates/index.md.tmpl
@@ -0,0 +1,34 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "InfluxDB V3 Provider"
+subcategory: ""
+description: |-
+ Use the InfluxDB V3 provider to deploy and manage resources supported by InfluxDB V3. You must configure the provider with the proper credentials before you can use it.
+---
+
+# InfluxDB V3 Provider
+
+Use the InfluxDB V3 provider to deploy and manage resources supported by InfluxDB V3. You must configure the provider with the proper credentials before you can use it.
+
+## Example Usage
+
+{{tffile "examples/provider/provider.tf"}}
+
+## Environment Variables
+
+Credentials can be provided by using the `INFLUXDB3_ACCOUNT_ID` and `INFLUXDB3_CLUSTER_ID` and `INFLUXDB3_TOKEN` and `INFLUXDB3_URL`.
+
+### Example
+
+```terraform
+export INFLUXDB3_ACCOUNT_ID="*******"
+export INFLUXDB3_CLUSTER_ID="*******"
+export INFLUXDB3_TOKEN="*******"
+export INFLUXDB3_URL="https://console.influxdata.com"
+
+provider "influxdb3" {}
+
+terraform plan
+```
+
+{{ .SchemaMarkdown | trimspace }}
diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json
new file mode 100644
index 0000000..fec2a56
--- /dev/null
+++ b/terraform-registry-manifest.json
@@ -0,0 +1,6 @@
+{
+ "version": 1,
+ "metadata": {
+ "protocol_versions": ["6.0"]
+ }
+}
diff --git a/tools/tools.go b/tools/tools.go
new file mode 100644
index 0000000..dee4dca
--- /dev/null
+++ b/tools/tools.go
@@ -0,0 +1,9 @@
+//go:build tools
+// +build tools
+
+package tools
+
+import (
+ // Documentation generation
+ _ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
+)