diff --git a/docs/data-sources/bigAnimal_projects.md b/docs/data-sources/projects.md similarity index 88% rename from docs/data-sources/bigAnimal_projects.md rename to docs/data-sources/projects.md index 28ed521c..d6c8d269 100644 --- a/docs/data-sources/bigAnimal_projects.md +++ b/docs/data-sources/projects.md @@ -1,12 +1,12 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "bigAnimal_projects Data Source - terraform-provider-biganimal" +page_title: "biganimal_projects Data Source - terraform-provider-biganimal" subcategory: "" description: |- The projects data source shows the BigAnimal Projects. --- -# bigAnimal_projects (Data Source) +# biganimal_projects (Data Source) The projects data source shows the BigAnimal Projects. @@ -36,7 +36,7 @@ output "projects" { Required: -- `name` (String) Project Name of the project. +- `project_name` (String) Project Name of the project. Optional: diff --git a/docs/index.md b/docs/index.md index d00f0270..f660252d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,10 +35,7 @@ Credentials can be provided by using the `BA_BEARER_TOKEN` and optionally `BA_AP ## Schema -### Required - -- `ba_bearer_token` (String) BigAnimal Bearer Token - ### Optional - `ba_api_uri` (String) BigAnimal API URL +- `ba_bearer_token` (String) BigAnimal Bearer Token diff --git a/docs/resources/project.md b/docs/resources/project.md index 13190448..09a29b0a 100644 --- a/docs/resources/project.md +++ b/docs/resources/project.md @@ -57,32 +57,18 @@ output "project" { - `project_name` (String) Project Name of the project. -### Optional - -- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts)) - ### Read-Only -- `cloud_providers` (Set of Object) Enabled Cloud Providers. (see [below for nested schema](#nestedatt--cloud_providers)) +- `cloud_providers` (Attributes Set) Enabled Cloud Providers. (see [below for nested schema](#nestedatt--cloud_providers)) - `cluster_count` (Number) User Count of the project. -- `id` (String) The ID of this resource. -- `project_id` (String) Project ID of the project. +- `id` (String) Project ID of the project. +- `project_id` (String, Deprecated) Project ID of the project. - `user_count` (Number) User Count of the project. - -### Nested Schema for `timeouts` - -Optional: - -- `create` (String) -- `delete` (String) -- `update` (String) - - ### Nested Schema for `cloud_providers` Read-Only: -- `cloud_provider_id` (String) -- `cloud_provider_name` (String) +- `cloud_provider_id` (String) Cloud Provider ID. +- `cloud_provider_name` (String) Cloud Provider Name. diff --git a/main.go b/main.go index 5e267ad0..b22ec4ea 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/terraform-plugin-mux/tf5to6server" "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "log" ) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 86d074a8..cd3476e1 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -9,6 +9,7 @@ import ( schema2 "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + diagv2 "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "os" ) @@ -30,6 +31,7 @@ func init() { // Set descriptions to support markdown syntax, this will be used in document generation // and the language server. schema.DescriptionKind = schema.StringMarkdown + } func New(version string) func() *schema.Provider { @@ -38,14 +40,14 @@ func New(version string) func() *schema.Provider { Schema: map[string]*schema.Schema{ "ba_bearer_token": { Type: schema.TypeString, + Description: "BigAnimal Bearer Token", Sensitive: false, - Required: true, - DefaultFunc: schema.EnvDefaultFunc("BA_BEARER_TOKEN", nil), + Optional: true, }, "ba_api_uri": { Type: schema.TypeString, + Description: "BigAnimal API URL", Optional: true, - DefaultFunc: schema.EnvDefaultFunc("BA_API_URI", "https://portal.biganimal.com/api/v3"), }, }, DataSourcesMap: map[string]*schema.Resource{ @@ -83,6 +85,27 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema // api.BuildAPI(meta).RegionClient() userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", version) + diags := diag.Diagnostics{} + + if ba_bearer_token == "" { + ba_bearer_token = os.Getenv("BA_BEARER_TOKEN") + } + + if ba_bearer_token == "" { + diags = append(diags, diagv2.Diagnostic{ + Severity: diagv2.Error, + Summary: "Unable to find ba_nearer_token", + Detail: "ba_nearer_token cannot be an empty string"}) + return nil, diags + } + + if ba_api_uri == "" { + ba_api_uri = os.Getenv("BA_API_URI") + } + if ba_api_uri == "" { + ba_api_uri = "https://portal.biganimal.com/api/v3" + } + return api.NewAPI(ba_bearer_token, ba_api_uri, userAgent), nil } } @@ -93,8 +116,8 @@ type bigAnimalProvider struct { // providerData can be used to store data from the Terraform configuration. type providerData struct { - BaBearerToken string `tfsdk:"ba_bearer_token"` - BaAPIUri string `tfsdk:"ba_api_uri"` + BaBearerToken *string `tfsdk:"ba_bearer_token"` + BaAPIUri *string `tfsdk:"ba_api_uri"` } func NewProvider(version string) func() provider.Provider { @@ -121,8 +144,8 @@ func (b bigAnimalProvider) Configure(ctx context.Context, req provider.Configure } var token = os.Getenv("BA_BEARER_TOKEN") - if data.BaBearerToken != "" { - token = data.BaBearerToken + if data.BaBearerToken != nil { + token = *data.BaBearerToken } if token == "" { @@ -137,8 +160,8 @@ func (b bigAnimalProvider) Configure(ctx context.Context, req provider.Configure if os.Getenv("BA_API_URI") != "" { host = os.Getenv("BA_API_URI") } - if data.BaAPIUri != "" { - host = data.BaAPIUri + if data.BaAPIUri != nil { + host = *data.BaAPIUri } userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", b.version) diff --git a/pkg/provider/resource_proejct.go b/pkg/provider/resource_proejct.go index a7185181..c5638279 100644 --- a/pkg/provider/resource_proejct.go +++ b/pkg/provider/resource_proejct.go @@ -3,9 +3,14 @@ package provider import ( "context" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" ) type projectResource struct { @@ -18,42 +23,71 @@ func (p projectResource) Metadata(ctx context.Context, req resource.MetadataRequ func (p projectResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "The project resource is used to manage projects in your organization. " + + MarkdownDescription: "The project resource is used to manage projects in your organization. " + "See [Managing projects](https://www.enterprisedb.com/docs/biganimal/latest/administering_cluster/projects/) for more details.\n\n" + "Newly created projects are not automatically connected to your cloud providers. " + "Please visit [Connecting your cloud](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/02_connecting_to_your_cloud/) for more details.", Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "Project ID of the project.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, "project_id": schema.StringAttribute{ - Description: "Project ID of the project.", - Computed: true, + MarkdownDescription: "Project ID of the project.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + DeprecationMessage: "The usage of 'project_id' is no longer recommended and has been deprecated. We suggest using 'id' instead.", }, "project_name": schema.StringAttribute{ - Description: "Project Name of the project.", - Required: true, + MarkdownDescription: "Project Name of the project.", + Required: true, }, "user_count": schema.Int64Attribute{ - Description: "User Count of the project.", - Computed: true, + MarkdownDescription: "User Count of the project.", + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, }, "cluster_count": schema.Int64Attribute{ - Description: "User Count of the project.", - Computed: true, + MarkdownDescription: "User Count of the project.", + Computed: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, }, // We don't have a mechanism to automate the csp connection right now // So, the `cloud_providers` value is computed only. "cloud_providers": schema.SetNestedAttribute{ - Description: "Enabled Cloud Providers.", - Computed: true, + MarkdownDescription: "Enabled Cloud Providers.", + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, NestedObject: schema.NestedAttributeObject{ + PlanModifiers: []planmodifier.Object{ + objectplanmodifier.UseStateForUnknown(), + }, Attributes: map[string]schema.Attribute{ "cloud_provider_id": schema.StringAttribute{ - Description: "Cloud Provider ID.", - Computed: true, + MarkdownDescription: "Cloud Provider ID.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "cloud_provider_name": schema.StringAttribute{ - Description: "Cloud Provider Name.", - Computed: true, + MarkdownDescription: "Cloud Provider Name.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, }, }, @@ -71,16 +105,30 @@ func (r *projectResource) Configure(_ context.Context, req resource.ConfigureReq r.client = req.ProviderData.(*api.API) } +type cloudProvider struct { + CloudProviderId string `tfsdk:"cloud_provider_id"` + CloudProviderName string `tfsdk:"cloud_provider_name"` +} + +type Project struct { + ID types.String `tfsdk:"id"` + ProjectID types.String `tfsdk:"project_id"` + ProjectName types.String `tfsdk:"project_name"` + UserCount types.Int64 `tfsdk:"user_count"` + ClusterCount types.Int64 `tfsdk:"cluster_count"` + CloudProviders []cloudProvider `tfsdk:"cloud_providers"` +} + // Create creates the resource and sets the initial Terraform state. func (p projectResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan models.Project - diags := req.Plan.Get(ctx, &plan) + var config Project + diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - projectId, err := p.client.ProjectClient().Create(ctx, plan.ProjectName) + projectId, err := p.client.ProjectClient().Create(ctx, config.ProjectName.ValueString()) if err != nil { resp.Diagnostics.AddError("Error creating project", "Could not create project, unexpected error: "+err.Error()) return @@ -92,7 +140,14 @@ func (p projectResource) Create(ctx context.Context, req resource.CreateRequest, return } - diags = resp.State.Set(ctx, project) + config.ID = types.StringValue(project.ProjectId) + config.ProjectID = types.StringValue(project.ProjectId) + config.ProjectName = types.StringValue(project.ProjectName) + config.UserCount = types.Int64Value(int64(project.UserCount)) + config.ClusterCount = types.Int64Value(int64(project.ClusterCount)) + config.CloudProviders = []cloudProvider{} + + diags = resp.State.Set(ctx, &config) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -101,20 +156,32 @@ func (p projectResource) Create(ctx context.Context, req resource.CreateRequest, // Read refreshes the Terraform state with the latest data. func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state models.Project + var state Project diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - project, err := p.client.ProjectClient().Read(ctx, state.ProjectId) + project, err := p.client.ProjectClient().Read(ctx, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) return } - diags = resp.State.Set(ctx, project) + state.ProjectName = types.StringValue(project.ProjectName) + state.UserCount = types.Int64Value(int64(project.UserCount)) + state.ClusterCount = types.Int64Value(int64(project.ClusterCount)) + if cps := project.CloudProviders; cps != nil { + for _, provider := range *cps { + state.CloudProviders = append(state.CloudProviders, cloudProvider{ + CloudProviderId: provider.CloudProviderId, + CloudProviderName: provider.CloudProviderName, + }) + } + } + + diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -123,20 +190,20 @@ func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, res // Update updates the resource and sets the updated Terraform state on success. func (p projectResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan models.Project + var plan Project diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - _, err := p.client.ProjectClient().Update(ctx, plan.ProjectId, plan.ProjectName) + _, err := p.client.ProjectClient().Update(ctx, plan.ID.ValueString(), plan.ProjectName.ValueString()) if err != nil { resp.Diagnostics.AddError("Error updating project", "Could not update project, unexpected error: "+err.Error()) return } - diags = resp.State.Set(ctx, plan) + diags = resp.State.Set(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return @@ -146,14 +213,14 @@ func (p projectResource) Update(ctx context.Context, req resource.UpdateRequest, // Delete deletes the resource and removes the Terraform state on success. func (p projectResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // Retrieve values from state - var state models.Project + var state Project diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - if err := p.client.ProjectClient().Delete(ctx, state.ProjectId); err != nil { + if err := p.client.ProjectClient().Delete(ctx, state.ID.ValueString()); err != nil { resp.Diagnostics.AddError("Error deleting project", "Could not delete project, unexpected error: "+err.Error()) return } diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go index 4844507a..3c44ffa1 100644 --- a/pkg/provider/resource_proejct_test.go +++ b/pkg/provider/resource_proejct_test.go @@ -22,7 +22,6 @@ func TestAccBiganimalProjectResource(t *testing.T) { resource.TestCheckResourceAttrSet("biganimal_project.test_project", "project_id"), resource.TestCheckResourceAttrSet("biganimal_project.test_project", "user_count"), resource.TestCheckResourceAttrSet("biganimal_project.test_project", "cluster_count"), - resource.TestCheckResourceAttrSet("biganimal_project.test_project", "cloud_providers"), ), }, {