From 6041108f2fc86f1dbf46be9cf29c5d6fa8f2f854 Mon Sep 17 00:00:00 2001 From: momer Date: Tue, 14 May 2024 14:19:14 -0500 Subject: [PATCH] Add bonsai_release (GetBySlug) and bonsai_releases (List) data sources. --- .pre-commit-config.yaml | 2 +- .task/checksum/docs | 2 +- docs/data-sources/release.md | 33 +++++++ docs/data-sources/releases.md | 38 ++++++++ examples/complete/complete.tf | 16 ++++ .../bonsai_release/data-source.tf | 3 + .../bonsai_releases/data-source.tf | 1 + internal/provider/provider.go | 3 + internal/release/data_source.go | 93 ++++++++++++++++++ internal/release/data_source_list.go | 94 +++++++++++++++++++ internal/release/data_source_list_test.go | 27 ++++++ internal/release/data_source_test.go | 32 +++++++ internal/release/release.go | 61 ++++++++++++ internal/release/release_test.go | 20 ++++ 14 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 docs/data-sources/release.md create mode 100644 docs/data-sources/releases.md create mode 100644 examples/data-sources/bonsai_release/data-source.tf create mode 100644 examples/data-sources/bonsai_releases/data-source.tf create mode 100644 internal/release/data_source.go create mode 100644 internal/release/data_source_list.go create mode 100644 internal/release/data_source_list_test.go create mode 100644 internal/release/data_source_test.go create mode 100644 internal/release/release.go create mode 100644 internal/release/release_test.go diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad6c045..28f2c96 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: name: task-docs description: Run the docs task for documentation generation entry: task docs - types: [ go ] + types_or: [ go, terraform, markdown ] language: system require_serial: true pass_filenames: false \ No newline at end of file diff --git a/.task/checksum/docs b/.task/checksum/docs index 147458f..4f1cd95 100644 --- a/.task/checksum/docs +++ b/.task/checksum/docs @@ -1 +1 @@ -f7f04e4db1b11ccfe67e14765ab91cb3 +759ba68929e54e055e8873c1a30cef6a diff --git a/docs/data-sources/release.md b/docs/data-sources/release.md new file mode 100644 index 0000000..5a3771c --- /dev/null +++ b/docs/data-sources/release.md @@ -0,0 +1,33 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "bonsai_release Data Source - terraform-provider-bonsai" +subcategory: "" +description: |- + A Release is a version of Elasticsearch available to your account. +--- + +# bonsai_release (Data Source) + +A Release is a version of Elasticsearch available to your account. + +## Example Usage + +```terraform +data "bonsai_release" "get_by_slug" { + slug = "elasticsearch-6.4.2" +} +``` + + +## Schema + +### Optional + +- `slug` (String) The machine-readable name for the deployment. + +### Read-Only + +- `multitenant` (Boolean) Whether the release is available on multitenant deployments. +- `name` (String) The name for the release. +- `service_type` (String) The service type of the deployment - for example, "elasticsearch". +- `version` (String) The version of the release. diff --git a/docs/data-sources/releases.md b/docs/data-sources/releases.md new file mode 100644 index 0000000..a26fbd5 --- /dev/null +++ b/docs/data-sources/releases.md @@ -0,0 +1,38 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "bonsai_releases Data Source - terraform-provider-bonsai" +subcategory: "" +description: |- + A list of all available releases on your account. +--- + +# bonsai_releases (Data Source) + +A list of all **available** releases on your account. + +## Example Usage + +```terraform +data "bonsai_releases" "list" {} +``` + + +## Schema + +### Read-Only + +- `releases` (Attributes List) A Release is a version of Elasticsearch available to your account. (see [below for nested schema](#nestedatt--releases)) + + +### Nested Schema for `releases` + +Optional: + +- `slug` (String) The machine-readable name for the deployment. + +Read-Only: + +- `multitenant` (Boolean) Whether the release is available on multitenant deployments. +- `name` (String) The name for the release. +- `service_type` (String) The service type of the deployment - for example, "elasticsearch". +- `version` (String) The version of the release. diff --git a/examples/complete/complete.tf b/examples/complete/complete.tf index f8b68ae..7aa9702 100644 --- a/examples/complete/complete.tf +++ b/examples/complete/complete.tf @@ -6,6 +6,7 @@ terraform { } } +// Bonsai Spaces provider "bonsai" {} data "bonsai_space" "get_by_path" { @@ -21,3 +22,18 @@ output "bonsai_space" { output "bonsai_spaces" { value = data.bonsai_spaces.list } + +// Bonsai Releases +data "bonsai_release" "get_by_slug" { + slug = "elasticsearch-6.4.2" +} + +data "bonsai_releases" "list" {} + +output "bonsai_release" { + value = data.bonsai_release.get_by_slug +} + +output "bonsai_releases" { + value = data.bonsai_releases.list +} diff --git a/examples/data-sources/bonsai_release/data-source.tf b/examples/data-sources/bonsai_release/data-source.tf new file mode 100644 index 0000000..37696e0 --- /dev/null +++ b/examples/data-sources/bonsai_release/data-source.tf @@ -0,0 +1,3 @@ +data "bonsai_release" "get_by_slug" { + slug = "elasticsearch-6.4.2" +} \ No newline at end of file diff --git a/examples/data-sources/bonsai_releases/data-source.tf b/examples/data-sources/bonsai_releases/data-source.tf new file mode 100644 index 0000000..5abd0aa --- /dev/null +++ b/examples/data-sources/bonsai_releases/data-source.tf @@ -0,0 +1 @@ +data "bonsai_releases" "list" {} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c72b2a1..ef3fd18 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/omc/bonsai-api-go/v2/bonsai" + "github.com/omc/terraform-provider-bonsai/internal/release" "github.com/omc/terraform-provider-bonsai/internal/space" ) @@ -82,6 +83,8 @@ func (p *bonsaiProvider) Resources(ctx context.Context) []func() resource.Resour func (p *bonsaiProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ + release.NewDataSource, + release.NewListDataSource, space.NewDataSource, space.NewListDataSource, } diff --git a/internal/release/data_source.go b/internal/release/data_source.go new file mode 100644 index 0000000..23fdcbf --- /dev/null +++ b/internal/release/data_source.go @@ -0,0 +1,93 @@ +package release + +import ( + "context" + "fmt" + + tfds "github.com/hashicorp/terraform-plugin-framework/datasource" + dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/omc/bonsai-api-go/v2/bonsai" +) + +// dataSource is the data source implementation. +type dataSource struct { + client *bonsai.ReleaseClient +} + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ tfds.DataSource = &dataSource{} +) + +// NewDataSource is a helper function to simplify the provider implementation. +func NewDataSource() tfds.DataSource { + return &dataSource{} +} + +// Metadata returns the data source type name. +func (d *dataSource) Metadata(_ context.Context, req tfds.MetadataRequest, resp *tfds.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_release" +} + +// Schema defines the schema for the data source. +func (d *dataSource) Schema(_ context.Context, _ tfds.SchemaRequest, resp *tfds.SchemaResponse) { + resp.Schema = dschema.Schema{ + Attributes: schemaAttributes(), + MarkdownDescription: dataSourceMarkdownDescription, + } +} + +// Read refreshes the Terraform state with the latest data. +func (d *dataSource) Read(ctx context.Context, req tfds.ReadRequest, resp *tfds.ReadResponse) { + var state model + + // Fetch requested Slug from context + resp.Diagnostics.Append(req.Config.GetAttribute(ctx, path.Root("slug"), &state.Slug)...) + if resp.Diagnostics.HasError() { + return + } + + if state.Slug.IsNull() { + resp.Diagnostics.AddError( + fmt.Sprintf("Unable to Read Bonsai Release (%s) from the Bonsai API", state.Slug.ValueString()), + "expected 'slug' option to be set", + ) + return + } + + s, err := d.client.Release.GetBySlug(ctx, state.Slug.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Unable to Read Bonsai Release (%s) from the Bonsai API", state.Slug.ValueString()), + err.Error(), + ) + return + } + + // Map response body to model + state = convert(s) + + // Set state + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) +} + +// Configure adds the provider configured client to the data source. +func (d *dataSource) Configure(_ context.Context, req tfds.ConfigureRequest, resp *tfds.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*bonsai.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *bonsai.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = &client.Release +} diff --git a/internal/release/data_source_list.go b/internal/release/data_source_list.go new file mode 100644 index 0000000..b6f7c50 --- /dev/null +++ b/internal/release/data_source_list.go @@ -0,0 +1,94 @@ +package release + +import ( + "context" + "fmt" + + tfds "github.com/hashicorp/terraform-plugin-framework/datasource" + dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/omc/bonsai-api-go/v2/bonsai" +) + +// listDataSourceModel maps the data source schema data. +type listDataSourceModel struct { + Releases []model `tfsdk:"releases"` +} + +// listDataSource is the data source implementation. +type listDataSource struct { + client *bonsai.ReleaseClient +} + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ tfds.DataSource = &listDataSource{} +) + +// NewListDataSource is a helper function to simplify the provider implementation. +func NewListDataSource() tfds.DataSource { + return &listDataSource{} +} + +// Metadata returns the data source type name. +func (d *listDataSource) Metadata(_ context.Context, req tfds.MetadataRequest, resp *tfds.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_releases" +} + +// Schema defines the schema for the data source. +func (d *listDataSource) Schema(_ context.Context, _ tfds.SchemaRequest, resp *tfds.SchemaResponse) { + resp.Schema = dschema.Schema{ + MarkdownDescription: listDataSourceMarkdownDescription, + Attributes: map[string]dschema.Attribute{ + "releases": dschema.ListNestedAttribute{ + MarkdownDescription: dataSourceMarkdownDescription, + Computed: true, + NestedObject: dschema.NestedAttributeObject{ + Attributes: schemaAttributes(), + }, + }, + }, + } +} + +// Read refreshes the Terraform state with the latest data. +func (d *listDataSource) Read(ctx context.Context, req tfds.ReadRequest, resp *tfds.ReadResponse) { + var state listDataSourceModel + + releases, err := d.client.Release.All(ctx) + if err != nil { + resp.Diagnostics.AddError( + "Unable to Read Bonsai Releases from the Bonsai API", + err.Error(), + ) + return + } + + // Map response body to model + for _, s := range releases { + releaseState := convert(s) + state.Releases = append(state.Releases, releaseState) + } + + // Set state + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) +} + +// Configure adds the provider configured client to the data source. +func (d *listDataSource) Configure(_ context.Context, req tfds.ConfigureRequest, resp *tfds.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*bonsai.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *bonsai.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = &client.Release +} diff --git a/internal/release/data_source_list_test.go b/internal/release/data_source_list_test.go new file mode 100644 index 0000000..a9fc457 --- /dev/null +++ b/internal/release/data_source_list_test.go @@ -0,0 +1,27 @@ +package release_test + +import ( + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func (s *ReleaseTestSuite) TestRelease_ListDataSource() { + resource.Test(s.T(), resource.TestCase{ + ProtoV6ProviderFactories: s.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + data "bonsai_releases" "list" {} + + output "bonsai_releases" { + value = [for s in data.bonsai_releases.list.releases : s.slug] + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.bonsai_releases.list", "releases.#", "12"), + resource.TestCheckResourceAttr("data.bonsai_releases.list", "releases.0.%", "5"), + resource.TestCheckResourceAttr("data.bonsai_releases.list", "releases.0.slug", "elasticsearch-2.4.0"), + ), + }, + }, + }) +} diff --git a/internal/release/data_source_test.go b/internal/release/data_source_test.go new file mode 100644 index 0000000..61fe07a --- /dev/null +++ b/internal/release/data_source_test.go @@ -0,0 +1,32 @@ +package release_test + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func (s *ReleaseTestSuite) TestRelease_DataSource() { + const releaseSlug = "elasticsearch-5.6.16" + resource.Test(s.T(), resource.TestCase{ + ProtoV6ProviderFactories: s.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + data "bonsai_release" "get_by_slug" { + slug = "%s" + } + + output "bonsai_release_slug" { + value = data.bonsai_release.get_by_slug.slug + } + `, releaseSlug), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.bonsai_release.get_by_slug", "slug"), + resource.TestCheckResourceAttr("data.bonsai_release.get_by_slug", "slug", releaseSlug), + resource.TestCheckOutput("bonsai_release_slug", releaseSlug), + ), + }, + }, + }) +} diff --git a/internal/release/release.go b/internal/release/release.go new file mode 100644 index 0000000..cc501b1 --- /dev/null +++ b/internal/release/release.go @@ -0,0 +1,61 @@ +package release + +import ( + dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/omc/bonsai-api-go/v2/bonsai" +) + +const ( + dataSourceMarkdownDescription = "A Release is a version of Elasticsearch " + + "available to your account." + listDataSourceMarkdownDescription = "A list of all **available** releases on " + + "your account." +) + +// model maps releases schema data. +type model struct { + Name types.String `tfsdk:"name"` + Slug types.String `tfsdk:"slug"` + ServiceType types.String `tfsdk:"service_type"` + Version types.String `tfsdk:"version"` + MultiTenant types.Bool `tfsdk:"multitenant"` +} + +func convert(r bonsai.Release) model { + return model{ + Name: types.StringValue(r.Name), + Slug: types.StringValue(r.Slug), + ServiceType: types.StringValue(r.ServiceType), + Version: types.StringValue(r.Version), + MultiTenant: types.BoolValue(r.MultiTenant), + } +} + +func schemaAttributes() map[string]dschema.Attribute { + return map[string]dschema.Attribute{ + "name": dschema.StringAttribute{ + MarkdownDescription: "The name for the release.", + Computed: true, + }, + "slug": dschema.StringAttribute{ + MarkdownDescription: "The machine-readable name for the deployment.", + Computed: true, + Optional: true, + }, + "service_type": dschema.StringAttribute{ + MarkdownDescription: "The service type of the deployment - for " + + "example, \"elasticsearch\".", + Computed: true, + }, + "version": dschema.StringAttribute{ + MarkdownDescription: "The version of the release.", + Computed: true, + }, + "multitenant": dschema.BoolAttribute{ + Computed: true, + MarkdownDescription: "Whether the release is available on " + + "multitenant deployments.", + }, + } +} diff --git a/internal/release/release_test.go b/internal/release/release_test.go new file mode 100644 index 0000000..c65463e --- /dev/null +++ b/internal/release/release_test.go @@ -0,0 +1,20 @@ +package release_test + +import ( + "testing" + + "github.com/omc/terraform-provider-bonsai/internal/test" + "github.com/stretchr/testify/suite" +) + +type ReleaseTestSuite struct { + *test.ProviderTestSuite +} + +func TestReleaseTestSuite(t *testing.T) { + suite.Run(t, &ReleaseTestSuite{ProviderTestSuite: &test.ProviderTestSuite{}}) +} + +func (s *ReleaseTestSuite) SetupSuite() { + suite.SetupAllSuite(s.ProviderTestSuite).SetupSuite() +}