From 0c47437636fc444b823f02b807b4bbe1f24e7cf3 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:18:22 +0800 Subject: [PATCH 01/49] refactor: rewrite datasource_projects with terraform-plugin-framework --- docs/data-sources/bigAnimal_projects.md | 57 +++++++++++++ docs/data-sources/projects.md | 52 ------------ docs/index.md | 4 +- go.mod | 4 +- go.sum | 4 + main.go | 50 +++++++++-- pkg/models/project.go | 10 +-- pkg/provider/data_source_projects.go | 106 +++++++++++++----------- pkg/provider/provider.go | 106 ++++++++++++++++++++---- 9 files changed, 264 insertions(+), 129 deletions(-) create mode 100644 docs/data-sources/bigAnimal_projects.md delete mode 100644 docs/data-sources/projects.md diff --git a/docs/data-sources/bigAnimal_projects.md b/docs/data-sources/bigAnimal_projects.md new file mode 100644 index 00000000..28ed521c --- /dev/null +++ b/docs/data-sources/bigAnimal_projects.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "bigAnimal_projects Data Source - terraform-provider-biganimal" +subcategory: "" +description: |- + The projects data source shows the BigAnimal Projects. +--- + +# bigAnimal_projects (Data Source) + +The projects data source shows the BigAnimal Projects. + +## Example Usage + +```terraform +data "biganimal_projects" "this" {} + +output "projects" { + value = data.biganimal_projects.this.projects +} +``` + + +## Schema + +### Optional + +- `query` (String) Query to filter project list. + +### Read-Only + +- `projects` (Attributes Set) List of the organization's projects. (see [below for nested schema](#nestedatt--projects)) + + +### Nested Schema for `projects` + +Required: + +- `name` (String) Project Name of the project. + +Optional: + +- `cloud_providers` (Attributes Set) Enabled Cloud Providers. (see [below for nested schema](#nestedatt--projects--cloud_providers)) + +Read-Only: + +- `cluster_count` (Number) User Count of the project. +- `project_id` (String) Project ID of the project. +- `user_count` (Number) User Count of the project. + + +### Nested Schema for `projects.cloud_providers` + +Read-Only: + +- `cloud_provider_id` (String) Cloud Provider ID. +- `cloud_provider_name` (String) Cloud Provider Name. diff --git a/docs/data-sources/projects.md b/docs/data-sources/projects.md deleted file mode 100644 index 77ef0979..00000000 --- a/docs/data-sources/projects.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "biganimal_projects Data Source - terraform-provider-biganimal" -subcategory: "" -description: |- - The projects data source shows the BigAnimal Projects. ---- - -# biganimal_projects (Data Source) - -The projects data source shows the BigAnimal Projects. - -## Example Usage - -```terraform -data "biganimal_projects" "this" {} - -output "projects" { - value = data.biganimal_projects.this.projects -} -``` - - -## Schema - -### Optional - -- `query` (String) Query to filter project list. - -### Read-Only - -- `id` (String) The ID of this resource. -- `projects` (Set of Object) List of the organization's projects. (see [below for nested schema](#nestedatt--projects)) - - -### Nested Schema for `projects` - -Read-Only: - -- `cloud_providers` (Set of Object) (see [below for nested schema](#nestedobjatt--projects--cloud_providers)) -- `cluster_count` (Number) -- `name` (String) -- `project_id` (String) -- `user_count` (Number) - - -### Nested Schema for `projects.cloud_providers` - -Read-Only: - -- `cloud_provider_id` (String) -- `cloud_provider_name` (String) diff --git a/docs/index.md b/docs/index.md index 22705608..d00f0270 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,8 +37,8 @@ Credentials can be provided by using the `BA_BEARER_TOKEN` and optionally `BA_AP ### Required -- `ba_bearer_token` (String) +- `ba_bearer_token` (String) BigAnimal Bearer Token ### Optional -- `ba_api_uri` (String) +- `ba_api_uri` (String) BigAnimal API URL diff --git a/go.mod b/go.mod index 240437f8..e8c54491 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,10 @@ require ( github.com/h2non/gock v1.2.0 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb + github.com/hashicorp/terraform-plugin-framework v1.2.0 + github.com/hashicorp/terraform-plugin-go v0.15.0 github.com/hashicorp/terraform-plugin-log v0.9.0 + github.com/hashicorp/terraform-plugin-mux v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 github.com/hashicorp/terraform-plugin-testing v1.2.0 github.com/joho/godotenv v1.5.1 @@ -48,7 +51,6 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.18.1 // indirect github.com/hashicorp/terraform-json v0.16.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.15.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.0 // indirect github.com/hashicorp/terraform-svchost v0.1.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect diff --git a/go.sum b/go.sum index ff57d004..7cd61796 100644 --- a/go.sum +++ b/go.sum @@ -103,10 +103,14 @@ github.com/hashicorp/terraform-json v0.16.0 h1:UKkeWRWb23do5LNAFlh/K3N0ymn1qTOO8 github.com/hashicorp/terraform-json v0.16.0/go.mod h1:v0Ufk9jJnk6tcIZvScHvetlKfiNTC+WS21mnXIlc0B0= github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb h1:xEEf58fuUvCVJC5xtCHLDA5Rr3kHBBstKOEXCX1RTcM= github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb/go.mod h1:1YAwCOLHQLQUkM+rPf1+tCayEK92kdyLIfzSfEDe6og= +github.com/hashicorp/terraform-plugin-framework v1.2.0 h1:MZjFFfULnFq8fh04FqrKPcJ/nGpHOvX4buIygT3MSNY= +github.com/hashicorp/terraform-plugin-framework v1.2.0/go.mod h1:nToI62JylqXDq84weLJ/U3umUsBhZAaTmU0HXIVUOcw= github.com/hashicorp/terraform-plugin-go v0.15.0 h1:1BJNSUFs09DS8h/XNyJNJaeusQuWc/T9V99ylU9Zwp0= github.com/hashicorp/terraform-plugin-go v0.15.0/go.mod h1:tk9E3/Zx4RlF/9FdGAhwxHExqIHHldqiQGt20G6g+nQ= 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-mux v0.9.0 h1:a2Xh63cunDB/1GZECrV02cGA74AhQGUjY9X8W3P/L7k= +github.com/hashicorp/terraform-plugin-mux v0.9.0/go.mod h1:8NUFbgeMigms7Tma/r2Vgi5Jv5mPv4xcJ05pJtIOhwc= github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 h1:G9WAfb8LHeCxu7Ae8nc1agZlQOSCUWsb610iAogBhCs= github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1/go.mod h1:xcOSYlRVdPLmDUoqPhO9fiO/YCN/l6MGYeTzGt5jgkQ= github.com/hashicorp/terraform-plugin-testing v1.2.0 h1:pASRAe6BOZFO4xSGQr9WzitXit0nrQAYDk8ziuRfn9E= diff --git a/main.go b/main.go index 1954dbd8..5e267ad0 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,18 @@ package main import ( + "context" "flag" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/provider" - "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server" + "github.com/hashicorp/terraform-plugin-mux/tf5to6server" + "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "log" ) // Run "go generate" to format example terraform files and generate the docs for the registry/website @@ -29,16 +37,42 @@ var ( func main() { var debugMode bool flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") - flag.Parse() - opts := &plugin.ServeOpts{ - Debug: debugMode, + ctx := context.Background() + upgradedSdkServer, err := tf5to6server.UpgradeServer( + ctx, + func() tfprotov5.ProviderServer { + return schema.NewGRPCProviderServer(provider.New(version)()) + }, + ) + if err != nil { + log.Fatal(err) + } + + providers := []func() tfprotov6.ProviderServer{ + providerserver.NewProtocol6(provider.NewProvider(version)()), + func() tfprotov6.ProviderServer { + return upgradedSdkServer + }, + } - ProviderAddr: "registry.terraform.io/EnterpriseDB/biganimal", + muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...) + if err != nil { + log.Fatal(err) + } - ProviderFunc: provider.New(version), + var serveOpts []tf6server.ServeOpt + if debugMode { + serveOpts = append(serveOpts, tf6server.WithManagedDebug()) } - plugin.Serve(opts) + err = tf6server.Serve( + "registry.terraform.io/EnterpriseDB/biganimal", + muxServer.ProviderServer, + serveOpts..., + ) + if err != nil { + log.Fatal(err) + } } diff --git a/pkg/models/project.go b/pkg/models/project.go index ea110454..d27d74a8 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -6,11 +6,11 @@ type CloudProvider struct { } type Project struct { - ProjectId string `json:"projectId,omitempty" mapstructure:"project_id"` - ProjectName string `json:"projectName,omitempty" mapstructure:"name"` - UserCount int `json:"userCount,omitempty" mapstructure:"user_count"` - ClusterCount int `json:"clusterCount,omitempty" mapstructure:"cluster_count"` - CloudProviders []CloudProvider `json:"cloudProviders" mapstructure:"cloud_providers"` + ProjectId string `json:"projectId,omitempty" tfsdk:"project_id"` + ProjectName string `json:"projectName,omitempty" tfsdk:"name"` + UserCount int `json:"userCount,omitempty" tfsdk:"user_count"` + ClusterCount int `json:"clusterCount,omitempty" tfsdk:"cluster_count"` + CloudProviders []CloudProvider `json:"cloudProviders" tfsdk:"cloud_providers"` } // Check the return value, if ProjectName is also needed diff --git a/pkg/provider/data_source_projects.go b/pkg/provider/data_source_projects.go index c6c4381c..7c8a1500 100644 --- a/pkg/provider/data_source_projects.go +++ b/pkg/provider/data_source_projects.go @@ -2,68 +2,74 @@ package provider import ( "context" - "strconv" - "time" - + "fmt" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" ) -type ProjectsData struct{} +type projectsDataSource struct { + client *api.API +} + +func (p projectsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_projects" + +} + +// Configure adds the provider configured client to the data source. +func (d *projectsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } -func NewProjectsData() *ProjectsData { - return &ProjectsData{} + d.client = req.ProviderData.(*api.API) } -func (p *ProjectsData) Schema() *schema.Resource { - return &schema.Resource{ - Description: "The projects data source shows the BigAnimal Projects.", - ReadContext: p.Read, +type projectsDataSourceData struct { + Query string `tfsdk:"query"` + Projects []*models.Project `tfsdk:"items"` +} - Schema: map[string]*schema.Schema{ - "projects": { +func (p projectsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "The projects data source shows the BigAnimal Projects.", + Attributes: map[string]schema.Attribute{ + "projects": schema.SetNestedAttribute{ Description: "List of the organization's projects.", - Type: schema.TypeSet, Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "project_id": { + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "project_id": schema.StringAttribute{ Description: "Project ID of the project.", - Type: schema.TypeString, Computed: true, }, - "name": { + "name": schema.StringAttribute{ Description: "Project Name of the project.", - Type: schema.TypeString, Required: true, }, - "user_count": { + "user_count": schema.Int64Attribute{ Description: "User Count of the project.", - Type: schema.TypeInt, Computed: true, }, - "cluster_count": { + "cluster_count": schema.Int64Attribute{ Description: "User Count of the project.", - Type: schema.TypeInt, Computed: true, }, - "cloud_providers": { + "cloud_providers": schema.SetNestedAttribute{ Description: "Enabled Cloud Providers.", - Type: schema.TypeSet, Computed: true, Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cloud_provider_id": { + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cloud_provider_id": schema.StringAttribute{ Description: "Cloud Provider ID.", - Type: schema.TypeString, Computed: true, }, - "cloud_provider_name": { + "cloud_provider_name": schema.StringAttribute{ Description: "Cloud Provider Name.", - Type: schema.TypeString, Computed: true, }, }, @@ -72,29 +78,35 @@ func (p *ProjectsData) Schema() *schema.Resource { }, }, }, - "query": { + "query": schema.StringAttribute{ Description: "Query to filter project list.", - Type: schema.TypeString, Optional: true, }, }, } } -func (p *ProjectsData) Read(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - diags := diag.Diagnostics{} - client := api.BuildAPI(meta).ProjectClient() - - query := d.Get("query").(string) +func (p projectsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data projectsDataSourceData + diags := req.Config.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } - projects, err := client.List(ctx, query) + tflog.Trace(ctx, "read projects data source") + list, err := p.client.ProjectClient().List(ctx, data.Query) if err != nil { - return fromBigAnimalErr(err) + resp.Diagnostics.AddError("Read Error", fmt.Sprintf("Unable to call read project, got error: %s", err)) + return } - utils.SetOrPanic(d, "projects", projects) - // FIXME: Check if there is a better way to set the ID. - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + data.Projects = list + diags = resp.State.Set(ctx, &data) + resp.Diagnostics.Append(diags...) + +} - return diags +func NewProjectsDataSource() datasource.DataSource { + return &projectsDataSource{} } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index ddf79ab0..329f07f7 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -3,10 +3,14 @@ package provider import ( "context" "fmt" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/provider" + schema2 "github.com/hashicorp/terraform-plugin-framework/provider/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "os" ) var ( @@ -19,7 +23,6 @@ var ( dataRegion = NewRegionData() dataCluster = NewClusterData() - dataProjects = NewProjectsData() dataAWSConnection = NewAWSConnectionData() dataFaReplica = NewFAReplicaData() ) @@ -28,16 +31,6 @@ func init() { // Set descriptions to support markdown syntax, this will be used in document generation // and the language server. schema.DescriptionKind = schema.StringMarkdown - - // Customize the content of descriptions when output. For example you can add defaults on - // to the exported descriptions if present. - // schema.SchemaDescriptionBuilder = func(s *schema.Schema) string { - // desc := s.Description - // if s.Default != nil { - // desc += fmt.Sprintf(" Defaults to `%v`.", s.Default) - // } - // return strings.TrimSpace(desc) - // } } func New(version string) func() *schema.Provider { @@ -59,7 +52,6 @@ func New(version string) func() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ "biganimal_cluster": dataCluster.Schema(), "biganimal_region": dataRegion.Schema(), - "biganimal_projects": dataProjects.Schema(), "biganimal_faraway_replica": dataFaReplica.Schema(), "biganimal_aws_connection": dataAWSConnection.Schema(), }, @@ -86,7 +78,7 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema ba_bearer_token := schema.Get("ba_bearer_token").(string) ba_api_uri := schema.Get("ba_api_uri").(string) // set our meta to be a new api.API - // this can be turned into concrete clients + // this can be turned into concrete cba_api_uri := schema.Get("ba_api_uri").(string)lients // by // api.BuildAPI(meta).ClusterClient() // or @@ -96,3 +88,89 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema return api.NewAPI(ba_bearer_token, ba_api_uri, userAgent), nil } } + +type bigAnimalProvider struct { + version string +} + +// 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"` +} + +func NewProvider(version string) func() provider.Provider { + return func() provider.Provider { + return &bigAnimalProvider{ + version: version, + } + } +} + +func (b bigAnimalProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "bigAnimal" + resp.Version = b.version +} + +func (b bigAnimalProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + // get providerData + var data providerData + diags := req.Config.Get(ctx, &data) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + var token = os.Getenv("BA_BEARER_TOKEN") + if data.BaBearerToken != "" { + token = data.BaBearerToken + } + + if token == "" { + resp.Diagnostics.AddError( + "Unable to find ba_nearer_token", + "ba_nearer_token cannot be an empty string", + ) + return + } + + var host = "https://portal.biganimal.com/api/v3" + if os.Getenv("BA_API_URI") != "" { + host = os.Getenv("BA_API_URI") + } + if data.BaAPIUri != "" { + host = data.BaAPIUri + } + + userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", b.version) + client := api.NewAPI(token, host, userAgent) + resp.ResourceData = client + resp.DataSourceData = client +} + +func (b bigAnimalProvider) Schema(ctx context.Context, request provider.SchemaRequest, resp *provider.SchemaResponse) { + resp.Schema = schema2.Schema{ + Attributes: map[string]schema2.Attribute{ + "ba_bearer_token": schema2.StringAttribute{ + MarkdownDescription: "BigAnimal Bearer Token", + Sensitive: false, + Required: true, + }, + "ba_api_uri": schema2.StringAttribute{ + MarkdownDescription: "BigAnimal API URL", + Optional: true, + }, + }, + } +} + +func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource.DataSource { + return []func() datasource.DataSource{ + NewProjectsDataSource, + } +} + +func (b bigAnimalProvider) Resources(ctx context.Context) []func() resource.Resource { + return nil +} From fa4e2a282de484510f7af770e58d1db5b6fc829f Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Wed, 31 May 2023 18:40:42 +0800 Subject: [PATCH 02/49] test: Added acctest for project resource --- pkg/models/project.go | 6 +- pkg/provider/data_source_projects.go | 2 +- pkg/provider/provider.go | 10 +- pkg/provider/provider_test.go | 16 +- pkg/provider/resource_aws_connection_test.go | 3 +- pkg/provider/resource_proejct.go | 164 +++++++++++++++++++ pkg/provider/resource_proejct_test.go | 43 +++++ pkg/provider/resource_project.go | 152 ----------------- 8 files changed, 230 insertions(+), 166 deletions(-) create mode 100644 pkg/provider/resource_proejct.go create mode 100644 pkg/provider/resource_proejct_test.go delete mode 100644 pkg/provider/resource_project.go diff --git a/pkg/models/project.go b/pkg/models/project.go index d27d74a8..a63e8a17 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -1,13 +1,13 @@ package models type CloudProvider struct { - CloudProviderId string `json:"cloudProviderId,omitempty" mapstructure:"cloud_provider_id"` - CloudProviderName string `json:"cloudProviderName,omitempty" mapstructure:"cloud_provider_name"` + CloudProviderId string `json:"cloudProviderId,omitempty" tfsdk:"cloud_provider_id"` + CloudProviderName string `json:"cloudProviderName,omitempty" tfsdk:"cloud_provider_name"` } type Project struct { ProjectId string `json:"projectId,omitempty" tfsdk:"project_id"` - ProjectName string `json:"projectName,omitempty" tfsdk:"name"` + ProjectName string `json:"projectName,omitempty" tfsdk:"project_name"` UserCount int `json:"userCount,omitempty" tfsdk:"user_count"` ClusterCount int `json:"clusterCount,omitempty" tfsdk:"cluster_count"` CloudProviders []CloudProvider `json:"cloudProviders" tfsdk:"cloud_providers"` diff --git a/pkg/provider/data_source_projects.go b/pkg/provider/data_source_projects.go index 7c8a1500..a8477c24 100644 --- a/pkg/provider/data_source_projects.go +++ b/pkg/provider/data_source_projects.go @@ -46,7 +46,7 @@ func (p projectsDataSource) Schema(ctx context.Context, req datasource.SchemaReq Description: "Project ID of the project.", Computed: true, }, - "name": schema.StringAttribute{ + "project_name": schema.StringAttribute{ Description: "Project Name of the project.", Required: true, }, diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 329f07f7..86d074a8 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -16,7 +16,6 @@ import ( var ( resourceRegion = NewRegionResource() resourceCluster = NewClusterResource() - resourceProject = NewProjectResource() resourceAWSConnection = NewAWSConnectionResource() resourceAzureConnection = NewAzureConnectionResource() resourceFAReplica = NewFAReplicaResource() @@ -59,7 +58,6 @@ func New(version string) func() *schema.Provider { ResourcesMap: map[string]*schema.Resource{ "biganimal_cluster": resourceCluster.Schema(), "biganimal_region": resourceRegion.Schema(), - "biganimal_project": resourceProject.Schema(), "biganimal_aws_connection": resourceAWSConnection.Schema(), "biganimal_azure_connection": resourceAzureConnection.Schema(), "biganimal_faraway_replica": resourceFAReplica.Schema(), @@ -108,7 +106,7 @@ func NewProvider(version string) func() provider.Provider { } func (b bigAnimalProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { - resp.TypeName = "bigAnimal" + resp.TypeName = "biganimal" resp.Version = b.version } @@ -155,7 +153,7 @@ func (b bigAnimalProvider) Schema(ctx context.Context, request provider.SchemaRe "ba_bearer_token": schema2.StringAttribute{ MarkdownDescription: "BigAnimal Bearer Token", Sensitive: false, - Required: true, + Optional: true, }, "ba_api_uri": schema2.StringAttribute{ MarkdownDescription: "BigAnimal API URL", @@ -172,5 +170,7 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. } func (b bigAnimalProvider) Resources(ctx context.Context) []func() resource.Resource { - return nil + return []func() resource.Resource{ + NewProjectResource, + } } diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index f4043be3..229ce7e5 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -1,14 +1,18 @@ package provider import ( + "github.com/hashicorp/terraform-plugin-framework/providerserver" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "fmt" "os" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -var testAccProviderFactories map[string]func() (*schema.Provider, error) +var ( + testAccProviderFactories map[string]func() (*schema.Provider, error) + testAccProtoV6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) +) func init() { testAccProviderFactories = map[string]func() (*schema.Provider, error){ @@ -17,6 +21,10 @@ func init() { }, } + testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "biganimal": providerserver.NewProtocol6WithError(NewProvider("debug")()), + } + } // The following check can be added as a Resource specific PreCheck during Acceptance Tests @@ -42,6 +50,6 @@ func testAccPreCheck(t *testing.T) { t.Fatal("BA_API_URI must be set for acceptance tests") } if os.Getenv("BA_BEARER_TOKEN") == "" { - t.Fatal("BA_BEARER_TOKEN= must be set for acceptance tests") + t.Fatal("BA_BEARER_TOKEN must be set for acceptance tests") } } diff --git a/pkg/provider/resource_aws_connection_test.go b/pkg/provider/resource_aws_connection_test.go index f7ae94ef..5ca70816 100644 --- a/pkg/provider/resource_aws_connection_test.go +++ b/pkg/provider/resource_aws_connection_test.go @@ -38,7 +38,8 @@ func TestAccBiganimalAWSConnectionResource_basic(t *testing.T) { }, }) } -func awsConnConfig(projectID, roleARN, externalID string) string { + +func awsConnConfig(projectID, roleARN, externalID string) string { // nolint return fmt.Sprintf(`resource "biganimal_aws_connection" "test_aws_conn" { project_id = "%s" role_arn = "%s" diff --git a/pkg/provider/resource_proejct.go b/pkg/provider/resource_proejct.go new file mode 100644 index 00000000..a7185181 --- /dev/null +++ b/pkg/provider/resource_proejct.go @@ -0,0 +1,164 @@ +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" +) + +type projectResource struct { + client *api.API +} + +func (p projectResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_project" +} + +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. " + + "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{ + "project_id": schema.StringAttribute{ + Description: "Project ID of the project.", + Computed: true, + }, + "project_name": schema.StringAttribute{ + Description: "Project Name of the project.", + Required: true, + }, + "user_count": schema.Int64Attribute{ + Description: "User Count of the project.", + Computed: true, + }, + "cluster_count": schema.Int64Attribute{ + Description: "User Count of the project.", + Computed: true, + }, + + // 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, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cloud_provider_id": schema.StringAttribute{ + Description: "Cloud Provider ID.", + Computed: true, + }, + "cloud_provider_name": schema.StringAttribute{ + Description: "Cloud Provider Name.", + Computed: true, + }, + }, + }, + }, + }, + } +} + +// Configure adds the provider configured client to the data source. +func (r *projectResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*api.API) +} + +// 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) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId, err := p.client.ProjectClient().Create(ctx, plan.ProjectName) + if err != nil { + resp.Diagnostics.AddError("Error creating project", "Could not create project, unexpected error: "+err.Error()) + return + } + + project, err := p.client.ProjectClient().Read(ctx, projectId) + if err != nil { + resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, project) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + project, err := p.client.ProjectClient().Read(ctx, state.ProjectId) + if err != nil { + resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, project) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + 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) + if err != nil { + resp.Diagnostics.AddError("Error updating project", "Could not update project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + 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 { + resp.Diagnostics.AddError("Error deleting project", "Could not delete project, unexpected error: "+err.Error()) + return + } +} + +func NewProjectResource() resource.Resource { + return &projectResource{} +} diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go new file mode 100644 index 00000000..cf902110 --- /dev/null +++ b/pkg/provider/resource_proejct_test.go @@ -0,0 +1,43 @@ +package provider + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccBiganimalProjectResource(t *testing.T) { + projectName := fmt.Sprintf("acc_test_project_%v", time.Now().Unix()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: projectConfig(projectName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName), + 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"), + ), + }, + { + Config: projectConfig(projectName + "_updated"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName+"_updated"), + ), + }, + }, + }) +} + +func projectConfig(projectName string) string { + return fmt.Sprintf(` +resource "biganimal_project" "test_project" { + project_name = "%s" + }`, projectName) +} diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go deleted file mode 100644 index 51f6918f..00000000 --- a/pkg/provider/resource_project.go +++ /dev/null @@ -1,152 +0,0 @@ -package provider - -import ( - "context" - "time" - - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -type ProjectResource struct{} - -func NewProjectResource() *ProjectResource { - return &ProjectResource{} -} - -func (p *ProjectResource) Schema() *schema.Resource { - return &schema.Resource{ - Description: "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.", - - CreateContext: p.Create, - ReadContext: p.Read, - UpdateContext: p.Update, - DeleteContext: p.Delete, - - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(60 * time.Minute), - Update: schema.DefaultTimeout(60 * time.Minute), - Delete: schema.DefaultTimeout(60 * time.Minute), - }, - - Schema: map[string]*schema.Schema{ - "project_id": { - Description: "Project ID of the project.", - Type: schema.TypeString, - Computed: true, - }, - "project_name": { - Description: "Project Name of the project.", - Type: schema.TypeString, - Required: true, - }, - "user_count": { - Description: "User Count of the project.", - Type: schema.TypeInt, - Computed: true, - }, - "cluster_count": { - Description: "User Count of the project.", - Type: schema.TypeInt, - Computed: true, - }, - // We don't have a mechanism to automate the csp connection right now - // So, the `cloud_providers` value is computed only. - "cloud_providers": { - Description: "Enabled Cloud Providers.", - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cloud_provider_id": { - Description: "Cloud Provider ID.", - Type: schema.TypeString, - Computed: true, - }, - "cloud_provider_name": { - Description: "Cloud Provider Name.", - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func (p *ProjectResource) Create(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := api.BuildAPI(meta).ProjectClient() - project_name := d.Get("project_name").(string) - - projectId, err := client.Create(ctx, project_name) - - if err != nil { - return fromBigAnimalErr(err) - } - - d.SetId(projectId) - - if err := p.read(ctx, d, meta); err != nil { - return diag.FromErr(err) - } - return diag.Diagnostics{} -} - -func (p *ProjectResource) Read(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - if err := p.read(ctx, d, meta); err != nil { - return fromBigAnimalErr(err) - } - return diag.Diagnostics{} -} - -func (p *ProjectResource) read(ctx context.Context, d *schema.ResourceData, meta any) error { - // read the given project - client := api.BuildAPI(meta).ProjectClient() - - projectId := d.Id() - project, err := client.Read(ctx, projectId) - if err != nil { - return err - } - utils.SetOrPanic(d, "project_id", project.ProjectId) - utils.SetOrPanic(d, "project_name", project.ProjectName) - utils.SetOrPanic(d, "user_count", project.UserCount) - utils.SetOrPanic(d, "cluster_count", project.ClusterCount) - utils.SetOrPanic(d, "cloud_providers", project.CloudProviders) - - d.SetId(project.ProjectId) - return nil -} - -func (p *ProjectResource) Update(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - if d.HasChange("project_name") { - client := api.BuildAPI(meta).ProjectClient() - projectId := d.Id() - projectName := d.Get("project_name").(string) - _, err := client.Update(ctx, projectId, projectName) - if err != nil { - return fromBigAnimalErr(err) - } - if err := p.read(ctx, d, meta); err != nil { - return diag.FromErr(err) - } - return diag.Diagnostics{} - } - return nil -} - -func (p *ProjectResource) Delete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - // Delete a project - client := api.BuildAPI(meta).ProjectClient() - projectId := d.Id() - if err := client.Delete(ctx, projectId); err != nil { - return fromBigAnimalErr(err) - } - return diag.Diagnostics{} -} From f8b442ed2228012246d1fdcbd3a63bafa4ed970f Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 6 Jun 2023 14:31:50 +0800 Subject: [PATCH 03/49] fix: Use other test helper --- pkg/provider/provider_test.go | 2 +- pkg/provider/resource_aws_connection_test.go | 3 +-- pkg/provider/resource_proejct_test.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index 229ce7e5..072dd694 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -22,7 +22,7 @@ func init() { } testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "biganimal": providerserver.NewProtocol6WithError(NewProvider("debug")()), + "biganimal": providerserver.NewProtocol6WithError(NewProvider("test")()), } } diff --git a/pkg/provider/resource_aws_connection_test.go b/pkg/provider/resource_aws_connection_test.go index 5ca70816..52988c4e 100644 --- a/pkg/provider/resource_aws_connection_test.go +++ b/pkg/provider/resource_aws_connection_test.go @@ -2,10 +2,9 @@ package provider import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "os" "testing" - - "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccBiganimalAWSConnectionResource_basic(t *testing.T) { diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go index cf902110..4844507a 100644 --- a/pkg/provider/resource_proejct_test.go +++ b/pkg/provider/resource_proejct_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccBiganimalProjectResource(t *testing.T) { From cd523e6832724562950994fe3663c36fdd217dd3 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Wed, 31 May 2023 18:41:16 +0800 Subject: [PATCH 04/49] fix: Fixed acc failure --- .../{bigAnimal_projects.md => projects.md} | 6 +- docs/index.md | 5 +- docs/resources/project.md | 24 +--- main.go | 1 - pkg/provider/provider.go | 41 ++++-- pkg/provider/resource_proejct.go | 123 ++++++++++++++---- pkg/provider/resource_proejct_test.go | 1 - 7 files changed, 136 insertions(+), 65 deletions(-) rename docs/data-sources/{bigAnimal_projects.md => projects.md} (88%) 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 a53a8730..b9de9579 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"), ), }, { From 3af6be4b8760480ed6aea3198f56576079ca244a Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 6 Jun 2023 14:32:31 +0800 Subject: [PATCH 05/49] fix: Fixed pre-commit --- pkg/provider/provider.go | 6 +++--- pkg/provider/provider_test.go | 2 +- pkg/provider/{resource_proejct.go => resource_project.go} | 0 .../{resource_proejct_test.go => resource_project_test.go} | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename pkg/provider/{resource_proejct.go => resource_project.go} (100%) rename pkg/provider/{resource_proejct_test.go => resource_project_test.go} (100%) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index cd3476e1..4325aeb0 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -78,7 +78,7 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema ba_bearer_token := schema.Get("ba_bearer_token").(string) ba_api_uri := schema.Get("ba_api_uri").(string) // set our meta to be a new api.API - // this can be turned into concrete cba_api_uri := schema.Get("ba_api_uri").(string)lients + // this can be turned into concrete clients // by // api.BuildAPI(meta).ClusterClient() // or @@ -94,8 +94,8 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema 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"}) + Summary: "Unable to find ba_bearer_token", + Detail: "ba_bearer_token cannot be an empty string"}) return nil, diags } diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index 072dd694..813a83be 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -1,10 +1,10 @@ package provider import ( + "fmt" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "fmt" "os" "testing" ) diff --git a/pkg/provider/resource_proejct.go b/pkg/provider/resource_project.go similarity index 100% rename from pkg/provider/resource_proejct.go rename to pkg/provider/resource_project.go diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_project_test.go similarity index 100% rename from pkg/provider/resource_proejct_test.go rename to pkg/provider/resource_project_test.go From 228bfef75bf3af6dcf6462b7bce10bde0d223199 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:47:51 +0800 Subject: [PATCH 06/49] fix: Fixed compile error --- pkg/provider/data_source_projects.go | 10 +++++++--- pkg/provider/resource_project.go | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/provider/data_source_projects.go b/pkg/provider/data_source_projects.go index a8477c24..b368a1df 100644 --- a/pkg/provider/data_source_projects.go +++ b/pkg/provider/data_source_projects.go @@ -29,8 +29,8 @@ func (d *projectsDataSource) Configure(_ context.Context, req datasource.Configu } type projectsDataSourceData struct { - Query string `tfsdk:"query"` - Projects []*models.Project `tfsdk:"items"` + Query *string `tfsdk:"query"` + Projects []*models.Project `tfsdk:"projects"` } func (p projectsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { @@ -95,7 +95,11 @@ func (p projectsDataSource) Read(ctx context.Context, req datasource.ReadRequest } tflog.Trace(ctx, "read projects data source") - list, err := p.client.ProjectClient().List(ctx, data.Query) + var query string + if data.Query != nil { + query = *data.Query + } + list, err := p.client.ProjectClient().List(ctx, query) if err != nil { resp.Diagnostics.AddError("Read Error", fmt.Sprintf("Unable to call read project, got error: %s", err)) return diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index c5638279..d702cac6 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -173,7 +173,7 @@ func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, res state.UserCount = types.Int64Value(int64(project.UserCount)) state.ClusterCount = types.Int64Value(int64(project.ClusterCount)) if cps := project.CloudProviders; cps != nil { - for _, provider := range *cps { + for _, provider := range cps { state.CloudProviders = append(state.CloudProviders, cloudProvider{ CloudProviderId: provider.CloudProviderId, CloudProviderName: provider.CloudProviderName, From f004766685c07b51017c6e75371a80c9a75518d5 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Fri, 9 Jun 2023 14:43:44 +0800 Subject: [PATCH 07/49] fix: Fixed go fmt and goimports --- pkg/api/cloud_provider_client.go | 3 ++- pkg/models/project.go | 8 ++++---- pkg/provider/data_source_aws_connection.go | 1 + pkg/provider/data_source_projects.go | 1 + pkg/provider/provider.go | 3 ++- pkg/provider/provider_test.go | 5 +++-- pkg/provider/resource_aws_connection.go | 3 ++- pkg/provider/resource_aws_connection_test.go | 3 ++- pkg/provider/resource_azure_connection.go | 3 ++- pkg/provider/resource_project.go | 1 + pkg/provider/utils.go | 1 + pkg/provider/validators.go | 3 ++- pkg/provider/validators_test.go | 3 ++- 13 files changed, 25 insertions(+), 13 deletions(-) diff --git a/pkg/api/cloud_provider_client.go b/pkg/api/cloud_provider_client.go index c3a083df..63f3c4cf 100644 --- a/pkg/api/cloud_provider_client.go +++ b/pkg/api/cloud_provider_client.go @@ -5,9 +5,10 @@ import ( "context" "encoding/json" "fmt" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "net/http" "time" + + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" ) type CloudProviderClient struct{ API } diff --git a/pkg/models/project.go b/pkg/models/project.go index a63e8a17..3bedf48c 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -6,10 +6,10 @@ type CloudProvider struct { } type Project struct { - ProjectId string `json:"projectId,omitempty" tfsdk:"project_id"` - ProjectName string `json:"projectName,omitempty" tfsdk:"project_name"` - UserCount int `json:"userCount,omitempty" tfsdk:"user_count"` - ClusterCount int `json:"clusterCount,omitempty" tfsdk:"cluster_count"` + ProjectId string `json:"projectId,omitempty" tfsdk:"project_id"` + ProjectName string `json:"projectName,omitempty" tfsdk:"project_name"` + UserCount int `json:"userCount,omitempty" tfsdk:"user_count"` + ClusterCount int `json:"clusterCount,omitempty" tfsdk:"cluster_count"` CloudProviders []CloudProvider `json:"cloudProviders" tfsdk:"cloud_providers"` } diff --git a/pkg/provider/data_source_aws_connection.go b/pkg/provider/data_source_aws_connection.go index 463bba72..77c86f8f 100644 --- a/pkg/provider/data_source_aws_connection.go +++ b/pkg/provider/data_source_aws_connection.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" diff --git a/pkg/provider/data_source_projects.go b/pkg/provider/data_source_projects.go index b368a1df..11814ba7 100644 --- a/pkg/provider/data_source_projects.go +++ b/pkg/provider/data_source_projects.go @@ -3,6 +3,7 @@ package provider import ( "context" "fmt" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "github.com/hashicorp/terraform-plugin-framework/datasource" diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 4325aeb0..abd3f201 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -3,6 +3,8 @@ package provider import ( "context" "fmt" + "os" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" @@ -11,7 +13,6 @@ import ( "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" ) var ( diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index 813a83be..111492ab 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -2,11 +2,12 @@ package provider import ( "fmt" + "os" + "testing" + "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "os" - "testing" ) var ( diff --git a/pkg/provider/resource_aws_connection.go b/pkg/provider/resource_aws_connection.go index 7a2320bc..c5a07442 100644 --- a/pkg/provider/resource_aws_connection.go +++ b/pkg/provider/resource_aws_connection.go @@ -3,13 +3,14 @@ package provider import ( "context" "fmt" + "time" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "time" ) type AWSConnectionResource struct{} diff --git a/pkg/provider/resource_aws_connection_test.go b/pkg/provider/resource_aws_connection_test.go index 52988c4e..38c4ef0b 100644 --- a/pkg/provider/resource_aws_connection_test.go +++ b/pkg/provider/resource_aws_connection_test.go @@ -2,9 +2,10 @@ package provider import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "os" "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccBiganimalAWSConnectionResource_basic(t *testing.T) { diff --git a/pkg/provider/resource_azure_connection.go b/pkg/provider/resource_azure_connection.go index 184071a8..21adf29f 100644 --- a/pkg/provider/resource_azure_connection.go +++ b/pkg/provider/resource_azure_connection.go @@ -2,12 +2,13 @@ package provider import ( "context" + "time" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "time" ) type AzureConnectionResource struct{} diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index d702cac6..9f2e999c 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index 9275f10e..583fb65d 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -2,6 +2,7 @@ package provider import ( "errors" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" diff --git a/pkg/provider/validators.go b/pkg/provider/validators.go index c3636fe4..d773c4aa 100644 --- a/pkg/provider/validators.go +++ b/pkg/provider/validators.go @@ -2,10 +2,11 @@ package provider import ( "fmt" - "github.com/google/uuid" "regexp" "strings" + "github.com/google/uuid" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" diff --git a/pkg/provider/validators_test.go b/pkg/provider/validators_test.go index eb62d366..cd5ca363 100644 --- a/pkg/provider/validators_test.go +++ b/pkg/provider/validators_test.go @@ -2,10 +2,11 @@ package provider import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "reflect" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/go-cty/cty" ) From fb385a040ced7e5fa5ee9699dc6cd0202c4a514e Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Fri, 9 Jun 2023 16:57:50 +0800 Subject: [PATCH 08/49] fix: Added UT for configure function --- main.go | 4 +- pkg/provider/provider.go | 107 +++++++++---------- pkg/provider/provider_test.go | 80 +++++++++++++- pkg/provider/resource_aws_connection_test.go | 2 +- pkg/provider/resource_project.go | 14 +-- pkg/provider/resource_project_test.go | 2 +- 6 files changed, 138 insertions(+), 71 deletions(-) diff --git a/main.go b/main.go index b22ec4ea..d486ad59 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ func main() { upgradedSdkServer, err := tf5to6server.UpgradeServer( ctx, func() tfprotov5.ProviderServer { - return schema.NewGRPCProviderServer(provider.New(version)()) + return schema.NewGRPCProviderServer(provider.NewSDKProvider(version)()) }, ) if err != nil { @@ -50,7 +50,7 @@ func main() { } providers := []func() tfprotov6.ProviderServer{ - providerserver.NewProtocol6(provider.NewProvider(version)()), + providerserver.NewProtocol6(provider.NewFrameworkProvider(version)()), func() tfprotov6.ProviderServer { return upgradedSdkServer }, diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index abd3f201..e03b0718 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -8,13 +8,14 @@ import ( "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" - schema2 "github.com/hashicorp/terraform-plugin-framework/provider/schema" + frameworkschema "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" + sdkschema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) +const DefaultAPIURL = "https://portal.biganimal.com/api/v3" + var ( resourceRegion = NewRegionResource() resourceCluster = NewClusterResource() @@ -31,34 +32,34 @@ var ( func init() { // Set descriptions to support markdown syntax, this will be used in document generation // and the language server. - schema.DescriptionKind = schema.StringMarkdown + sdkschema.DescriptionKind = sdkschema.StringMarkdown } -func New(version string) func() *schema.Provider { - return func() *schema.Provider { - p := &schema.Provider{ - Schema: map[string]*schema.Schema{ +func NewSDKProvider(version string) func() *sdkschema.Provider { + return func() *sdkschema.Provider { + p := &sdkschema.Provider{ + Schema: map[string]*sdkschema.Schema{ "ba_bearer_token": { - Type: schema.TypeString, + Type: sdkschema.TypeString, Description: "BigAnimal Bearer Token", Sensitive: false, Optional: true, }, "ba_api_uri": { - Type: schema.TypeString, + Type: sdkschema.TypeString, Description: "BigAnimal API URL", Optional: true, }, }, - DataSourcesMap: map[string]*schema.Resource{ + DataSourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": dataCluster.Schema(), "biganimal_region": dataRegion.Schema(), "biganimal_faraway_replica": dataFaReplica.Schema(), "biganimal_aws_connection": dataAWSConnection.Schema(), }, - ResourcesMap: map[string]*schema.Resource{ + ResourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": resourceCluster.Schema(), "biganimal_region": resourceRegion.Schema(), "biganimal_aws_connection": resourceAWSConnection.Schema(), @@ -73,9 +74,8 @@ func New(version string) func() *schema.Provider { } } -func configure(version string, p *schema.Provider) func(context.Context, *schema.ResourceData) (any, diag.Diagnostics) { - - return func(ctx context.Context, schema *schema.ResourceData) (any, diag.Diagnostics) { +func configure(version string, p *sdkschema.Provider) func(context.Context, *sdkschema.ResourceData) (any, diag.Diagnostics) { + return func(ctx context.Context, schema *sdkschema.ResourceData) (any, diag.Diagnostics) { ba_bearer_token := schema.Get("ba_bearer_token").(string) ba_api_uri := schema.Get("ba_api_uri").(string) // set our meta to be a new api.API @@ -85,28 +85,13 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema // or // 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_bearer_token", - Detail: "ba_bearer_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" + data := &providerData{BaAPIUri: &ba_api_uri, BaBearerToken: &ba_bearer_token} + ok, summary, detail := checkProviderConfig(data) + if !ok { + return nil, diag.Diagnostics{diag.Diagnostic{Severity: diag.Error, Summary: summary, Detail: detail}} } + userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", version) return api.NewAPI(ba_bearer_token, ba_api_uri, userAgent), nil } } @@ -121,7 +106,7 @@ type providerData struct { BaAPIUri *string `tfsdk:"ba_api_uri"` } -func NewProvider(version string) func() provider.Provider { +func NewFrameworkProvider(version string) func() provider.Provider { return func() provider.Provider { return &bigAnimalProvider{ version: version, @@ -144,42 +129,48 @@ func (b bigAnimalProvider) Configure(ctx context.Context, req provider.Configure return } - var token = os.Getenv("BA_BEARER_TOKEN") - if data.BaBearerToken != nil { - token = *data.BaBearerToken + ok, summary, detail := checkProviderConfig(&data) + if !ok { + resp.Diagnostics.AddError(summary, detail) + return } - if token == "" { - resp.Diagnostics.AddError( - "Unable to find ba_nearer_token", - "ba_nearer_token cannot be an empty string", - ) - return + userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", b.version) + client := api.NewAPI(*data.BaBearerToken, *data.BaAPIUri, userAgent) + resp.ResourceData = client + resp.DataSourceData = client +} + +func checkProviderConfig(data *providerData) (ok bool, summary, detail string) { + if data.BaBearerToken == nil { + token := os.Getenv("BA_BEARER_TOKEN") + data.BaBearerToken = &token } - var host = "https://portal.biganimal.com/api/v3" - if os.Getenv("BA_API_URI") != "" { - host = os.Getenv("BA_API_URI") + if *data.BaBearerToken == "" { + return false, "Unable to find ba_nearer_token", "ba_nearer_token cannot be an empty string" } - if data.BaAPIUri != nil { - host = *data.BaAPIUri + + if data.BaAPIUri == nil { + url := os.Getenv("BA_API_URI") + data.BaAPIUri = &url } - userAgent := fmt.Sprintf("%s/%s", "terraform-provider-biganimal", b.version) - client := api.NewAPI(token, host, userAgent) - resp.ResourceData = client - resp.DataSourceData = client + if *data.BaAPIUri == "" { + *data.BaAPIUri = DefaultAPIURL + } + return true, "", "" } func (b bigAnimalProvider) Schema(ctx context.Context, request provider.SchemaRequest, resp *provider.SchemaResponse) { - resp.Schema = schema2.Schema{ - Attributes: map[string]schema2.Attribute{ - "ba_bearer_token": schema2.StringAttribute{ + resp.Schema = frameworkschema.Schema{ + Attributes: map[string]frameworkschema.Attribute{ + "ba_bearer_token": frameworkschema.StringAttribute{ MarkdownDescription: "BigAnimal Bearer Token", Sensitive: false, Optional: true, }, - "ba_api_uri": schema2.StringAttribute{ + "ba_api_uri": frameworkschema.StringAttribute{ MarkdownDescription: "BigAnimal API URL", Optional: true, }, diff --git a/pkg/provider/provider_test.go b/pkg/provider/provider_test.go index 111492ab..3bff10bb 100644 --- a/pkg/provider/provider_test.go +++ b/pkg/provider/provider_test.go @@ -18,12 +18,12 @@ var ( func init() { testAccProviderFactories = map[string]func() (*schema.Provider, error){ "biganimal": func() (*schema.Provider, error) { - return New("test")(), nil + return NewSDKProvider("test")(), nil }, } testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "biganimal": providerserver.NewProtocol6WithError(NewProvider("test")()), + "biganimal": providerserver.NewProtocol6WithError(NewFrameworkProvider("test")()), } } @@ -54,3 +54,79 @@ func testAccPreCheck(t *testing.T) { t.Fatal("BA_BEARER_TOKEN must be set for acceptance tests") } } + +func Test_checkProviderConfig(t *testing.T) { + configToken := "config_token" + configUrl := "config_url" + + type args struct { + data *providerData + envURL bool + envToken bool + } + tests := []struct { + name string + args args + wantOk bool + wantToken string + wantUrl string + }{ + { + name: "failure due to an empty token", + args: args{data: &providerData{}}, + }, + + { + name: "From environment variables", + args: args{envToken: true, envURL: true, data: &providerData{}}, + wantOk: true, + wantToken: "env_token", + wantUrl: "env_url", + }, + + { + name: "From configuration", + args: args{envToken: true, envURL: true, data: &providerData{BaAPIUri: &configUrl, BaBearerToken: &configToken}}, + wantOk: true, + wantUrl: configUrl, + wantToken: configToken, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + envToken := "" + if tt.args.envToken { + envToken = "env_token" + } + if err := os.Setenv("BA_BEARER_TOKEN", envToken); err != nil { + t.Fatal(err) + } + + envURL := "" + if tt.args.envURL { + envURL = "env_url" + } + if err := os.Setenv("BA_API_URI", envURL); err != nil { + t.Fatal(err) + } + + gotOk, _, _ := checkProviderConfig(tt.args.data) + if gotOk != tt.wantOk { + t.Errorf("checkProviderConfig() gotOk = %v, want %v", gotOk, tt.wantOk) + } + + if gotOk { + gotUrl := *tt.args.data.BaAPIUri + if gotUrl != tt.wantUrl { + t.Errorf("checkProviderConfig() gotURL = %v, want %v", gotUrl, tt.wantUrl) + } + + gotToken := *tt.args.data.BaBearerToken + if gotToken != tt.wantToken { + t.Errorf("checkProviderConfig() gotToken = %v, want %v", gotToken, tt.wantToken) + } + + } + }) + } +} diff --git a/pkg/provider/resource_aws_connection_test.go b/pkg/provider/resource_aws_connection_test.go index 38c4ef0b..5ca70816 100644 --- a/pkg/provider/resource_aws_connection_test.go +++ b/pkg/provider/resource_aws_connection_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccBiganimalAWSConnectionResource_basic(t *testing.T) { diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index 9f2e999c..34a26f98 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -15,7 +15,7 @@ import ( ) type projectResource struct { - client *api.API + client *api.ProjectClient } func (p projectResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -103,7 +103,7 @@ func (r *projectResource) Configure(_ context.Context, req resource.ConfigureReq return } - r.client = req.ProviderData.(*api.API) + r.client = req.ProviderData.(*api.API).ProjectClient() } type cloudProvider struct { @@ -129,13 +129,13 @@ func (p projectResource) Create(ctx context.Context, req resource.CreateRequest, return } - projectId, err := p.client.ProjectClient().Create(ctx, config.ProjectName.ValueString()) + projectId, err := p.client.Create(ctx, config.ProjectName.ValueString()) if err != nil { resp.Diagnostics.AddError("Error creating project", "Could not create project, unexpected error: "+err.Error()) return } - project, err := p.client.ProjectClient().Read(ctx, projectId) + project, err := p.client.Read(ctx, projectId) if err != nil { resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) return @@ -164,7 +164,7 @@ func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, res return } - project, err := p.client.ProjectClient().Read(ctx, state.ID.ValueString()) + project, err := p.client.Read(ctx, state.ID.ValueString()) if err != nil { resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) return @@ -198,7 +198,7 @@ func (p projectResource) Update(ctx context.Context, req resource.UpdateRequest, return } - _, err := p.client.ProjectClient().Update(ctx, plan.ID.ValueString(), plan.ProjectName.ValueString()) + _, err := p.client.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 @@ -221,7 +221,7 @@ func (p projectResource) Delete(ctx context.Context, req resource.DeleteRequest, return } - if err := p.client.ProjectClient().Delete(ctx, state.ID.ValueString()); err != nil { + if err := p.client.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_project_test.go b/pkg/provider/resource_project_test.go index 3c44ffa1..0a80f0a3 100644 --- a/pkg/provider/resource_project_test.go +++ b/pkg/provider/resource_project_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) func TestAccBiganimalProjectResource(t *testing.T) { From 95f8ff007fde2ba3279ec551269127d65e22c2f1 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Fri, 9 Jun 2023 18:43:10 +0800 Subject: [PATCH 09/49] feat: Added import for project resource --- pkg/provider/provider.go | 4 ++-- pkg/provider/resource_project.go | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index e03b0718..56276145 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -142,13 +142,13 @@ func (b bigAnimalProvider) Configure(ctx context.Context, req provider.Configure } func checkProviderConfig(data *providerData) (ok bool, summary, detail string) { - if data.BaBearerToken == nil { + if data.BaBearerToken == nil || *data.BaBearerToken == "" { token := os.Getenv("BA_BEARER_TOKEN") data.BaBearerToken = &token } if *data.BaBearerToken == "" { - return false, "Unable to find ba_nearer_token", "ba_nearer_token cannot be an empty string" + return false, "Unable to find BA_BEARER_TOKEN", "BA_BEARER_TOKEN cannot be an empty string" } if data.BaAPIUri == nil { diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index 34a26f98..d7022a1c 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -227,6 +228,10 @@ func (p projectResource) Delete(ctx context.Context, req resource.DeleteRequest, } } +func (p projectResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + func NewProjectResource() resource.Resource { return &projectResource{} } From 90d6b504a9540843c9563867c50a0b7cb74c626b Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:19:39 +0800 Subject: [PATCH 10/49] refactor: rewrite datasource_projects with terraform-plugin-framework --- docs/data-sources/bigAnimal_projects.md | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/data-sources/bigAnimal_projects.md diff --git a/docs/data-sources/bigAnimal_projects.md b/docs/data-sources/bigAnimal_projects.md new file mode 100644 index 00000000..28ed521c --- /dev/null +++ b/docs/data-sources/bigAnimal_projects.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "bigAnimal_projects Data Source - terraform-provider-biganimal" +subcategory: "" +description: |- + The projects data source shows the BigAnimal Projects. +--- + +# bigAnimal_projects (Data Source) + +The projects data source shows the BigAnimal Projects. + +## Example Usage + +```terraform +data "biganimal_projects" "this" {} + +output "projects" { + value = data.biganimal_projects.this.projects +} +``` + + +## Schema + +### Optional + +- `query` (String) Query to filter project list. + +### Read-Only + +- `projects` (Attributes Set) List of the organization's projects. (see [below for nested schema](#nestedatt--projects)) + + +### Nested Schema for `projects` + +Required: + +- `name` (String) Project Name of the project. + +Optional: + +- `cloud_providers` (Attributes Set) Enabled Cloud Providers. (see [below for nested schema](#nestedatt--projects--cloud_providers)) + +Read-Only: + +- `cluster_count` (Number) User Count of the project. +- `project_id` (String) Project ID of the project. +- `user_count` (Number) User Count of the project. + + +### Nested Schema for `projects.cloud_providers` + +Read-Only: + +- `cloud_provider_id` (String) Cloud Provider ID. +- `cloud_provider_name` (String) Cloud Provider Name. From 5da140c90fdd022f8692ef8a3135c6bd19fb20a9 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:20:28 +0800 Subject: [PATCH 11/49] test: Added acctest for project resource --- pkg/provider/resource_proejct.go | 164 ++++++++++++++++++++++++++ pkg/provider/resource_proejct_test.go | 43 +++++++ 2 files changed, 207 insertions(+) create mode 100644 pkg/provider/resource_proejct.go create mode 100644 pkg/provider/resource_proejct_test.go diff --git a/pkg/provider/resource_proejct.go b/pkg/provider/resource_proejct.go new file mode 100644 index 00000000..a7185181 --- /dev/null +++ b/pkg/provider/resource_proejct.go @@ -0,0 +1,164 @@ +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" +) + +type projectResource struct { + client *api.API +} + +func (p projectResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_project" +} + +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. " + + "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{ + "project_id": schema.StringAttribute{ + Description: "Project ID of the project.", + Computed: true, + }, + "project_name": schema.StringAttribute{ + Description: "Project Name of the project.", + Required: true, + }, + "user_count": schema.Int64Attribute{ + Description: "User Count of the project.", + Computed: true, + }, + "cluster_count": schema.Int64Attribute{ + Description: "User Count of the project.", + Computed: true, + }, + + // 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, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cloud_provider_id": schema.StringAttribute{ + Description: "Cloud Provider ID.", + Computed: true, + }, + "cloud_provider_name": schema.StringAttribute{ + Description: "Cloud Provider Name.", + Computed: true, + }, + }, + }, + }, + }, + } +} + +// Configure adds the provider configured client to the data source. +func (r *projectResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*api.API) +} + +// 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) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + projectId, err := p.client.ProjectClient().Create(ctx, plan.ProjectName) + if err != nil { + resp.Diagnostics.AddError("Error creating project", "Could not create project, unexpected error: "+err.Error()) + return + } + + project, err := p.client.ProjectClient().Read(ctx, projectId) + if err != nil { + resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, project) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + project, err := p.client.ProjectClient().Read(ctx, state.ProjectId) + if err != nil { + resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, project) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + 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) + if err != nil { + resp.Diagnostics.AddError("Error updating project", "Could not update project, unexpected error: "+err.Error()) + return + } + + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// 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 + 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 { + resp.Diagnostics.AddError("Error deleting project", "Could not delete project, unexpected error: "+err.Error()) + return + } +} + +func NewProjectResource() resource.Resource { + return &projectResource{} +} diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go new file mode 100644 index 00000000..cf902110 --- /dev/null +++ b/pkg/provider/resource_proejct_test.go @@ -0,0 +1,43 @@ +package provider + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccBiganimalProjectResource(t *testing.T) { + projectName := fmt.Sprintf("acc_test_project_%v", time.Now().Unix()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: projectConfig(projectName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName), + 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"), + ), + }, + { + Config: projectConfig(projectName + "_updated"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName+"_updated"), + ), + }, + }, + }) +} + +func projectConfig(projectName string) string { + return fmt.Sprintf(` +resource "biganimal_project" "test_project" { + project_name = "%s" + }`, projectName) +} From 540dabfd9074afd57398ac62fccb927373b4d3ea Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:20:41 +0800 Subject: [PATCH 12/49] fix: Use other test helper --- go.mod | 1 - pkg/provider/resource_proejct_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e8c54491..f43cf59e 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 - github.com/hashicorp/terraform-plugin-testing v1.2.0 github.com/joho/godotenv v1.5.1 github.com/kr/pretty v0.3.1 github.com/mitchellh/mapstructure v1.5.0 diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go index cf902110..4844507a 100644 --- a/pkg/provider/resource_proejct_test.go +++ b/pkg/provider/resource_proejct_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccBiganimalProjectResource(t *testing.T) { From 5698727e16a9e49fb93b7a235ccf30b1f5be9607 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Mon, 24 Apr 2023 21:16:31 +0800 Subject: [PATCH 13/49] fix: Fixed acc failure --- docs/data-sources/bigAnimal_projects.md | 57 ----------- pkg/provider/resource_proejct.go | 123 ++++++++++++++++++------ pkg/provider/resource_proejct_test.go | 1 - 3 files changed, 95 insertions(+), 86 deletions(-) delete mode 100644 docs/data-sources/bigAnimal_projects.md diff --git a/docs/data-sources/bigAnimal_projects.md b/docs/data-sources/bigAnimal_projects.md deleted file mode 100644 index 28ed521c..00000000 --- a/docs/data-sources/bigAnimal_projects.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "bigAnimal_projects Data Source - terraform-provider-biganimal" -subcategory: "" -description: |- - The projects data source shows the BigAnimal Projects. ---- - -# bigAnimal_projects (Data Source) - -The projects data source shows the BigAnimal Projects. - -## Example Usage - -```terraform -data "biganimal_projects" "this" {} - -output "projects" { - value = data.biganimal_projects.this.projects -} -``` - - -## Schema - -### Optional - -- `query` (String) Query to filter project list. - -### Read-Only - -- `projects` (Attributes Set) List of the organization's projects. (see [below for nested schema](#nestedatt--projects)) - - -### Nested Schema for `projects` - -Required: - -- `name` (String) Project Name of the project. - -Optional: - -- `cloud_providers` (Attributes Set) Enabled Cloud Providers. (see [below for nested schema](#nestedatt--projects--cloud_providers)) - -Read-Only: - -- `cluster_count` (Number) User Count of the project. -- `project_id` (String) Project ID of the project. -- `user_count` (Number) User Count of the project. - - -### Nested Schema for `projects.cloud_providers` - -Read-Only: - -- `cloud_provider_id` (String) Cloud Provider ID. -- `cloud_provider_name` (String) Cloud Provider Name. 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"), ), }, { From ad5cba27d5a584e089f6a032c498b09357dfb3dd Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Wed, 10 May 2023 17:58:39 +0800 Subject: [PATCH 14/49] fix: fixed typo --- pkg/provider/resource_proejct.go | 231 -------------------------- pkg/provider/resource_proejct_test.go | 42 ----- 2 files changed, 273 deletions(-) delete mode 100644 pkg/provider/resource_proejct.go delete mode 100644 pkg/provider/resource_proejct_test.go diff --git a/pkg/provider/resource_proejct.go b/pkg/provider/resource_proejct.go deleted file mode 100644 index c5638279..00000000 --- a/pkg/provider/resource_proejct.go +++ /dev/null @@ -1,231 +0,0 @@ -package provider - -import ( - "context" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "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 { - client *api.API -} - -func (p projectResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_project" -} - -func (p projectResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ - 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{ - 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{ - MarkdownDescription: "Project Name of the project.", - Required: true, - }, - "user_count": schema.Int64Attribute{ - MarkdownDescription: "User Count of the project.", - Computed: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - }, - "cluster_count": schema.Int64Attribute{ - 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{ - 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{ - MarkdownDescription: "Cloud Provider ID.", - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "cloud_provider_name": schema.StringAttribute{ - MarkdownDescription: "Cloud Provider Name.", - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - }, - }, - }, - }, - } -} - -// Configure adds the provider configured client to the data source. -func (r *projectResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - - 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 config Project - diags := req.Config.Get(ctx, &config) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - 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 - } - - project, err := p.client.ProjectClient().Read(ctx, projectId) - if err != nil { - resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) - return - } - - 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 - } -} - -// Read refreshes the Terraform state with the latest data. -func (p projectResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - 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.ID.ValueString()) - if err != nil { - resp.Diagnostics.AddError("Error reading project", "Could not read project, unexpected error: "+err.Error()) - return - } - - 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 - } -} - -// 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 Project - diags := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - _, 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) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } -} - -// 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 Project - diags := req.State.Get(ctx, &state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - 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 - } -} - -func NewProjectResource() resource.Resource { - return &projectResource{} -} diff --git a/pkg/provider/resource_proejct_test.go b/pkg/provider/resource_proejct_test.go deleted file mode 100644 index 3c44ffa1..00000000 --- a/pkg/provider/resource_proejct_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package provider - -import ( - "fmt" - "testing" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccBiganimalProjectResource(t *testing.T) { - projectName := fmt.Sprintf("acc_test_project_%v", time.Now().Unix()) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - { - Config: projectConfig(projectName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName), - resource.TestCheckResourceAttrSet("biganimal_project.test_project", "project_id"), - resource.TestCheckResourceAttrSet("biganimal_project.test_project", "user_count"), - resource.TestCheckResourceAttrSet("biganimal_project.test_project", "cluster_count"), - ), - }, - { - Config: projectConfig(projectName + "_updated"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("biganimal_project.test_project", "project_name", projectName+"_updated"), - ), - }, - }, - }) -} - -func projectConfig(projectName string) string { - return fmt.Sprintf(` -resource "biganimal_project" "test_project" { - project_name = "%s" - }`, projectName) -} From 833e0b61e73e9cca43001b743a6ea00a881866cb Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Mon, 12 Jun 2023 14:09:30 +0800 Subject: [PATCH 15/49] fix: Fixed acc failure --- go.mod | 1 + pkg/provider/provider.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f43cf59e..e8c54491 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 + github.com/hashicorp/terraform-plugin-testing v1.2.0 github.com/joho/godotenv v1.5.1 github.com/kr/pretty v0.3.1 github.com/mitchellh/mapstructure v1.5.0 diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 56276145..0a6dc702 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -33,7 +33,6 @@ func init() { // Set descriptions to support markdown syntax, this will be used in document generation // and the language server. sdkschema.DescriptionKind = sdkschema.StringMarkdown - } func NewSDKProvider(version string) func() *sdkschema.Provider { From b2f692718368310084eb921c9a36f3700ae7faba Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Mon, 12 Jun 2023 14:13:43 +0800 Subject: [PATCH 16/49] refactor: rewrite resource_region with terraform-plugin-framework --- go.mod | 2 + go.sum | 4 + pkg/provider/default.go | 29 ++++ pkg/provider/provider.go | 2 +- pkg/provider/resource_project.go | 1 + pkg/provider/resource_region.go | 246 ++++++++++++++++--------------- pkg/provider/utils.go | 12 ++ pkg/provider/validators.go | 11 ++ 8 files changed, 188 insertions(+), 119 deletions(-) create mode 100644 pkg/provider/default.go diff --git a/go.mod b/go.mod index e8c54491..21e6ec3e 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb github.com/hashicorp/terraform-plugin-framework v1.2.0 + github.com/hashicorp/terraform-plugin-framework-timeouts v0.3.1 + github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 github.com/hashicorp/terraform-plugin-go v0.15.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.9.0 diff --git a/go.sum b/go.sum index 7cd61796..461efade 100644 --- a/go.sum +++ b/go.sum @@ -105,6 +105,10 @@ github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb github.com/hashicorp/terraform-plugin-docs v0.14.2-0.20230330141128-e8f8eb1f6dbb/go.mod h1:1YAwCOLHQLQUkM+rPf1+tCayEK92kdyLIfzSfEDe6og= github.com/hashicorp/terraform-plugin-framework v1.2.0 h1:MZjFFfULnFq8fh04FqrKPcJ/nGpHOvX4buIygT3MSNY= github.com/hashicorp/terraform-plugin-framework v1.2.0/go.mod h1:nToI62JylqXDq84weLJ/U3umUsBhZAaTmU0HXIVUOcw= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.3.1 h1:5GhozvHUsrqxqku+yd0UIRTkmDLp2QPX5paL1Kq5uUA= +github.com/hashicorp/terraform-plugin-framework-timeouts v0.3.1/go.mod h1:ThtYDU8p6sJ9+SI+TYxXrw28vXxgBwYOpoPv1EojSJI= +github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 h1:4L0tmy/8esP6OcvocVymw52lY0HyQ5OxB7VNl7k4bS0= +github.com/hashicorp/terraform-plugin-framework-validators v0.10.0/go.mod h1:qdQJCdimB9JeX2YwOpItEu+IrfoJjWQ5PhLpAOMDQAE= github.com/hashicorp/terraform-plugin-go v0.15.0 h1:1BJNSUFs09DS8h/XNyJNJaeusQuWc/T9V99ylU9Zwp0= github.com/hashicorp/terraform-plugin-go v0.15.0/go.mod h1:tk9E3/Zx4RlF/9FdGAhwxHExqIHHldqiQGt20G6g+nQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= diff --git a/pkg/provider/default.go b/pkg/provider/default.go new file mode 100644 index 00000000..dbc44e0b --- /dev/null +++ b/pkg/provider/default.go @@ -0,0 +1,29 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type defaultString struct { + Desc string + Default string +} + +func DefaultString(desc string, Default string) *defaultString { + return &defaultString{Desc: desc, Default: Default} +} + +func (d defaultString) Description(_ context.Context) string { + return d.Desc +} + +func (d defaultString) MarkdownDescription(_ context.Context) string { + return d.Desc +} + +func (d defaultString) DefaultString(ctx context.Context, request defaults.StringRequest, response *defaults.StringResponse) { + response.PlanValue = types.StringValue(d.Default) +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 0a6dc702..e1e27640 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -60,7 +60,6 @@ func NewSDKProvider(version string) func() *sdkschema.Provider { ResourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": resourceCluster.Schema(), - "biganimal_region": resourceRegion.Schema(), "biganimal_aws_connection": resourceAWSConnection.Schema(), "biganimal_azure_connection": resourceAzureConnection.Schema(), "biganimal_faraway_replica": resourceFAReplica.Schema(), @@ -186,5 +185,6 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. func (b bigAnimalProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewProjectResource, + NewRegionResource, } } diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index d7022a1c..23955069 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 722ba053..7a802ab0 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -7,182 +7,192 @@ import ( "time" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + fdiag "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + fschema "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/tfsdk" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -// RegionResource is a struct to namespace all the functions -// involved in the Region Resource. When multiple resources and objects -// are in the same pkg/provider, then it's difficult to namespace things well -type RegionResource struct{} - -func NewRegionResource() *RegionResource { - return &RegionResource{} +func NewRegionResource() resource.Resource { + return ®ionResource{} } -func (r *RegionResource) Schema() *schema.Resource { - return &schema.Resource{ - Description: "The region resource is used to manage regions for a given cloud provider. See [Activating regions](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/activating_regions/) for more details.", - - CreateContext: r.Create, - ReadContext: r.Read, - UpdateContext: r.Update, - DeleteContext: r.Delete, +type regionResource struct { + client *api.API +} - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(60 * time.Minute), - Update: schema.DefaultTimeout(60 * time.Minute), - Delete: schema.DefaultTimeout(60 * time.Minute), +func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = fschema.Schema{ + MarkdownDescription: "The region resource is used to manage regions for a given cloud provider. See [Activating regions](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/activating_regions/) for more details.", + Blocks: map[string]fschema.Block{ + "timeouts": timeouts.Block(ctx, + timeouts.Opts{Create: true, Delete: true, Update: true}), }, - Schema: map[string]*schema.Schema{ - "cloud_provider": { - Description: "Cloud provider. For example, \"aws\" or \"azure\".", - Type: schema.TypeString, - Required: true, + Attributes: map[string]fschema.Attribute{ + "cloud_provider": fschema.StringAttribute{ + MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", + Required: true, }, - "project_id": { - Description: "BigAnimal Project ID.", - Type: schema.TypeString, - Required: true, - ValidateDiagFunc: validateProjectId, + "project_id": fschema.StringAttribute{ + MarkdownDescription: "BigAnimal Project ID.", + Required: true, + Validators: []validator.String{ + ProjectIdValidator(), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, - "region_id": { - Description: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", - Type: schema.TypeString, - Required: true, + "region_id": fschema.StringAttribute{ + MarkdownDescription: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", + Required: true, }, - "name": { - Description: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", - Type: schema.TypeString, - Computed: true, + "name": fschema.StringAttribute{ + MarkdownDescription: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, - "status": { - Description: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", - Type: schema.TypeString, - Optional: true, - Default: api.REGION_ACTIVE, + "status": fschema.StringAttribute{ + MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", + Optional: true, + Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), }, - "continent": { - Description: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", - Type: schema.TypeString, - Computed: true, + "continent": fschema.StringAttribute{ + MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, }, } } -func (r *RegionResource) Create(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - return r.Update(ctx, d, meta) +type Region struct { + ProjectID string `tfsdk:"project_id"` + CloudProvider string `tfsdk:"cloud_provider"` + RegionID string `tfsdk:"region_id"` + Name string `tfsdk:"name"` + Status string `tfsdk:"status"` + Continent string `tfsdk:"continent"` + + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +func (r regionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_region" } -func (r *RegionResource) Read(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - if err := r.read(ctx, d, meta); err != nil { - return fromBigAnimalErr(err) +func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var config Region + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return } - return diag.Diagnostics{} + + diags = r.update(ctx, config, resp.State) + resp.Diagnostics.Append(diags...) + return } -func (r *RegionResource) read(ctx context.Context, d *schema.ResourceData, meta any) error { - client := api.BuildAPI(meta).RegionClient() - projectId := d.Get("project_id").(string) - cloud_provider := d.Get("cloud_provider").(string) +func (r regionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state Region + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(r.read(ctx, state, resp.State)...) +} - id := d.Get("region_id").(string) - region, err := client.Read(ctx, projectId, cloud_provider, id) +func (r regionResource) read(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { + read, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { - return err + return fromErr(err, "Error reading region %v", region.RegionID) } - utils.SetOrPanic(d, "name", region.Name) - utils.SetOrPanic(d, "status", region.Status) - utils.SetOrPanic(d, "continent", region.Continent) - d.SetId(fmt.Sprintf("%s/%s", cloud_provider, id)) - - return nil + region.Name = read.Name + region.Status = read.Status + region.Continent = read.Continent + return state.Set(ctx, ®ion) } -func (r *RegionResource) Update(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := api.BuildAPI(meta).RegionClient() +func (r regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan Region + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } - cloudProvider := d.Get("cloud_provider").(string) - id := d.Get("region_id").(string) - projectId := d.Get("project_id").(string) - desiredState := d.Get("status").(string) + resp.Diagnostics.Append(r.update(ctx, plan, resp.State)...) +} - region, err := client.Read(ctx, projectId, cloudProvider, id) +func (r regionResource) update(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { + current, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { - return fromBigAnimalErr(err) + return fromErr(err, "Error reading region %v", region.RegionID) + } + if current.Status == region.Status { // no change, exit early + return nil } - utils.SetOrPanic(d, "name", region.Name) - utils.SetOrPanic(d, "continent", region.Continent) - utils.SetOrPanic(d, "status", desiredState) - d.SetId(fmt.Sprintf("%s/%s", cloudProvider, id)) + tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", current.Status, region.Status)) - if desiredState == region.Status { // no change, exit early - return diag.Diagnostics{} + if err := r.client.RegionClient().Update(ctx, region.Status, region.ProjectID, region.CloudProvider, region.RegionID); err != nil { + return fromErr(err, "Error updating region %v", region.RegionID) } - tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", region.Status, desiredState)) - if err = client.Update(ctx, desiredState, projectId, cloudProvider, id); err != nil { - return fromBigAnimalErr(err) + timeout, diagnostics := region.Timeouts.Create(ctx, 60*time.Minute) + if diagnostics != nil { + return diagnostics } - // retry until we get success err = retry.RetryContext( ctx, - d.Timeout(schema.TimeoutCreate)-time.Minute, - r.retryFunc(ctx, d, meta, cloudProvider, id, desiredState)) + timeout-time.Minute, + r.retryFunc(ctx, region)) if err != nil { - return diag.FromErr(err) + return fromErr(err, "") } - return diag.Diagnostics{} -} -func (r *RegionResource) Delete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := api.BuildAPI(meta).RegionClient() + return r.read(ctx, region, state) +} - projectId := d.Get("project_id").(string) - cloudProvider := d.Get("cloud_provider").(string) - id := d.Get("region_id").(string) - desiredState := api.REGION_INACTIVE - if err := client.Update(ctx, api.REGION_INACTIVE, projectId, cloudProvider, id); err != nil { - return fromBigAnimalErr(err) - } +func (r regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + //TODO implement me + panic("implement me") +} - // retry until we get success - err := retry.RetryContext( - ctx, - d.Timeout(schema.TimeoutDelete)-time.Minute, - r.retryFunc(ctx, d, meta, cloudProvider, id, desiredState)) - if err != nil { - return diag.FromErr(err) +func (r regionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return } - return diag.Diagnostics{} + r.client = req.ProviderData.(*api.API) } -func (r *RegionResource) retryFunc(ctx context.Context, d *schema.ResourceData, meta any, cloudProvider, regionId, desiredState string) retry.RetryFunc { - client := api.BuildAPI(meta).RegionClient() +func (r regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { - projectId := d.Get("project_id").(string) - region, err := client.Read(ctx, projectId, cloudProvider, regionId) + curr, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { return retry.NonRetryableError(fmt.Errorf("error describing instance: %s", err)) } - if region.Status != desiredState { + if curr.Status != region.Status { return retry.RetryableError(errors.New("operation incomplete")) } - - if err := r.read(ctx, d, meta); err != nil { - return retry.NonRetryableError(err) - } - return nil } } diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index 583fb65d..ff41799c 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -3,7 +3,10 @@ package provider import ( "errors" + "fmt" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" + diag2 "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) @@ -35,3 +38,12 @@ func unsupportedWarning(message string) diag.Diagnostics { }, } } + +func fromErr(err error, summary string, args ...any) diag2.Diagnostics { + summary = fmt.Sprintf(summary, args...) + return diag2.Diagnostics{ + diag2.NewErrorDiagnostic( + summary, err.Error(), + ), + } +} diff --git a/pkg/provider/validators.go b/pkg/provider/validators.go index d773c4aa..cc194254 100644 --- a/pkg/provider/validators.go +++ b/pkg/provider/validators.go @@ -2,9 +2,13 @@ package provider import ( "fmt" + "regexp" "strings" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/google/uuid" "github.com/aws/aws-sdk-go/aws/arn" @@ -28,6 +32,13 @@ func validateProjectId(v interface{}, path cty.Path) diag.Diagnostics { return diags } +func ProjectIdValidator() validator.String { + return stringvalidator.RegexMatches( + regexp.MustCompile("^prj_[0-9A-Za-z_]{16}$"), + "Please provide a valid name for the project_id, for example: prj_abcdABCD01234567", + ) +} + func validateARN(v interface{}, _ cty.Path) diag.Diagnostics { a, err := arn.Parse(v.(string)) if err != nil || a.Service != "iam" || !strings.HasPrefix(a.Resource, "role") { From 82eff4e10de94a1ccb73ae5c78d4f9e7f5ee3286 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:05:17 +0800 Subject: [PATCH 17/49] refactor: rewrite datasource_region with terraform-plugin-framework --- pkg/provider/data_source_region.go | 149 ++++++++++++++++------------- pkg/provider/provider.go | 4 +- pkg/provider/resource_region.go | 31 +++++- 3 files changed, 114 insertions(+), 70 deletions(-) diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go index 397710a7..deee430f 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_region.go @@ -2,114 +2,133 @@ package provider import ( "context" - "errors" - "fmt" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/utils" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) -// RegionResource is a struct to namespace all the functions -// involved in the Region Resource. When multiple resources and objects -// are in the same pkg/provider, then it's difficult to namespace things well -type RegionData struct{} +// NewRegionDataSource is a helper function to simplify the provider implementation. +func NewRegionDataSource() datasource.DataSource { + return ®ionDataSource{} +} + +// regionDataSource is the data source implementation. +type regionDataSource struct { + client *api.API +} + +// Configure adds the provider configured client to the data source. +func (r *regionDataSource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } -func NewRegionData() *RegionData { - return &RegionData{} + r.client = req.ProviderData.(*api.API) } -func (r *RegionData) Schema() *schema.Resource { - return &schema.Resource{ - Description: "The region data source shows the available regions within a cloud provider.", - ReadContext: r.Read, +func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_region" + +} - //{ - // "regionId": "eu-west-1", - // "regionName": "EU West 1", - // "status": "ACTIVE", - // "continent": "Europe" - //} - Schema: map[string]*schema.Schema{ - "regions": { +func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "", + Attributes: map[string]schema.Attribute{ + "regions": schema.ListNestedAttribute{ Description: "Region information.", - Type: schema.TypeList, Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "region_id": { + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "region_id": schema.StringAttribute{ Description: "Region ID of the region.", - Type: schema.TypeString, Computed: true, }, - "name": { + "name": schema.StringAttribute{ Description: "Region name of the region.", - Type: schema.TypeString, Computed: true, }, - "status": { + "status": schema.StringAttribute{ Description: "Region status of the region.", - Type: schema.TypeString, Computed: true, }, - "continent": { + "continent": schema.StringAttribute{ Description: "Continent that region belongs to.", - Type: schema.TypeString, Computed: true, }, }, }, }, - "cloud_provider": { + + "cloud_provider": schema.StringAttribute{ Description: "Cloud provider to list the regions. For example, \"aws\" or \"azure\".", - Type: schema.TypeString, Required: true, }, - "project_id": { - Description: "BigAnimal Project ID.", - Type: schema.TypeString, - Required: true, - ValidateDiagFunc: validateProjectId, + "project_id": schema.StringAttribute{ + Description: "BigAnimal Project ID.", + Required: true, + Validators: []validator.String{ + ProjectIdValidator(), + }, }, - "query": { + "query": schema.StringAttribute{ Description: "Query to filter region list.", - Type: schema.TypeString, Optional: true, }, - "region_id": { + "region_id": schema.StringAttribute{ Description: "Unique region ID. For example, \"germanywestcentral\" in the Azure cloud provider, \"eu-west-1\" in the AWS cloud provider.", - Type: schema.TypeString, Optional: true, }, }, } } -func (r *RegionData) Read(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - diags := diag.Diagnostics{} - client := api.BuildAPI(meta).RegionClient() - cloud_provider := d.Get("cloud_provider").(string) - projectId := d.Get("project_id").(string) - - query := d.Get("query").(string) +type regionDatasource struct { + Regions []Region `tfsdk:"regions,omitempty"` + CloudProvider string `tfsdk:"cloudProvider,omitempty"` + ProjectId string `tfsdk:"projectId,omitempty"` + Query string `tfsdk:"query,omitempty"` + RegionId string `tfsdk:"regionId,omitempty"` +} - id, ok := d.Get("region_id").(string) - if ok { - query = id +func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var cfg regionDatasource + diags := req.Config.Get(ctx, &cfg) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return } - regions, err := client.List(ctx, projectId, cloud_provider, query) - if err != nil { - return fromBigAnimalErr(err) - } + regions := []*models.Region{} + if cfg.RegionId != "" { + region, err := r.client.RegionClient().Read(ctx, cfg.ProjectId, cfg.CloudProvider, cfg.RegionId) + if err != nil { + resp.Diagnostics.Append(fromErr(err, "Error reading region by id: %v", cfg.RegionId)...) + return + } + regions = append(regions, region) - if id != "" && len(regions) != 1 { - return diag.FromErr(errors.New("unable to find a unique region")) + } else { + respRegions, err := r.client.RegionClient().List(ctx, cfg.ProjectId, cfg.CloudProvider, cfg.Query) + if err != nil { + return + } + regions = respRegions } - utils.SetOrPanic(d, "regions", regions) - d.SetId(fmt.Sprintf("%s/%s", cloud_provider, query)) + for _, region := range regions { + cfg.Regions = append(cfg.Regions, Region{ + ProjectID: cfg.ProjectId, + CloudProvider: cfg.CloudProvider, + RegionID: region.Id, + Name: region.Name, + Status: region.Status, + Continent: region.Continent, + }) + } - return diags + resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index e1e27640..9f0edbae 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -17,13 +17,11 @@ import ( const DefaultAPIURL = "https://portal.biganimal.com/api/v3" var ( - resourceRegion = NewRegionResource() resourceCluster = NewClusterResource() resourceAWSConnection = NewAWSConnectionResource() resourceAzureConnection = NewAzureConnectionResource() resourceFAReplica = NewFAReplicaResource() - dataRegion = NewRegionData() dataCluster = NewClusterData() dataAWSConnection = NewAWSConnectionData() dataFaReplica = NewFAReplicaData() @@ -53,7 +51,6 @@ func NewSDKProvider(version string) func() *sdkschema.Provider { }, DataSourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": dataCluster.Schema(), - "biganimal_region": dataRegion.Schema(), "biganimal_faraway_replica": dataFaReplica.Schema(), "biganimal_aws_connection": dataAWSConnection.Schema(), }, @@ -179,6 +176,7 @@ func (b bigAnimalProvider) Schema(ctx context.Context, request provider.SchemaRe func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewProjectsDataSource, + NewRegionDataSource, } } diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 7a802ab0..4fede469 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -171,8 +171,35 @@ func (r regionResource) update(ctx context.Context, region Region, state tfsdk.S } func (r regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - //TODO implement me - panic("implement me") + var state Region + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + if state.Status == api.REGION_INACTIVE { + return + } + + if err := r.client.RegionClient().Update(ctx, api.REGION_INACTIVE, state.ProjectID, state.CloudProvider, state.RegionID); err != nil { + resp.Diagnostics.Append(fromErr(err, "Error deleting region %v", state.RegionID)...) + return + } + + timeout, diagnostics := state.Timeouts.Create(ctx, 60*time.Minute) + resp.Diagnostics.Append(diagnostics...) + if resp.Diagnostics.HasError() { + return + } + + err := retry.RetryContext( + ctx, + timeout-time.Minute, + r.retryFunc(ctx, state)) + if err != nil { + resp.Diagnostics.Append(fromErr(err, "")...) + } } func (r regionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { From fad9a2fefcb01a69ee00e17d298bf58f92bcfab3 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 8 Jun 2023 16:25:29 +0800 Subject: [PATCH 18/49] fix: Fixed golint --- pkg/provider/resource_region.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 4fede469..164cd338 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -27,7 +27,7 @@ type regionResource struct { client *api.API } -func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = fschema.Schema{ MarkdownDescription: "The region resource is used to manage regions for a given cloud provider. See [Activating regions](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/activating_regions/) for more details.", Blocks: map[string]fschema.Block{ @@ -88,11 +88,11 @@ type Region struct { Timeouts timeouts.Value `tfsdk:"timeouts"` } -func (r regionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *regionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_region" } -func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { +func (r *regionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var config Region diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) @@ -102,10 +102,9 @@ func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, diags = r.update(ctx, config, resp.State) resp.Diagnostics.Append(diags...) - return } -func (r regionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var state Region diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) @@ -116,7 +115,7 @@ func (r regionResource) Read(ctx context.Context, req resource.ReadRequest, resp resp.Diagnostics.Append(r.read(ctx, state, resp.State)...) } -func (r regionResource) read(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { +func (r *regionResource) read(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { read, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) @@ -128,7 +127,7 @@ func (r regionResource) read(ctx context.Context, region Region, state tfsdk.Sta return state.Set(ctx, ®ion) } -func (r regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { +func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { var plan Region diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) @@ -139,7 +138,7 @@ func (r regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp.Diagnostics.Append(r.update(ctx, plan, resp.State)...) } -func (r regionResource) update(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { +func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { current, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) @@ -170,7 +169,7 @@ func (r regionResource) update(ctx context.Context, region Region, state tfsdk.S return r.read(ctx, region, state) } -func (r regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { +func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var state Region diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) @@ -202,7 +201,7 @@ func (r regionResource) Delete(ctx context.Context, req resource.DeleteRequest, } } -func (r regionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +func (r *regionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return } @@ -210,7 +209,7 @@ func (r regionResource) Configure(ctx context.Context, req resource.ConfigureReq r.client = req.ProviderData.(*api.API) } -func (r regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { +func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { curr, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) if err != nil { From d94579780fad80d7f44f7e4e47b4cf4688ec34a0 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 15 Jun 2023 17:30:13 +0800 Subject: [PATCH 19/49] fix: Removed useless code --- pkg/provider/provider.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 73196a15..5c627faf 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -52,14 +52,12 @@ func NewSDKProvider(version string) func() *sdkschema.Provider { }, DataSourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": dataCluster.Schema(), - "biganimal_region": dataRegion.Schema(), "biganimal_faraway_replica": dataFaReplica.Schema(), "biganimal_aws_connection": dataAWSConnection.Schema(), }, ResourcesMap: map[string]*sdkschema.Resource{ "biganimal_cluster": resourceCluster.Schema(), - "biganimal_region": resourceRegion.Schema(), "biganimal_aws_connection": resourceAWSConnection.Schema(), "biganimal_azure_connection": resourceAzureConnection.Schema(), "biganimal_faraway_replica": resourceFAReplica.Schema(), From cef8a4bea4ef1a1034505584eaa1b8e84b5f674b Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 15 Jun 2023 17:34:59 +0800 Subject: [PATCH 20/49] fix: Updated name to biganimal_regions --- pkg/provider/data_source_region.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go index deee430f..0f4ad202 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_region.go @@ -30,8 +30,7 @@ func (r *regionDataSource) Configure(_ context.Context, req resource.ConfigureRe } func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_region" - + resp.TypeName = req.ProviderTypeName + "_regions" } func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { From 298a2f4af9fcac758a85ce11632d5262512c37a8 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 15 Jun 2023 19:43:44 +0800 Subject: [PATCH 21/49] fix: Fixed datasource regions --- pkg/models/region.go | 8 ++-- pkg/provider/data_source_region.go | 33 +++++++--------- pkg/provider/provider.go | 2 +- pkg/provider/resource_region.go | 60 +++++++++++++++--------------- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/pkg/models/region.go b/pkg/models/region.go index 4ee7d740..14ce0c81 100644 --- a/pkg/models/region.go +++ b/pkg/models/region.go @@ -1,10 +1,10 @@ package models type Region struct { - Id string `json:"regionId,omitempty" mapstructure:"region_id"` - Name string `json:"regionName,omitempty" mapstructure:"name,omitempty"` - Status string `json:"status,omitempty" mapstructure:"status,omitempty"` - Continent string `json:"continent,omitempty" mapstructure:"continent,omitempty"` + Id string `json:"regionId,omitempty" tfsdk:"region_id"` + Name string `json:"regionName,omitempty" tfsdk:"name"` + Status string `json:"status,omitempty" tfsdk:"status"` + Continent string `json:"continent,omitempty" tfsdk:"continent"` } func (r Region) String() string { diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go index 0f4ad202..ad4dbf63 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_region.go @@ -6,8 +6,8 @@ import ( "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" ) // NewRegionDataSource is a helper function to simplify the provider implementation. @@ -17,16 +17,16 @@ func NewRegionDataSource() datasource.DataSource { // regionDataSource is the data source implementation. type regionDataSource struct { - client *api.API + client *api.RegionClient } // Configure adds the provider configured client to the data source. -func (r *regionDataSource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { +func (r *regionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { if req.ProviderData == nil { return } - r.client = req.ProviderData.(*api.API) + r.client = req.ProviderData.(*api.API).RegionClient() } func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { @@ -86,11 +86,11 @@ func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequ } type regionDatasource struct { - Regions []Region `tfsdk:"regions,omitempty"` - CloudProvider string `tfsdk:"cloudProvider,omitempty"` - ProjectId string `tfsdk:"projectId,omitempty"` - Query string `tfsdk:"query,omitempty"` - RegionId string `tfsdk:"regionId,omitempty"` + Regions []*models.Region `tfsdk:"regions"` + CloudProvider *string `tfsdk:"cloud_provider"` + ProjectId *string `tfsdk:"project_id"` + Query types.String `tfsdk:"query"` + RegionId *string `tfsdk:"region_id"` } func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { @@ -102,8 +102,8 @@ func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, } regions := []*models.Region{} - if cfg.RegionId != "" { - region, err := r.client.RegionClient().Read(ctx, cfg.ProjectId, cfg.CloudProvider, cfg.RegionId) + if cfg.RegionId != nil { + region, err := r.client.Read(ctx, *cfg.ProjectId, *cfg.CloudProvider, *cfg.RegionId) if err != nil { resp.Diagnostics.Append(fromErr(err, "Error reading region by id: %v", cfg.RegionId)...) return @@ -111,7 +111,7 @@ func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, regions = append(regions, region) } else { - respRegions, err := r.client.RegionClient().List(ctx, cfg.ProjectId, cfg.CloudProvider, cfg.Query) + respRegions, err := r.client.List(ctx, *cfg.ProjectId, *cfg.CloudProvider, cfg.Query.ValueString()) if err != nil { return } @@ -119,14 +119,7 @@ func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, } for _, region := range regions { - cfg.Regions = append(cfg.Regions, Region{ - ProjectID: cfg.ProjectId, - CloudProvider: cfg.CloudProvider, - RegionID: region.Id, - Name: region.Name, - Status: region.Status, - Continent: region.Continent, - }) + cfg.Regions = append(cfg.Regions, region) } resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 5c627faf..a756f820 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -184,6 +184,6 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. func (b bigAnimalProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewProjectResource, - NewRegionResource, + //NewRegionResource, } } diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 164cd338..e93adffe 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -8,9 +8,9 @@ import ( "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" - fdiag "github.com/hashicorp/terraform-plugin-framework/diag" + frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" - fschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + frameworkschema "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" @@ -28,19 +28,19 @@ type regionResource struct { } func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = fschema.Schema{ + resp.Schema = frameworkschema.Schema{ MarkdownDescription: "The region resource is used to manage regions for a given cloud provider. See [Activating regions](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/activating_regions/) for more details.", - Blocks: map[string]fschema.Block{ + Blocks: map[string]frameworkschema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{Create: true, Delete: true, Update: true}), }, - Attributes: map[string]fschema.Attribute{ - "cloud_provider": fschema.StringAttribute{ + Attributes: map[string]frameworkschema.Attribute{ + "cloud_provider": frameworkschema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", Required: true, }, - "project_id": fschema.StringAttribute{ + "project_id": frameworkschema.StringAttribute{ MarkdownDescription: "BigAnimal Project ID.", Required: true, Validators: []validator.String{ @@ -50,23 +50,23 @@ func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, stringplanmodifier.UseStateForUnknown(), }, }, - "region_id": fschema.StringAttribute{ + "region_id": frameworkschema.StringAttribute{ MarkdownDescription: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", Required: true, }, - "name": fschema.StringAttribute{ + "name": frameworkschema.StringAttribute{ MarkdownDescription: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, }, - "status": fschema.StringAttribute{ + "status": frameworkschema.StringAttribute{ MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", Optional: true, Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), }, - "continent": fschema.StringAttribute{ + "continent": frameworkschema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", Computed: true, PlanModifiers: []planmodifier.String{ @@ -78,12 +78,12 @@ func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, } type Region struct { - ProjectID string `tfsdk:"project_id"` - CloudProvider string `tfsdk:"cloud_provider"` - RegionID string `tfsdk:"region_id"` - Name string `tfsdk:"name"` - Status string `tfsdk:"status"` - Continent string `tfsdk:"continent"` + ProjectID *string `tfsdk:"project_id"` + CloudProvider *string `tfsdk:"cloud_provider"` + RegionID *string `tfsdk:"region_id"` + Name *string `tfsdk:"name"` + Status *string `tfsdk:"status"` + Continent *string `tfsdk:"continent"` Timeouts timeouts.Value `tfsdk:"timeouts"` } @@ -115,15 +115,15 @@ func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, res resp.Diagnostics.Append(r.read(ctx, state, resp.State)...) } -func (r *regionResource) read(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { - read, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) +func (r *regionResource) read(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { + read, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } - region.Name = read.Name - region.Status = read.Status - region.Continent = read.Continent + region.Name = &read.Name + region.Status = &read.Status + region.Continent = &read.Continent return state.Set(ctx, ®ion) } @@ -138,18 +138,18 @@ func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp.Diagnostics.Append(r.update(ctx, plan, resp.State)...) } -func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) fdiag.Diagnostics { - current, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) +func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { + current, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } - if current.Status == region.Status { // no change, exit early + if current.Status == *region.Status { // no change, exit early return nil } tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", current.Status, region.Status)) - if err := r.client.RegionClient().Update(ctx, region.Status, region.ProjectID, region.CloudProvider, region.RegionID); err != nil { + if err := r.client.RegionClient().Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { return fromErr(err, "Error updating region %v", region.RegionID) } @@ -177,11 +177,11 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, return } - if state.Status == api.REGION_INACTIVE { + if *state.Status == api.REGION_INACTIVE { return } - if err := r.client.RegionClient().Update(ctx, api.REGION_INACTIVE, state.ProjectID, state.CloudProvider, state.RegionID); err != nil { + if err := r.client.RegionClient().Update(ctx, api.REGION_INACTIVE, *state.ProjectID, *state.CloudProvider, *state.RegionID); err != nil { resp.Diagnostics.Append(fromErr(err, "Error deleting region %v", state.RegionID)...) return } @@ -211,12 +211,12 @@ func (r *regionResource) Configure(ctx context.Context, req resource.ConfigureRe func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { - curr, err := r.client.RegionClient().Read(ctx, region.ProjectID, region.CloudProvider, region.RegionID) + curr, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return retry.NonRetryableError(fmt.Errorf("error describing instance: %s", err)) } - if curr.Status != region.Status { + if curr.Status != *region.Status { return retry.RetryableError(errors.New("operation incomplete")) } return nil From a0fa7d43f19c6098341d86deedde60d3ff7075f6 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Fri, 23 Jun 2023 17:30:10 +0200 Subject: [PATCH 22/49] fix: Staticcheck S1011 https://staticcheck.io/docs/checks#S1011 S1011 - Use a single append to concatenate two slices --- pkg/provider/data_source_region.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go index ad4dbf63..2c6eef88 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_region.go @@ -2,6 +2,7 @@ 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/datasource" @@ -118,9 +119,7 @@ func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, regions = respRegions } - for _, region := range regions { - cfg.Regions = append(cfg.Regions, region) - } + cfg.Regions = append(cfg.Regions, regions...) resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } From 4b722487b0e7d799c1e1a2ba72f5ff7bfa042f80 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Fri, 23 Jun 2023 17:31:23 +0200 Subject: [PATCH 23/49] feat: Further work on Region resource * Reordered the file contents * Use RegionClient for the CRUD operations * Use schema instead of frameworkschema, there is only frameworkschema here in the end * various pointer juggling, the code is not tested well --- pkg/provider/provider.go | 2 +- pkg/provider/resource_region.go | 75 ++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 6f2981b7..7c0bc665 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -184,6 +184,6 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. func (b bigAnimalProvider) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewProjectResource, - //NewRegionResource, + NewRegionResource, } } diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index e93adffe..71570b43 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" - frameworkschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "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" @@ -19,28 +19,28 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" ) -func NewRegionResource() resource.Resource { - return ®ionResource{} +type regionResource struct { + client *api.RegionClient } -type regionResource struct { - client *api.API +func (r regionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_region" } -func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = frameworkschema.Schema{ +func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ MarkdownDescription: "The region resource is used to manage regions for a given cloud provider. See [Activating regions](https://www.enterprisedb.com/docs/biganimal/latest/getting_started/activating_regions/) for more details.", - Blocks: map[string]frameworkschema.Block{ + Blocks: map[string]schema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{Create: true, Delete: true, Update: true}), }, - Attributes: map[string]frameworkschema.Attribute{ - "cloud_provider": frameworkschema.StringAttribute{ + Attributes: map[string]schema.Attribute{ + "cloud_provider": schema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", Required: true, }, - "project_id": frameworkschema.StringAttribute{ + "project_id": schema.StringAttribute{ MarkdownDescription: "BigAnimal Project ID.", Required: true, Validators: []validator.String{ @@ -50,23 +50,30 @@ func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, stringplanmodifier.UseStateForUnknown(), }, }, - "region_id": frameworkschema.StringAttribute{ + "region_id": schema.StringAttribute{ MarkdownDescription: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", Required: true, }, - "name": frameworkschema.StringAttribute{ + "name": schema.StringAttribute{ MarkdownDescription: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, }, - "status": frameworkschema.StringAttribute{ + "status": schema.StringAttribute{ MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", Optional: true, - Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), + /* + Commented out the following Default field. During compilation, the following error occurs: + error retrieving schema for *proto6server.Server: + Attribute: + Summary: Schema Using Attribute Default For Non-Computed Attribute + Detail: Attribute "status" must be computed when using default. This is an issue with the provider and should be reported to the provider developers. + */ + //Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), }, - "continent": frameworkschema.StringAttribute{ + "continent": schema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", Computed: true, PlanModifiers: []planmodifier.String{ @@ -77,6 +84,14 @@ func (r *regionResource) Schema(ctx context.Context, req resource.SchemaRequest, } } +func (r *regionResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*api.API).RegionClient() +} + type Region struct { ProjectID *string `tfsdk:"project_id"` CloudProvider *string `tfsdk:"cloud_provider"` @@ -88,11 +103,7 @@ type Region struct { Timeouts timeouts.Value `tfsdk:"timeouts"` } -func (r *regionResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_region" -} - -func (r *regionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { +func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var config Region diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) @@ -116,7 +127,7 @@ func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, res } func (r *regionResource) read(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { - read, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) + read, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } @@ -139,7 +150,7 @@ func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, } func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { - current, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) + current, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } @@ -147,9 +158,9 @@ func (r *regionResource) update(ctx context.Context, region Region, state tfsdk. return nil } - tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", current.Status, region.Status)) + tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", current.Status, *region.Status)) - if err := r.client.RegionClient().Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { + if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { return fromErr(err, "Error updating region %v", region.RegionID) } @@ -181,7 +192,7 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, return } - if err := r.client.RegionClient().Update(ctx, api.REGION_INACTIVE, *state.ProjectID, *state.CloudProvider, *state.RegionID); err != nil { + if err := r.client.Update(ctx, api.REGION_INACTIVE, *state.ProjectID, *state.CloudProvider, *state.RegionID); err != nil { resp.Diagnostics.Append(fromErr(err, "Error deleting region %v", state.RegionID)...) return } @@ -201,14 +212,6 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, } } -func (r *regionResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - - r.client = req.ProviderData.(*api.API) -} - func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { curr, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) @@ -222,3 +225,7 @@ func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.Ret return nil } } + +func NewRegionResource() resource.Resource { + return ®ionResource{} +} From 481ed6e633eed4ac97e5acc03556022042361039 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Fri, 23 Jun 2023 17:44:53 +0200 Subject: [PATCH 24/49] style: whitespace cosmetics --- pkg/provider/utils.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index a9bdcbe2..ff41799c 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -3,7 +3,6 @@ package provider import ( "errors" - "fmt" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" From 53f3af97042633924c343536bf77079053cfd06d Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Mon, 26 Jun 2023 17:16:56 +0800 Subject: [PATCH 25/49] fix: Changed datasource region to regions for naming conversion --- docs/data-sources/pgd.md | 1 + docs/data-sources/region.md | 64 ------------------- docs/data-sources/regions.md | 31 +++++++++ docs/resources/region.md | 1 - ...ource_region.go => data_source_regions.go} | 4 +- pkg/provider/provider.go | 2 +- .../{region.md.tmpl => regions.md.tmpl} | 0 7 files changed, 35 insertions(+), 68 deletions(-) delete mode 100644 docs/data-sources/region.md create mode 100644 docs/data-sources/regions.md rename pkg/provider/{data_source_region.go => data_source_regions.go} (96%) rename templates/data-sources/{region.md.tmpl => regions.md.tmpl} (100%) diff --git a/docs/data-sources/pgd.md b/docs/data-sources/pgd.md index 582521bc..112c0207 100644 --- a/docs/data-sources/pgd.md +++ b/docs/data-sources/pgd.md @@ -61,6 +61,7 @@ output "witness_groups" { - `cluster_id` (String) Cluster ID. - `data_groups` (Attributes Set) Cluster data groups. (see [below for nested schema](#nestedatt--data_groups)) +- `id` (String) Datasource ID. - `witness_groups` (Attributes Set) Cluster witness groups. (see [below for nested schema](#nestedatt--witness_groups)) diff --git a/docs/data-sources/region.md b/docs/data-sources/region.md deleted file mode 100644 index fa2537e5..00000000 --- a/docs/data-sources/region.md +++ /dev/null @@ -1,64 +0,0 @@ -# biganimal_region (Data Source) -The region data source shows the available regions within a cloud provider. - -## Example Usage -```terraform -variable "cloud_provider" { - type = string - description = "Cloud Provider" - - validation { - condition = contains(["aws", "azure"], var.cloud_provider) - error_message = "Please select one of the supported regions: aws, azure." - } -} - -variable "project_id" { - type = string - description = "BigAnimal Project ID" - -} - -data "biganimal_region" "this" { - cloud_provider = var.cloud_provider - project_id = var.project_id - // region_id = "us-west-1" //optional - // query = "eu" // optional -} - -output "regions" { - value = data.biganimal_region.this.regions -} - -output "cloud_provider_id" { - value = data.biganimal_region.this.cloud_provider -} -``` - - -## Schema - -### Required - -- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws" or "azure". -- `project_id` (String) BigAnimal Project ID. - -### Optional - -- `query` (String) Query to filter region list. -- `region_id` (String) Unique region ID. For example, "germanywestcentral" in the Azure cloud provider, "eu-west-1" in the AWS cloud provider. - -### Read-Only - -- `id` (String) The ID of this resource. -- `regions` (List of Object) Region information. (see [below for nested schema](#nestedatt--regions)) - - -### Nested Schema for `regions` - -Read-Only: - -- `continent` (String) -- `name` (String) -- `region_id` (String) -- `status` (String) diff --git a/docs/data-sources/regions.md b/docs/data-sources/regions.md new file mode 100644 index 00000000..4f55e0f2 --- /dev/null +++ b/docs/data-sources/regions.md @@ -0,0 +1,31 @@ +# biganimal_regions (Data Source) + + + + + +## Schema + +### Required + +- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws" or "azure". +- `project_id` (String) BigAnimal Project ID. + +### Optional + +- `query` (String) Query to filter region list. +- `region_id` (String) Unique region ID. For example, "germanywestcentral" in the Azure cloud provider, "eu-west-1" in the AWS cloud provider. + +### Read-Only + +- `regions` (Attributes List) Region information. (see [below for nested schema](#nestedatt--regions)) + + +### Nested Schema for `regions` + +Read-Only: + +- `continent` (String) Continent that region belongs to. +- `name` (String) Region name of the region. +- `region_id` (String) Region ID of the region. +- `status` (String) Region status of the region. diff --git a/docs/resources/region.md b/docs/resources/region.md index 26b333d2..303c2202 100644 --- a/docs/resources/region.md +++ b/docs/resources/region.md @@ -55,7 +55,6 @@ output "region_continent" { ### Read-Only - `continent` (String) Continent that region belongs to. For example, "Asia", "Australia", or "Europe". -- `id` (String) The ID of this resource. - `name` (String) Region name of the region. For example, "Germany West Central" or "EU West 1". diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_regions.go similarity index 96% rename from pkg/provider/data_source_region.go rename to pkg/provider/data_source_regions.go index 2c6eef88..1a6b5269 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_regions.go @@ -11,8 +11,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// NewRegionDataSource is a helper function to simplify the provider implementation. -func NewRegionDataSource() datasource.DataSource { +// NewRegionsDataSource is a helper function to simplify the provider implementation. +func NewRegionsDataSource() datasource.DataSource { return ®ionDataSource{} } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 7c0bc665..176c7766 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -177,7 +177,7 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. return []func() datasource.DataSource{ NewProjectsDataSource, NewPgdDataSource, - NewRegionDataSource, + NewRegionsDataSource, } } diff --git a/templates/data-sources/region.md.tmpl b/templates/data-sources/regions.md.tmpl similarity index 100% rename from templates/data-sources/region.md.tmpl rename to templates/data-sources/regions.md.tmpl From ae0773efa92281c83f906ca4a2cb3fde87286234 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 27 Jun 2023 11:42:26 +0800 Subject: [PATCH 26/49] feat: Added import for region resource --- pkg/provider/resource_region.go | 44 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 71570b43..61e1c489 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-framework/path" + "strings" "time" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" @@ -15,7 +17,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" ) @@ -36,6 +37,10 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, }, Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "Resource ID of the region.", + Computed: true, + }, "cloud_provider": schema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", Required: true, @@ -93,6 +98,7 @@ func (r *regionResource) Configure(_ context.Context, req resource.ConfigureRequ } type Region struct { + ID *string `tfsdk:"id"` ProjectID *string `tfsdk:"project_id"` CloudProvider *string `tfsdk:"cloud_provider"` RegionID *string `tfsdk:"region_id"` @@ -123,15 +129,16 @@ func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, res return } - resp.Diagnostics.Append(r.read(ctx, state, resp.State)...) + resp.Diagnostics.Append(r.read(ctx, state, &resp.State)...) } -func (r *regionResource) read(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { +func (r *regionResource) read(ctx context.Context, region Region, state *tfsdk.State) frameworkdiag.Diagnostics { read, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } - + id := fmt.Sprintf("%s/%s/%s", *region.ProjectID, *region.CloudProvider, *region.RegionID) + region.ID = &id region.Name = &read.Name region.Status = &read.Status region.Continent = &read.Continent @@ -150,16 +157,6 @@ func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, } func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { - current, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) - if err != nil { - return fromErr(err, "Error reading region %v", region.RegionID) - } - if current.Status == *region.Status { // no change, exit early - return nil - } - - tflog.Debug(ctx, fmt.Sprintf("updating region from %s to %s", current.Status, *region.Status)) - if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { return fromErr(err, "Error updating region %v", region.RegionID) } @@ -169,7 +166,7 @@ func (r *regionResource) update(ctx context.Context, region Region, state tfsdk. return diagnostics } - err = retry.RetryContext( + err := retry.RetryContext( ctx, timeout-time.Minute, r.retryFunc(ctx, region)) @@ -177,7 +174,7 @@ func (r *regionResource) update(ctx context.Context, region Region, state tfsdk. return fromErr(err, "") } - return r.read(ctx, region, state) + return r.read(ctx, region, &state) } func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -226,6 +223,21 @@ func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.Ret } } +func (r regionResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idParts := strings.Split(req.ID, "/") + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("Expected import identifier with format: project_id/cloud_provider/region_id. Got: %q", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cloud_provider"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region_id"), idParts[2])...) +} + func NewRegionResource() resource.Resource { return ®ionResource{} } From a355f436dcabaddc450adce786cceebe1aee072b Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 27 Jun 2023 14:18:48 +0800 Subject: [PATCH 27/49] fix: Add PlanModifiers for id attr --- pkg/provider/resource_region.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 61e1c489..8ac2b65c 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -40,6 +40,9 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "id": schema.StringAttribute{ MarkdownDescription: "Resource ID of the region.", Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "cloud_provider": schema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", @@ -117,7 +120,7 @@ func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, return } - diags = r.update(ctx, config, resp.State) + diags = r.update(ctx, config, &resp.State) resp.Diagnostics.Append(diags...) } @@ -153,10 +156,10 @@ func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, return } - resp.Diagnostics.Append(r.update(ctx, plan, resp.State)...) + resp.Diagnostics.Append(r.update(ctx, plan, &resp.State)...) } -func (r *regionResource) update(ctx context.Context, region Region, state tfsdk.State) frameworkdiag.Diagnostics { +func (r *regionResource) update(ctx context.Context, region Region, state *tfsdk.State) frameworkdiag.Diagnostics { if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { return fromErr(err, "Error updating region %v", region.RegionID) } @@ -174,7 +177,7 @@ func (r *regionResource) update(ctx context.Context, region Region, state tfsdk. return fromErr(err, "") } - return r.read(ctx, region, &state) + return r.read(ctx, region, state) } func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { From 3964ac35617b545d76a4d57fe0e190468ca411a3 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 27 Jun 2023 15:57:54 +0800 Subject: [PATCH 28/49] fix: Keep datasource_region existing before releasing major version --- pkg/provider/data_source_regions.go | 36 ++++++++++++++++++++++------- pkg/provider/provider.go | 1 + 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index 1a6b5269..ed1512b9 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -11,18 +11,20 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +var _ datasource.DataSourceWithConfigure = ®ionsDataSource{} + // NewRegionsDataSource is a helper function to simplify the provider implementation. func NewRegionsDataSource() datasource.DataSource { - return ®ionDataSource{} + return ®ionsDataSource{} } -// regionDataSource is the data source implementation. -type regionDataSource struct { +// regionsDataSource is the data source implementation. +type regionsDataSource struct { client *api.RegionClient } // Configure adds the provider configured client to the data source. -func (r *regionDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { +func (r *regionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { if req.ProviderData == nil { return } @@ -30,13 +32,12 @@ func (r *regionDataSource) Configure(_ context.Context, req datasource.Configure r.client = req.ProviderData.(*api.API).RegionClient() } -func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { +func (r *regionsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_regions" } -func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { +func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "", Attributes: map[string]schema.Attribute{ "regions": schema.ListNestedAttribute{ Description: "Region information.", @@ -94,7 +95,7 @@ type regionDatasource struct { RegionId *string `tfsdk:"region_id"` } -func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { +func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var cfg regionDatasource diags := req.Config.Get(ctx, &cfg) resp.Diagnostics.Append(diags...) @@ -123,3 +124,22 @@ func (r *regionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } + +// NewRegionDataSource is a helper function to simplify the provider implementation. +func NewRegionDataSource() datasource.DataSource { + return ®ionDataSource{} +} + +// regionDataSource is the data source implementation. +type regionDataSource struct { + regionsDataSource +} + +func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_region" +} + +func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + r.regionsDataSource.Schema(ctx, req, resp) + resp.Schema.DeprecationMessage = "The datasource' 'region' is deprecated and will be removed in the next major version. Please use 'regions' instead." +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 176c7766..2cfd5554 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -178,6 +178,7 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. NewProjectsDataSource, NewPgdDataSource, NewRegionsDataSource, + NewRegionDataSource, } } From b12f3d70001315e6115e5f74d079f83a6a3c2cf1 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 27 Jun 2023 16:59:54 +0800 Subject: [PATCH 29/49] test: Added acctest for region datasource --- pkg/provider/data_source_regions.go | 10 ++++- pkg/provider/data_source_regions_test.go | 52 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 pkg/provider/data_source_regions_test.go diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index ed1512b9..d491bec1 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -2,6 +2,8 @@ package provider import ( "context" + "strconv" + "time" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" @@ -39,6 +41,10 @@ func (r *regionsDataSource) Metadata(ctx context.Context, req datasource.Metadat func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Datasource ID.", + Computed: true, + }, "regions": schema.ListNestedAttribute{ Description: "Region information.", Computed: true, @@ -88,6 +94,7 @@ func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaReq } type regionDatasource struct { + ID *string `tfsdk:"id"` Regions []*models.Region `tfsdk:"regions"` CloudProvider *string `tfsdk:"cloud_provider"` ProjectId *string `tfsdk:"project_id"` @@ -121,7 +128,8 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest } cfg.Regions = append(cfg.Regions, regions...) - + resourceID := strconv.FormatInt(time.Now().Unix(), 10) + cfg.ID = &resourceID resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } diff --git a/pkg/provider/data_source_regions_test.go b/pkg/provider/data_source_regions_test.go new file mode 100644 index 00000000..50c69a8b --- /dev/null +++ b/pkg/provider/data_source_regions_test.go @@ -0,0 +1,52 @@ +package provider + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceRegions_basic(t *testing.T) { + var ( + acc_env_vars_checklist = []string{ + "BA_TF_ACC_VAR_datasource_regions_project_id", + "BA_TF_ACC_VAR_datasource_regions_provider", + "BA_TF_ACC_VAR_datasource_regions_region_id", + "BA_TF_ACC_VAR_datasource_regions_status", + } + projectId = os.Getenv("BA_TF_ACC_VAR_datasource_regions_project_id") + provider = os.Getenv("BA_TF_ACC_VAR_datasource_regions_provider") + regionId = os.Getenv("BA_TF_ACC_VAR_datasource_regions_region_id") + regionName = os.Getenv("BA_TF_ACC_VAR_datasource_regions_region_name") + status = os.Getenv("BA_TF_ACC_VAR_datasource_regions_status") + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccResourcePreCheck(t, "datasource regions", acc_env_vars_checklist) + }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: regionsDataSourceConfig(projectId, provider, regionId), + Check: resource.ComposeTestCheckFunc( + + resource.TestCheckResourceAttr("data.biganimal_regions.test", "regions.0.name", regionName), + resource.TestCheckResourceAttr("data.biganimal_regions.test", "regions.0.status", status), + ), + }, + }, + }) +} + +func regionsDataSourceConfig(projectId, provider, regionId string) string { + return fmt.Sprintf(`data "biganimal_regions" "test" { + project_id = "%s" + cloud_provider = "%s" + region_id = "%s" +} +`, projectId, provider, regionId) +} From ebe54d3510afe37dc0f94788489305dd6e16a167 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Wed, 28 Jun 2023 17:23:25 +0800 Subject: [PATCH 30/49] fix: Fixed comments --- .env.example | 6 +++++ docs/data-sources/regions.md | 1 + docs/resources/region.md | 1 + .../biganimal_region/data-source.tf | 4 +-- main.go | 3 ++- pkg/provider/data_source_region.go | 26 +++++++++++++++++++ pkg/provider/data_source_regions.go | 23 ++-------------- pkg/provider/data_source_regions_test.go | 18 ++++++------- pkg/provider/resource_project.go | 1 + pkg/provider/resource_region.go | 6 ++--- pkg/provider/validators.go | 8 +++--- 11 files changed, 56 insertions(+), 41 deletions(-) create mode 100644 pkg/provider/data_source_region.go diff --git a/.env.example b/.env.example index 95ae07ec..f47dee2c 100644 --- a/.env.example +++ b/.env.example @@ -17,3 +17,9 @@ BA_TF_ACC_VAR_region_region_id= # data_source_pgd BA_TF_ACC_VAR_pgd_project_id= BA_TF_ACC_VAR_pgd_name= + +# data_source_regions +BA_TF_ACC_VAR_regions_project_id +BA_TF_ACC_VAR_regions_provider +BA_TF_ACC_VAR_regions_region_id +BA_TF_ACC_VAR_regions_status diff --git a/docs/data-sources/regions.md b/docs/data-sources/regions.md index 4f55e0f2..4e2e85a1 100644 --- a/docs/data-sources/regions.md +++ b/docs/data-sources/regions.md @@ -18,6 +18,7 @@ ### Read-Only +- `id` (String) Datasource ID. - `regions` (Attributes List) Region information. (see [below for nested schema](#nestedatt--regions)) diff --git a/docs/resources/region.md b/docs/resources/region.md index 303c2202..793a7bae 100644 --- a/docs/resources/region.md +++ b/docs/resources/region.md @@ -55,6 +55,7 @@ output "region_continent" { ### Read-Only - `continent` (String) Continent that region belongs to. For example, "Asia", "Australia", or "Europe". +- `id` (String) Resource ID of the region. - `name` (String) Region name of the region. For example, "Germany West Central" or "EU West 1". diff --git a/examples/data-sources/biganimal_region/data-source.tf b/examples/data-sources/biganimal_region/data-source.tf index c8a724f4..158fbdb3 100644 --- a/examples/data-sources/biganimal_region/data-source.tf +++ b/examples/data-sources/biganimal_region/data-source.tf @@ -3,8 +3,8 @@ variable "cloud_provider" { description = "Cloud Provider" validation { - condition = contains(["aws", "azure"], var.cloud_provider) - error_message = "Please select one of the supported regions: aws, azure." + condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) + error_message = "Please select one of the supported regions: aws, azure and bah:aws." } } diff --git a/main.go b/main.go index d486ad59..54df32a7 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,8 @@ package main import ( "context" "flag" + "log" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/provider" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov5" @@ -11,7 +13,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" ) // Run "go generate" to format example terraform files and generate the docs for the registry/website diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go new file mode 100644 index 00000000..7966cf32 --- /dev/null +++ b/pkg/provider/data_source_region.go @@ -0,0 +1,26 @@ +package provider + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" +) + +// NewRegionDataSource is a helper function to simplify the provider implementation. +func NewRegionDataSource() datasource.DataSource { + return ®ionDataSource{} +} + +// regionDataSource is the data source implementation. +type regionDataSource struct { + regionsDataSource +} + +func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_region" +} + +func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + r.regionsDataSource.Schema(ctx, req, resp) + resp.Schema.DeprecationMessage = "The datasource 'region' is deprecated and will be removed in the next major version. Please use 'regions' instead." +} diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index d491bec1..5a3fa018 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -93,7 +93,7 @@ func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaReq } } -type regionDatasource struct { +type regionsDataSourceModel struct { ID *string `tfsdk:"id"` Regions []*models.Region `tfsdk:"regions"` CloudProvider *string `tfsdk:"cloud_provider"` @@ -103,7 +103,7 @@ type regionDatasource struct { } func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var cfg regionDatasource + var cfg regionsDataSourceModel diags := req.Config.Get(ctx, &cfg) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -132,22 +132,3 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest cfg.ID = &resourceID resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } - -// NewRegionDataSource is a helper function to simplify the provider implementation. -func NewRegionDataSource() datasource.DataSource { - return ®ionDataSource{} -} - -// regionDataSource is the data source implementation. -type regionDataSource struct { - regionsDataSource -} - -func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_region" -} - -func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - r.regionsDataSource.Schema(ctx, req, resp) - resp.Schema.DeprecationMessage = "The datasource' 'region' is deprecated and will be removed in the next major version. Please use 'regions' instead." -} diff --git a/pkg/provider/data_source_regions_test.go b/pkg/provider/data_source_regions_test.go index 50c69a8b..ee789968 100644 --- a/pkg/provider/data_source_regions_test.go +++ b/pkg/provider/data_source_regions_test.go @@ -11,16 +11,16 @@ import ( func TestAccDataSourceRegions_basic(t *testing.T) { var ( acc_env_vars_checklist = []string{ - "BA_TF_ACC_VAR_datasource_regions_project_id", - "BA_TF_ACC_VAR_datasource_regions_provider", - "BA_TF_ACC_VAR_datasource_regions_region_id", - "BA_TF_ACC_VAR_datasource_regions_status", + "BA_TF_ACC_VAR_regions_project_id", + "BA_TF_ACC_VAR_regions_provider", + "BA_TF_ACC_VAR_regions_region_id", + "BA_TF_ACC_VAR_regions_status", } - projectId = os.Getenv("BA_TF_ACC_VAR_datasource_regions_project_id") - provider = os.Getenv("BA_TF_ACC_VAR_datasource_regions_provider") - regionId = os.Getenv("BA_TF_ACC_VAR_datasource_regions_region_id") - regionName = os.Getenv("BA_TF_ACC_VAR_datasource_regions_region_name") - status = os.Getenv("BA_TF_ACC_VAR_datasource_regions_status") + projectId = os.Getenv("BA_TF_ACC_VAR_regions_project_id") + provider = os.Getenv("BA_TF_ACC_VAR_regions_provider") + regionId = os.Getenv("BA_TF_ACC_VAR_regions_region_id") + regionName = os.Getenv("BA_TF_ACC_VAR_regions_region_name") + status = os.Getenv("BA_TF_ACC_VAR_regions_status") ) resource.ParallelTest(t, resource.TestCase{ diff --git a/pkg/provider/resource_project.go b/pkg/provider/resource_project.go index 7f869968..db9ef614 100644 --- a/pkg/provider/resource_project.go +++ b/pkg/provider/resource_project.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 8ac2b65c..76e76506 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-framework/path" "strings" "time" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" + "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" @@ -79,7 +79,7 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, Summary: Schema Using Attribute Default For Non-Computed Attribute Detail: Attribute "status" must be computed when using default. This is an issue with the provider and should be reported to the provider developers. */ - //Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), + Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), }, "continent": schema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", @@ -214,7 +214,7 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { - curr, err := r.client.RegionClient().Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) + curr, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { return retry.NonRetryableError(fmt.Errorf("error describing instance: %s", err)) } diff --git a/pkg/provider/validators.go b/pkg/provider/validators.go index ff30827f..f59c1631 100644 --- a/pkg/provider/validators.go +++ b/pkg/provider/validators.go @@ -2,16 +2,14 @@ package provider import ( "fmt" - "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "regexp" "strings" - "github.com/google/uuid" - "github.com/aws/aws-sdk-go/aws/arn" + "github.com/google/uuid" "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) From 729e235f22fd4f8f8876440b161f8fd6e00df00f Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Wed, 28 Jun 2023 18:40:16 +0800 Subject: [PATCH 31/49] fix: Fixed make docs --- pkg/provider/default.go | 29 ----------------------------- pkg/provider/resource_region.go | 11 +++-------- 2 files changed, 3 insertions(+), 37 deletions(-) delete mode 100644 pkg/provider/default.go diff --git a/pkg/provider/default.go b/pkg/provider/default.go deleted file mode 100644 index dbc44e0b..00000000 --- a/pkg/provider/default.go +++ /dev/null @@ -1,29 +0,0 @@ -package provider - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults" - "github.com/hashicorp/terraform-plugin-framework/types" -) - -type defaultString struct { - Desc string - Default string -} - -func DefaultString(desc string, Default string) *defaultString { - return &defaultString{Desc: desc, Default: Default} -} - -func (d defaultString) Description(_ context.Context) string { - return d.Desc -} - -func (d defaultString) MarkdownDescription(_ context.Context) string { - return d.Desc -} - -func (d defaultString) DefaultString(ctx context.Context, request defaults.StringRequest, response *defaults.StringResponse) { - response.PlanValue = types.StringValue(d.Default) -} diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 76e76506..dc432e39 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -14,6 +14,7 @@ import ( "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/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" @@ -72,14 +73,8 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "status": schema.StringAttribute{ MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", Optional: true, - /* - Commented out the following Default field. During compilation, the following error occurs: - error retrieving schema for *proto6server.Server: - Attribute: - Summary: Schema Using Attribute Default For Non-Computed Attribute - Detail: Attribute "status" must be computed when using default. This is an issue with the provider and should be reported to the provider developers. - */ - Default: DefaultString("The default of region desired status", api.REGION_ACTIVE), + Computed: true, + Default: stringdefault.StaticString(api.REGION_ACTIVE), }, "continent": schema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", From 7e7674f906b76d157065ede5f3124ca2599a3705 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Wed, 28 Jun 2023 17:00:08 +0200 Subject: [PATCH 32/49] docs: Add docs for regions data source. --- docs/data-sources/regions.md | 32 +++++++++++++++++++ .../biganimal_regions/data-source.tf | 30 +++++++++++++++++ .../biganimal_regions/provider.tf | 8 +++++ 3 files changed, 70 insertions(+) create mode 100644 examples/data-sources/biganimal_regions/data-source.tf create mode 100644 examples/data-sources/biganimal_regions/provider.tf diff --git a/docs/data-sources/regions.md b/docs/data-sources/regions.md index 4e2e85a1..af45515d 100644 --- a/docs/data-sources/regions.md +++ b/docs/data-sources/regions.md @@ -1,7 +1,39 @@ # biganimal_regions (Data Source) +## Example Usage +```terraform +variable "cloud_provider" { + type = string + description = "Cloud Provider" + validation { + condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) + error_message = "Please select one of the supported regions: aws, azure and bah:aws." + } +} + +variable "project_id" { + type = string + description = "BigAnimal Project ID" + +} + +data "biganimal_regions" "this" { + cloud_provider = var.cloud_provider + project_id = var.project_id + // region_id = "us-west-1" //optional + // query = "eu" // optional +} + +output "regions" { + value = data.biganimal_regions.this.regions +} + +output "cloud_provider_id" { + value = data.biganimal_regions.this.cloud_provider +} +``` ## Schema diff --git a/examples/data-sources/biganimal_regions/data-source.tf b/examples/data-sources/biganimal_regions/data-source.tf new file mode 100644 index 00000000..28ed9ad0 --- /dev/null +++ b/examples/data-sources/biganimal_regions/data-source.tf @@ -0,0 +1,30 @@ +variable "cloud_provider" { + type = string + description = "Cloud Provider" + + validation { + condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) + error_message = "Please select one of the supported regions: aws, azure and bah:aws." + } +} + +variable "project_id" { + type = string + description = "BigAnimal Project ID" + +} + +data "biganimal_regions" "this" { + cloud_provider = var.cloud_provider + project_id = var.project_id + // region_id = "us-west-1" //optional + // query = "eu" // optional +} + +output "regions" { + value = data.biganimal_regions.this.regions +} + +output "cloud_provider_id" { + value = data.biganimal_regions.this.cloud_provider +} diff --git a/examples/data-sources/biganimal_regions/provider.tf b/examples/data-sources/biganimal_regions/provider.tf new file mode 100644 index 00000000..39fa059b --- /dev/null +++ b/examples/data-sources/biganimal_regions/provider.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + biganimal = { + source = "EnterpriseDB/biganimal" + version = "0.4.2" + } + } +} From c543b100a67344aaf2ee351e84f0fef28803ef8a Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Wed, 28 Jun 2023 19:53:52 +0200 Subject: [PATCH 33/49] feat: Add import for biganimal_region resource. --- docs/resources/region.md | 9 +++++++++ examples/resources/biganimal_region/import.sh | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 examples/resources/biganimal_region/import.sh diff --git a/docs/resources/region.md b/docs/resources/region.md index 793a7bae..3839dc66 100644 --- a/docs/resources/region.md +++ b/docs/resources/region.md @@ -66,3 +66,12 @@ Optional: - `create` (String) - `delete` (String) - `update` (String) + +## Import + +Import is supported using the following syntax: + +```shell +# terraform import biganimal_project. // +terraform import biganimal_region.this prj_deadbeef01234567/aws/eu-west-1 +``` diff --git a/examples/resources/biganimal_region/import.sh b/examples/resources/biganimal_region/import.sh new file mode 100644 index 00000000..ae6a01a2 --- /dev/null +++ b/examples/resources/biganimal_region/import.sh @@ -0,0 +1,2 @@ +# terraform import biganimal_project. // +terraform import biganimal_region.this prj_deadbeef01234567/aws/eu-west-1 From e5794319842ca8ea3dfdb82eddc04614d2382ac7 Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:49:36 +0800 Subject: [PATCH 34/49] fix: Added equal sign after variable name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- .env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index f47dee2c..b8db397f 100644 --- a/.env.example +++ b/.env.example @@ -19,7 +19,7 @@ BA_TF_ACC_VAR_pgd_project_id= BA_TF_ACC_VAR_pgd_name= # data_source_regions -BA_TF_ACC_VAR_regions_project_id -BA_TF_ACC_VAR_regions_provider -BA_TF_ACC_VAR_regions_region_id -BA_TF_ACC_VAR_regions_status +BA_TF_ACC_VAR_regions_project_id= +BA_TF_ACC_VAR_regions_provider= +BA_TF_ACC_VAR_regions_region_id= +BA_TF_ACC_VAR_regions_status= From 5e35be34eaeb1d5bc1fdd2ad29570bd1f0e607b9 Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:10:38 +0800 Subject: [PATCH 35/49] fix: Updated message. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- examples/data-sources/biganimal_region/data-source.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data-sources/biganimal_region/data-source.tf b/examples/data-sources/biganimal_region/data-source.tf index 158fbdb3..61236156 100644 --- a/examples/data-sources/biganimal_region/data-source.tf +++ b/examples/data-sources/biganimal_region/data-source.tf @@ -4,7 +4,7 @@ variable "cloud_provider" { validation { condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) - error_message = "Please select one of the supported regions: aws, azure and bah:aws." + error_message = "Please select one of the supported regions: aws, azure or bah:aws." } } From 6afac0b8ecdebb7ec2b4d62a23257f96fecb4260 Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:10:47 +0800 Subject: [PATCH 36/49] fix: Updated message. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- pkg/provider/data_source_regions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index 5a3fa018..6049e0e9 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -71,7 +71,7 @@ func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaReq }, "cloud_provider": schema.StringAttribute{ - Description: "Cloud provider to list the regions. For example, \"aws\" or \"azure\".", + Description: "Cloud provider to list the regions. For example, \"aws\", \"azure\" or \"bah:aws\".", Required: true, }, "project_id": schema.StringAttribute{ From 7201e6f1a7d5b3abe3269e9160da7640d8de393c Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:11:04 +0800 Subject: [PATCH 37/49] fix: Updated message. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- pkg/provider/resource_region.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index dc432e39..8b6c5068 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -46,7 +46,7 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, }, }, "cloud_provider": schema.StringAttribute{ - MarkdownDescription: "Cloud provider. For example, \"aws\" or \"azure\".", + MarkdownDescription: "Cloud provider. For example, \"aws\", \"azure\" or \"bah:aws\".", Required: true, }, "project_id": schema.StringAttribute{ From e98badfd79db25912a49c06ad9cd9c2248efc27b Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:19:03 +0800 Subject: [PATCH 38/49] fix: Use `frameworkdiag` for more specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- pkg/provider/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index ff41799c..82fd1b44 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - diag2 "github.com/hashicorp/terraform-plugin-framework/diag" + frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) From 21dc00f7a20eb18af0c37bc753ccb92f2c01cb06 Mon Sep 17 00:00:00 2001 From: huyantian <31943844+YanniHu1996@users.noreply.github.com> Date: Thu, 29 Jun 2023 15:19:17 +0800 Subject: [PATCH 39/49] fix: Use `sdkdiag ` for more specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Serdar Dalgıç --- pkg/provider/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index 82fd1b44..3d82fd81 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -8,7 +8,7 @@ import ( "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + sdkdiag "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) func fromBigAnimalErr(err error) diag.Diagnostics { From 9b76e3f632d063a737acc3c0c3d66e31f2d0a9fd Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 29 Jun 2023 16:08:12 +0800 Subject: [PATCH 40/49] fix: Run make docs --- docs/data-sources/regions.md | 2 +- docs/resources/region.md | 2 +- pkg/provider/utils.go | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/data-sources/regions.md b/docs/data-sources/regions.md index af45515d..141ae738 100644 --- a/docs/data-sources/regions.md +++ b/docs/data-sources/regions.md @@ -40,7 +40,7 @@ output "cloud_provider_id" { ### Required -- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws" or "azure". +- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws", "azure" or "bah:aws". - `project_id` (String) BigAnimal Project ID. ### Optional diff --git a/docs/resources/region.md b/docs/resources/region.md index 3839dc66..af081226 100644 --- a/docs/resources/region.md +++ b/docs/resources/region.md @@ -43,7 +43,7 @@ output "region_continent" { ### Required -- `cloud_provider` (String) Cloud provider. For example, "aws" or "azure". +- `cloud_provider` (String) Cloud provider. For example, "aws", "azure" or "bah:aws". - `project_id` (String) BigAnimal Project ID. - `region_id` (String) Region ID of the region. For example, "germanywestcentral" in the Azure cloud provider or "eu-west-1" in the AWS cloud provider. diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index 3d82fd81..bb08ea3e 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -11,38 +11,38 @@ import ( sdkdiag "github.com/hashicorp/terraform-plugin-sdk/v2/diag" ) -func fromBigAnimalErr(err error) diag.Diagnostics { +func fromBigAnimalErr(err error) sdkdiag.Diagnostics { if err == nil { return nil } var baError *api.BigAnimalError if errors.As(err, &baError) { - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, + return sdkdiag.Diagnostics{ + sdkdiag.Diagnostic{ + Severity: sdkdiag.Error, Summary: baError.Error(), Detail: baError.GetDetails(), }, } } - return diag.FromErr(err) + return sdkdiag.FromErr(err) } -func unsupportedWarning(message string) diag.Diagnostics { - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Warning, +func unsupportedWarning(message string) sdkdiag.Diagnostics { + return sdkdiag.Diagnostics{ + sdkdiag.Diagnostic{ + Severity: sdkdiag.Warning, Summary: "Unsupported", Detail: message, }, } } -func fromErr(err error, summary string, args ...any) diag2.Diagnostics { +func fromErr(err error, summary string, args ...any) frameworkdiag.Diagnostics { summary = fmt.Sprintf(summary, args...) - return diag2.Diagnostics{ - diag2.NewErrorDiagnostic( + return frameworkdiag.Diagnostics{ + frameworkdiag.NewErrorDiagnostic( summary, err.Error(), ), } From b1bf8e08837d72695e9b1575ee4e51659b2195b8 Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 29 Jun 2023 17:15:01 +0800 Subject: [PATCH 41/49] fix: Fixed unexpected of terraform plan and inconsistent result after apply --- pkg/provider/data_source_regions.go | 6 ++--- pkg/provider/resource_region.go | 36 +++++++++-------------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index 6049e0e9..a7e5c547 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -95,11 +95,11 @@ func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaReq type regionsDataSourceModel struct { ID *string `tfsdk:"id"` - Regions []*models.Region `tfsdk:"regions"` - CloudProvider *string `tfsdk:"cloud_provider"` ProjectId *string `tfsdk:"project_id"` - Query types.String `tfsdk:"query"` + CloudProvider *string `tfsdk:"cloud_provider"` RegionId *string `tfsdk:"region_id"` + Query types.String `tfsdk:"query"` + Regions []*models.Region `tfsdk:"regions"` } func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 8b6c5068..68373406 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/hashicorp/terraform-plugin-framework/types" "strings" "time" @@ -13,9 +14,7 @@ import ( "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/stringdefault" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" @@ -41,9 +40,6 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "id": schema.StringAttribute{ MarkdownDescription: "Resource ID of the region.", Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, "cloud_provider": schema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\", \"azure\" or \"bah:aws\".", @@ -55,9 +51,6 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, Validators: []validator.String{ ProjectIdValidator(), }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, "region_id": schema.StringAttribute{ MarkdownDescription: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", @@ -66,9 +59,6 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "name": schema.StringAttribute{ MarkdownDescription: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, "status": schema.StringAttribute{ MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", @@ -79,9 +69,6 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "continent": schema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, }, } @@ -96,13 +83,13 @@ func (r *regionResource) Configure(_ context.Context, req resource.ConfigureRequ } type Region struct { - ID *string `tfsdk:"id"` - ProjectID *string `tfsdk:"project_id"` - CloudProvider *string `tfsdk:"cloud_provider"` - RegionID *string `tfsdk:"region_id"` - Name *string `tfsdk:"name"` - Status *string `tfsdk:"status"` - Continent *string `tfsdk:"continent"` + ProjectID *string `tfsdk:"project_id"` + CloudProvider *string `tfsdk:"cloud_provider"` + RegionID *string `tfsdk:"region_id"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Continent types.String `tfsdk:"continent"` + Status *string `tfsdk:"status"` Timeouts timeouts.Value `tfsdk:"timeouts"` } @@ -135,11 +122,10 @@ func (r *regionResource) read(ctx context.Context, region Region, state *tfsdk.S if err != nil { return fromErr(err, "Error reading region %v", region.RegionID) } - id := fmt.Sprintf("%s/%s/%s", *region.ProjectID, *region.CloudProvider, *region.RegionID) - region.ID = &id - region.Name = &read.Name + region.ID = types.StringValue(fmt.Sprintf("%s/%s/%s", *region.ProjectID, *region.CloudProvider, *region.RegionID)) + region.Name = types.StringValue(read.Name) region.Status = &read.Status - region.Continent = &read.Continent + region.Continent = types.StringValue(read.Continent) return state.Set(ctx, ®ion) } From 6b343506f1b764f5225f56c6b69f5006bdc2eadb Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Thu, 29 Jun 2023 20:28:43 +0800 Subject: [PATCH 42/49] fix: Fixed test failure --- pkg/provider/resource_region_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/resource_region_test.go b/pkg/provider/resource_region_test.go index 30303658..aeb5bca6 100644 --- a/pkg/provider/resource_region_test.go +++ b/pkg/provider/resource_region_test.go @@ -34,7 +34,7 @@ func TestAccResourceRegion_basic(t *testing.T) { testAccPreCheck(t) testAccResourcePreCheck(t, "region", acc_env_vars_checklist) }, - ProviderFactories: testAccProviderFactories, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { Config: fmt.Sprintf(regionConfig, api.REGION_ACTIVE, projectID, provider, regionID), From 450cfc6a1fd1b334655a0facd0d41f62a41801fd Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Thu, 29 Jun 2023 16:50:53 +0200 Subject: [PATCH 43/49] feat: Add helper function for BA API Errors --- pkg/provider/data_source_regions.go | 5 ++++- pkg/provider/utils.go | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index a7e5c547..b0f9f3d2 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -114,7 +114,8 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest if cfg.RegionId != nil { region, err := r.client.Read(ctx, *cfg.ProjectId, *cfg.CloudProvider, *cfg.RegionId) if err != nil { - resp.Diagnostics.Append(fromErr(err, "Error reading region by id: %v", cfg.RegionId)...) + summary, detail := extractSumAndDetailfromBAErr(err) + resp.Diagnostics.AddError(summary, detail) return } regions = append(regions, region) @@ -122,6 +123,8 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest } else { respRegions, err := r.client.List(ctx, *cfg.ProjectId, *cfg.CloudProvider, cfg.Query.ValueString()) if err != nil { + summary, detail := extractSumAndDetailfromBAErr(err) + resp.Diagnostics.AddError(summary, detail) return } regions = respRegions diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index bb08ea3e..4d97387b 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -39,6 +39,24 @@ func unsupportedWarning(message string) sdkdiag.Diagnostics { } } +/* +Please use this function for error check after client API calls, for example: + + r.client.Read(ctx, ...) + if err != nil { + summary, detail := extractSumAndDetailfromBAErr(err) + resp.Diagnostics.AddError(summary, detail) + return + } +*/ +func extractSumAndDetailfromBAErr(err error) (summary, detail string) { + var baError *api.BigAnimalError + if errors.As(err, &baError) { + return baError.Error(), baError.GetDetails() + } + return +} + func fromErr(err error, summary string, args ...any) frameworkdiag.Diagnostics { summary = fmt.Sprintf(summary, args...) return frameworkdiag.Diagnostics{ From bd8b5d1c7f1aa36a8540c20b1de9e92977a5223f Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Fri, 30 Jun 2023 17:45:15 +0800 Subject: [PATCH 44/49] fix: Handle BigAnimal Error --- docs/data-sources/region.md | 74 +++++++++++++ pkg/provider/data_source_regions.go | 13 ++- pkg/provider/data_source_regions_test.go | 31 +++--- pkg/provider/resource_region.go | 129 +++++++++++++---------- pkg/provider/utils.go | 26 ++--- 5 files changed, 184 insertions(+), 89 deletions(-) create mode 100644 docs/data-sources/region.md diff --git a/docs/data-sources/region.md b/docs/data-sources/region.md new file mode 100644 index 00000000..946438f0 --- /dev/null +++ b/docs/data-sources/region.md @@ -0,0 +1,74 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "biganimal_region Data Source - terraform-provider-biganimal" +subcategory: "" +description: |- + +--- + +# biganimal_region (Data Source) + + + +## Example Usage + +```terraform +variable "cloud_provider" { + type = string + description = "Cloud Provider" + + validation { + condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) + error_message = "Please select one of the supported regions: aws, azure or bah:aws." + } +} + +variable "project_id" { + type = string + description = "BigAnimal Project ID" + +} + +data "biganimal_region" "this" { + cloud_provider = var.cloud_provider + project_id = var.project_id + // region_id = "us-west-1" //optional + // query = "eu" // optional +} + +output "regions" { + value = data.biganimal_region.this.regions +} + +output "cloud_provider_id" { + value = data.biganimal_region.this.cloud_provider +} +``` + + +## Schema + +### Required + +- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws", "azure" or "bah:aws". +- `project_id` (String) BigAnimal Project ID. + +### Optional + +- `query` (String) Query to filter region list. +- `region_id` (String) Unique region ID. For example, "germanywestcentral" in the Azure cloud provider, "eu-west-1" in the AWS cloud provider. + +### Read-Only + +- `id` (String) Datasource ID. +- `regions` (Attributes List) Region information. (see [below for nested schema](#nestedatt--regions)) + + +### Nested Schema for `regions` + +Read-Only: + +- `continent` (String) Continent that region belongs to. +- `name` (String) Region name of the region. +- `region_id` (String) Region ID of the region. +- `status` (String) Region status of the region. diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go index b0f9f3d2..503d0bdf 100644 --- a/pkg/provider/data_source_regions.go +++ b/pkg/provider/data_source_regions.go @@ -2,6 +2,7 @@ package provider import ( "context" + "fmt" "strconv" "time" @@ -114,8 +115,10 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest if cfg.RegionId != nil { region, err := r.client.Read(ctx, *cfg.ProjectId, *cfg.CloudProvider, *cfg.RegionId) if err != nil { - summary, detail := extractSumAndDetailfromBAErr(err) - resp.Diagnostics.AddError(summary, detail) + if appendDiagFromBAErr(err, &diags) { + return + } + diags.AddError(fmt.Sprintf("Error reading region by id: %q", *cfg.RegionId), err.Error()) return } regions = append(regions, region) @@ -123,8 +126,10 @@ func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest } else { respRegions, err := r.client.List(ctx, *cfg.ProjectId, *cfg.CloudProvider, cfg.Query.ValueString()) if err != nil { - summary, detail := extractSumAndDetailfromBAErr(err) - resp.Diagnostics.AddError(summary, detail) + if appendDiagFromBAErr(err, &diags) { + return + } + diags.AddError(fmt.Sprintf("Error reading region by query: %q", cfg.Query.ValueString()), err.Error()) return } regions = respRegions diff --git a/pkg/provider/data_source_regions_test.go b/pkg/provider/data_source_regions_test.go index ee789968..74bff5d6 100644 --- a/pkg/provider/data_source_regions_test.go +++ b/pkg/provider/data_source_regions_test.go @@ -14,13 +14,10 @@ func TestAccDataSourceRegions_basic(t *testing.T) { "BA_TF_ACC_VAR_regions_project_id", "BA_TF_ACC_VAR_regions_provider", "BA_TF_ACC_VAR_regions_region_id", - "BA_TF_ACC_VAR_regions_status", } - projectId = os.Getenv("BA_TF_ACC_VAR_regions_project_id") - provider = os.Getenv("BA_TF_ACC_VAR_regions_provider") - regionId = os.Getenv("BA_TF_ACC_VAR_regions_region_id") - regionName = os.Getenv("BA_TF_ACC_VAR_regions_region_name") - status = os.Getenv("BA_TF_ACC_VAR_regions_status") + projectId = os.Getenv("BA_TF_ACC_VAR_regions_project_id") + provider = os.Getenv("BA_TF_ACC_VAR_regions_provider") + regionId = os.Getenv("BA_TF_ACC_VAR_regions_region_id") ) resource.ParallelTest(t, resource.TestCase{ @@ -33,9 +30,8 @@ func TestAccDataSourceRegions_basic(t *testing.T) { { Config: regionsDataSourceConfig(projectId, provider, regionId), Check: resource.ComposeTestCheckFunc( - - resource.TestCheckResourceAttr("data.biganimal_regions.test", "regions.0.name", regionName), - resource.TestCheckResourceAttr("data.biganimal_regions.test", "regions.0.status", status), + resource.TestCheckResourceAttrPair("data.biganimal_regions.test", "regions.0.name", "biganimal_regions.test", "name"), + resource.TestCheckResourceAttrPair("data.biganimal_regions.test", "regions.0.status", "biganimal_regions.test", "status"), ), }, }, @@ -44,9 +40,20 @@ func TestAccDataSourceRegions_basic(t *testing.T) { func regionsDataSourceConfig(projectId, provider, regionId string) string { return fmt.Sprintf(`data "biganimal_regions" "test" { - project_id = "%s" - cloud_provider = "%s" - region_id = "%s" + project_id = "%[1]s" + cloud_provider = "%[2]s" + region_id = "%[3]s" +} + +resource "biganimal_region" "test" { + project_id = "%[1]s" + cloud_provider = "%[2]s" + region_id = "%[3]s" + # no need to active region actually for saving time' + status = "INACTIVE" } + + + `, projectId, provider, regionId) } diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 68373406..90ae318a 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -4,7 +4,9 @@ import ( "context" "errors" "fmt" - "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" "strings" "time" @@ -16,7 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/schema/validator" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" ) @@ -40,6 +41,9 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "id": schema.StringAttribute{ MarkdownDescription: "Resource ID of the region.", Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "cloud_provider": schema.StringAttribute{ MarkdownDescription: "Cloud provider. For example, \"aws\", \"azure\" or \"bah:aws\".", @@ -55,10 +59,16 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "region_id": schema.StringAttribute{ MarkdownDescription: "Region ID of the region. For example, \"germanywestcentral\" in the Azure cloud provider or \"eu-west-1\" in the AWS cloud provider.", Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "name": schema.StringAttribute{ MarkdownDescription: "Region name of the region. For example, \"Germany West Central\" or \"EU West 1\".", Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "status": schema.StringAttribute{ MarkdownDescription: "Region status of the region. For example, \"ACTIVE\", \"INACTIVE\", or \"SUSPENDED\".", @@ -69,6 +79,9 @@ func (r regionResource) Schema(ctx context.Context, req resource.SchemaRequest, "continent": schema.StringAttribute{ MarkdownDescription: "Continent that region belongs to. For example, \"Asia\", \"Australia\", or \"Europe\".", Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, }, } @@ -83,13 +96,13 @@ func (r *regionResource) Configure(_ context.Context, req resource.ConfigureRequ } type Region struct { - ProjectID *string `tfsdk:"project_id"` - CloudProvider *string `tfsdk:"cloud_provider"` - RegionID *string `tfsdk:"region_id"` - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - Continent types.String `tfsdk:"continent"` - Status *string `tfsdk:"status"` + ProjectID *string `tfsdk:"project_id"` + CloudProvider *string `tfsdk:"cloud_provider"` + RegionID *string `tfsdk:"region_id"` + ID *string `tfsdk:"id"` + Name *string `tfsdk:"name"` + Continent *string `tfsdk:"continent"` + Status *string `tfsdk:"status"` Timeouts timeouts.Value `tfsdk:"timeouts"` } @@ -102,8 +115,12 @@ func (r regionResource) Create(ctx context.Context, req resource.CreateRequest, return } - diags = r.update(ctx, config, &resp.State) - resp.Diagnostics.Append(diags...) + resp.Diagnostics.Append(r.ensureStatueUpdated(ctx, config)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(r.writeState(ctx, config, &resp.State)...) } func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { @@ -114,19 +131,7 @@ func (r *regionResource) Read(ctx context.Context, req resource.ReadRequest, res return } - resp.Diagnostics.Append(r.read(ctx, state, &resp.State)...) -} - -func (r *regionResource) read(ctx context.Context, region Region, state *tfsdk.State) frameworkdiag.Diagnostics { - read, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) - if err != nil { - return fromErr(err, "Error reading region %v", region.RegionID) - } - region.ID = types.StringValue(fmt.Sprintf("%s/%s/%s", *region.ProjectID, *region.CloudProvider, *region.RegionID)) - region.Name = types.StringValue(read.Name) - region.Status = &read.Status - region.Continent = types.StringValue(read.Continent) - return state.Set(ctx, ®ion) + resp.Diagnostics.Append(r.writeState(ctx, state, &resp.State)...) } func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { @@ -137,28 +142,12 @@ func (r *regionResource) Update(ctx context.Context, req resource.UpdateRequest, return } - resp.Diagnostics.Append(r.update(ctx, plan, &resp.State)...) -} - -func (r *regionResource) update(ctx context.Context, region Region, state *tfsdk.State) frameworkdiag.Diagnostics { - if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { - return fromErr(err, "Error updating region %v", region.RegionID) - } - - timeout, diagnostics := region.Timeouts.Create(ctx, 60*time.Minute) - if diagnostics != nil { - return diagnostics - } - - err := retry.RetryContext( - ctx, - timeout-time.Minute, - r.retryFunc(ctx, region)) - if err != nil { - return fromErr(err, "") + resp.Diagnostics.Append(r.ensureStatueUpdated(ctx, plan)...) + if resp.Diagnostics.HasError() { + return } - return r.read(ctx, region, state) + resp.Diagnostics.Append(r.writeState(ctx, plan, &resp.State)...) } func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { @@ -169,35 +158,61 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, return } - if *state.Status == api.REGION_INACTIVE { - return - } + *state.Status = api.REGION_INACTIVE + resp.Diagnostics.Append(r.ensureStatueUpdated(ctx, state)...) +} - if err := r.client.Update(ctx, api.REGION_INACTIVE, *state.ProjectID, *state.CloudProvider, *state.RegionID); err != nil { - resp.Diagnostics.Append(fromErr(err, "Error deleting region %v", state.RegionID)...) - return +func (r *regionResource) ensureStatueUpdated(ctx context.Context, region Region) frameworkdiag.Diagnostics { + diags := frameworkdiag.Diagnostics{} + if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { + if appendDiagFromBAErr(err, &diags) { + return diags + } + diags.AddError(fmt.Sprintf("Error turning region %q into %q status", *region.RegionID, *region.Status), err.Error()) + return diags } - timeout, diagnostics := state.Timeouts.Create(ctx, 60*time.Minute) - resp.Diagnostics.Append(diagnostics...) - if resp.Diagnostics.HasError() { - return + timeout, diagnostics := region.Timeouts.Create(ctx, 60*time.Minute) + if diagnostics != nil { + return diagnostics } err := retry.RetryContext( ctx, timeout-time.Minute, - r.retryFunc(ctx, state)) + r.retryFunc(ctx, region)) + if err != nil { + if appendDiagFromBAErr(err, &diags) { + return diags + } + diags.AddError(fmt.Sprintf("Error reading region %s", *region.RegionID), err.Error()) + } + return diags +} + +func (r *regionResource) writeState(ctx context.Context, region Region, state *tfsdk.State) frameworkdiag.Diagnostics { + read, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { - resp.Diagnostics.Append(fromErr(err, "")...) + diags := frameworkdiag.Diagnostics{} + if appendDiagFromBAErr(err, &diags) { + return diags + } + diags.AddError(fmt.Sprintf("Error reading region %s", *region.RegionID), err.Error()) + return diags } + id := fmt.Sprintf("%s/%s/%s", *region.ProjectID, *region.CloudProvider, *region.RegionID) + region.ID = &id + region.Name = &read.Name + region.Status = &read.Status + region.Continent = &read.Continent + return state.Set(ctx, ®ion) } func (r *regionResource) retryFunc(ctx context.Context, region Region) retry.RetryFunc { return func() *retry.RetryError { curr, err := r.client.Read(ctx, *region.ProjectID, *region.CloudProvider, *region.RegionID) if err != nil { - return retry.NonRetryableError(fmt.Errorf("error describing instance: %s", err)) + return retry.NonRetryableError(err) } if curr.Status != *region.Status { diff --git a/pkg/provider/utils.go b/pkg/provider/utils.go index 4d97387b..94388afc 100644 --- a/pkg/provider/utils.go +++ b/pkg/provider/utils.go @@ -3,8 +3,6 @@ package provider import ( "errors" - "fmt" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" frameworkdiag "github.com/hashicorp/terraform-plugin-framework/diag" @@ -40,28 +38,24 @@ func unsupportedWarning(message string) sdkdiag.Diagnostics { } /* -Please use this function for error check after client API calls, for example: +Please use this function for error check after client API calls. +This function returns a boolean representing if passed error is as *api.BigAnimalError, for example: r.client.Read(ctx, ...) if err != nil { - summary, detail := extractSumAndDetailfromBAErr(err) + if appendDiagFromBAErr(err){ + return + } + resp.Diagnostics.AddError(summary, detail) return } */ -func extractSumAndDetailfromBAErr(err error) (summary, detail string) { +func appendDiagFromBAErr(err error, diags *frameworkdiag.Diagnostics) bool { var baError *api.BigAnimalError if errors.As(err, &baError) { - return baError.Error(), baError.GetDetails() - } - return -} - -func fromErr(err error, summary string, args ...any) frameworkdiag.Diagnostics { - summary = fmt.Sprintf(summary, args...) - return frameworkdiag.Diagnostics{ - frameworkdiag.NewErrorDiagnostic( - summary, err.Error(), - ), + diags.AddError(baError.Error(), baError.GetDetails()) + return true } + return false } From c6c9b0f40e6bcc21e9c6ff30e2b98b6f7914c71b Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Fri, 7 Jul 2023 10:46:44 +0800 Subject: [PATCH 45/49] fix: Removed data_source_regions --- docs/data-sources/region.md | 10 -- docs/data-sources/regions.md | 64 -------- pkg/provider/data_source_region.go | 136 +++++++++++++++-- ...ons_test.go => data_source_region_test.go} | 0 pkg/provider/data_source_regions.go | 142 ------------------ pkg/provider/provider.go | 1 - .../{regions.md.tmpl => region.md.tmpl} | 0 7 files changed, 126 insertions(+), 227 deletions(-) delete mode 100644 docs/data-sources/regions.md rename pkg/provider/{data_source_regions_test.go => data_source_region_test.go} (100%) delete mode 100644 pkg/provider/data_source_regions.go rename templates/data-sources/{regions.md.tmpl => region.md.tmpl} (100%) diff --git a/docs/data-sources/region.md b/docs/data-sources/region.md index 946438f0..d02de644 100644 --- a/docs/data-sources/region.md +++ b/docs/data-sources/region.md @@ -1,17 +1,7 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "biganimal_region Data Source - terraform-provider-biganimal" -subcategory: "" -description: |- - ---- - # biganimal_region (Data Source) - ## Example Usage - ```terraform variable "cloud_provider" { type = string diff --git a/docs/data-sources/regions.md b/docs/data-sources/regions.md deleted file mode 100644 index 141ae738..00000000 --- a/docs/data-sources/regions.md +++ /dev/null @@ -1,64 +0,0 @@ -# biganimal_regions (Data Source) - - -## Example Usage -```terraform -variable "cloud_provider" { - type = string - description = "Cloud Provider" - - validation { - condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) - error_message = "Please select one of the supported regions: aws, azure and bah:aws." - } -} - -variable "project_id" { - type = string - description = "BigAnimal Project ID" - -} - -data "biganimal_regions" "this" { - cloud_provider = var.cloud_provider - project_id = var.project_id - // region_id = "us-west-1" //optional - // query = "eu" // optional -} - -output "regions" { - value = data.biganimal_regions.this.regions -} - -output "cloud_provider_id" { - value = data.biganimal_regions.this.cloud_provider -} -``` - - -## Schema - -### Required - -- `cloud_provider` (String) Cloud provider to list the regions. For example, "aws", "azure" or "bah:aws". -- `project_id` (String) BigAnimal Project ID. - -### Optional - -- `query` (String) Query to filter region list. -- `region_id` (String) Unique region ID. For example, "germanywestcentral" in the Azure cloud provider, "eu-west-1" in the AWS cloud provider. - -### Read-Only - -- `id` (String) Datasource ID. -- `regions` (Attributes List) Region information. (see [below for nested schema](#nestedatt--regions)) - - -### Nested Schema for `regions` - -Read-Only: - -- `continent` (String) Continent that region belongs to. -- `name` (String) Region name of the region. -- `region_id` (String) Region ID of the region. -- `status` (String) Region status of the region. diff --git a/pkg/provider/data_source_region.go b/pkg/provider/data_source_region.go index 7966cf32..8a58a55e 100644 --- a/pkg/provider/data_source_region.go +++ b/pkg/provider/data_source_region.go @@ -2,25 +2,141 @@ package provider import ( "context" + "fmt" + "strconv" + "time" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" + "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" "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" ) -// NewRegionDataSource is a helper function to simplify the provider implementation. -func NewRegionDataSource() datasource.DataSource { - return ®ionDataSource{} +var _ datasource.DataSourceWithConfigure = ®ionsDataSource{} + +// NewRegionsDataSource is a helper function to simplify the provider implementation. +func NewRegionsDataSource() datasource.DataSource { + return ®ionsDataSource{} +} + +// regionsDataSource is the data source implementation. +type regionsDataSource struct { + client *api.RegionClient } -// regionDataSource is the data source implementation. -type regionDataSource struct { - regionsDataSource +// Configure adds the provider configured client to the data source. +func (r *regionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*api.API).RegionClient() } -func (r *regionDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { +func (r *regionsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_region" } -func (r *regionDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - r.regionsDataSource.Schema(ctx, req, resp) - resp.Schema.DeprecationMessage = "The datasource 'region' is deprecated and will be removed in the next major version. Please use 'regions' instead." +func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Description: "Datasource ID.", + Computed: true, + }, + "regions": schema.ListNestedAttribute{ + Description: "Region information.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "region_id": schema.StringAttribute{ + Description: "Region ID of the region.", + Computed: true, + }, + "name": schema.StringAttribute{ + Description: "Region name of the region.", + Computed: true, + }, + "status": schema.StringAttribute{ + Description: "Region status of the region.", + Computed: true, + }, + "continent": schema.StringAttribute{ + Description: "Continent that region belongs to.", + Computed: true, + }, + }, + }, + }, + + "cloud_provider": schema.StringAttribute{ + Description: "Cloud provider to list the regions. For example, \"aws\", \"azure\" or \"bah:aws\".", + Required: true, + }, + "project_id": schema.StringAttribute{ + Description: "BigAnimal Project ID.", + Required: true, + Validators: []validator.String{ + ProjectIdValidator(), + }, + }, + "query": schema.StringAttribute{ + Description: "Query to filter region list.", + Optional: true, + }, + "region_id": schema.StringAttribute{ + Description: "Unique region ID. For example, \"germanywestcentral\" in the Azure cloud provider, \"eu-west-1\" in the AWS cloud provider.", + Optional: true, + }, + }, + } +} + +type regionsDataSourceModel struct { + ID *string `tfsdk:"id"` + ProjectId *string `tfsdk:"project_id"` + CloudProvider *string `tfsdk:"cloud_provider"` + RegionId *string `tfsdk:"region_id"` + Query types.String `tfsdk:"query"` + Regions []*models.Region `tfsdk:"regions"` +} + +func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var cfg regionsDataSourceModel + diags := req.Config.Get(ctx, &cfg) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + regions := []*models.Region{} + if cfg.RegionId != nil { + region, err := r.client.Read(ctx, *cfg.ProjectId, *cfg.CloudProvider, *cfg.RegionId) + if err != nil { + if appendDiagFromBAErr(err, &diags) { + return + } + diags.AddError(fmt.Sprintf("Error reading region by id: %q", *cfg.RegionId), err.Error()) + return + } + regions = append(regions, region) + + } else { + respRegions, err := r.client.List(ctx, *cfg.ProjectId, *cfg.CloudProvider, cfg.Query.ValueString()) + if err != nil { + if appendDiagFromBAErr(err, &diags) { + return + } + diags.AddError(fmt.Sprintf("Error reading region by query: %q", cfg.Query.ValueString()), err.Error()) + return + } + regions = respRegions + } + + cfg.Regions = append(cfg.Regions, regions...) + resourceID := strconv.FormatInt(time.Now().Unix(), 10) + cfg.ID = &resourceID + resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) } diff --git a/pkg/provider/data_source_regions_test.go b/pkg/provider/data_source_region_test.go similarity index 100% rename from pkg/provider/data_source_regions_test.go rename to pkg/provider/data_source_region_test.go diff --git a/pkg/provider/data_source_regions.go b/pkg/provider/data_source_regions.go deleted file mode 100644 index 503d0bdf..00000000 --- a/pkg/provider/data_source_regions.go +++ /dev/null @@ -1,142 +0,0 @@ -package provider - -import ( - "context" - "fmt" - "strconv" - "time" - - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/api" - "github.com/EnterpriseDB/terraform-provider-biganimal/pkg/models" - "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" -) - -var _ datasource.DataSourceWithConfigure = ®ionsDataSource{} - -// NewRegionsDataSource is a helper function to simplify the provider implementation. -func NewRegionsDataSource() datasource.DataSource { - return ®ionsDataSource{} -} - -// regionsDataSource is the data source implementation. -type regionsDataSource struct { - client *api.RegionClient -} - -// Configure adds the provider configured client to the data source. -func (r *regionsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - if req.ProviderData == nil { - return - } - - r.client = req.ProviderData.(*api.API).RegionClient() -} - -func (r *regionsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { - resp.TypeName = req.ProviderTypeName + "_regions" -} - -func (r *regionsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Description: "Datasource ID.", - Computed: true, - }, - "regions": schema.ListNestedAttribute{ - Description: "Region information.", - Computed: true, - NestedObject: schema.NestedAttributeObject{ - Attributes: map[string]schema.Attribute{ - "region_id": schema.StringAttribute{ - Description: "Region ID of the region.", - Computed: true, - }, - "name": schema.StringAttribute{ - Description: "Region name of the region.", - Computed: true, - }, - "status": schema.StringAttribute{ - Description: "Region status of the region.", - Computed: true, - }, - "continent": schema.StringAttribute{ - Description: "Continent that region belongs to.", - Computed: true, - }, - }, - }, - }, - - "cloud_provider": schema.StringAttribute{ - Description: "Cloud provider to list the regions. For example, \"aws\", \"azure\" or \"bah:aws\".", - Required: true, - }, - "project_id": schema.StringAttribute{ - Description: "BigAnimal Project ID.", - Required: true, - Validators: []validator.String{ - ProjectIdValidator(), - }, - }, - "query": schema.StringAttribute{ - Description: "Query to filter region list.", - Optional: true, - }, - "region_id": schema.StringAttribute{ - Description: "Unique region ID. For example, \"germanywestcentral\" in the Azure cloud provider, \"eu-west-1\" in the AWS cloud provider.", - Optional: true, - }, - }, - } -} - -type regionsDataSourceModel struct { - ID *string `tfsdk:"id"` - ProjectId *string `tfsdk:"project_id"` - CloudProvider *string `tfsdk:"cloud_provider"` - RegionId *string `tfsdk:"region_id"` - Query types.String `tfsdk:"query"` - Regions []*models.Region `tfsdk:"regions"` -} - -func (r *regionsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var cfg regionsDataSourceModel - diags := req.Config.Get(ctx, &cfg) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - - regions := []*models.Region{} - if cfg.RegionId != nil { - region, err := r.client.Read(ctx, *cfg.ProjectId, *cfg.CloudProvider, *cfg.RegionId) - if err != nil { - if appendDiagFromBAErr(err, &diags) { - return - } - diags.AddError(fmt.Sprintf("Error reading region by id: %q", *cfg.RegionId), err.Error()) - return - } - regions = append(regions, region) - - } else { - respRegions, err := r.client.List(ctx, *cfg.ProjectId, *cfg.CloudProvider, cfg.Query.ValueString()) - if err != nil { - if appendDiagFromBAErr(err, &diags) { - return - } - diags.AddError(fmt.Sprintf("Error reading region by query: %q", cfg.Query.ValueString()), err.Error()) - return - } - regions = respRegions - } - - cfg.Regions = append(cfg.Regions, regions...) - resourceID := strconv.FormatInt(time.Now().Unix(), 10) - cfg.ID = &resourceID - resp.Diagnostics.Append(resp.State.Set(ctx, &cfg)...) -} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 2cfd5554..176c7766 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -178,7 +178,6 @@ func (b bigAnimalProvider) DataSources(ctx context.Context) []func() datasource. NewProjectsDataSource, NewPgdDataSource, NewRegionsDataSource, - NewRegionDataSource, } } diff --git a/templates/data-sources/regions.md.tmpl b/templates/data-sources/region.md.tmpl similarity index 100% rename from templates/data-sources/regions.md.tmpl rename to templates/data-sources/region.md.tmpl From 2f9857d3ca983669257ccb1f5c99dfc52397cb91 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Mon, 10 Jul 2023 17:02:20 +0200 Subject: [PATCH 46/49] fix: Correct regions -> region in the ACC test --- pkg/provider/data_source_region_test.go | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/provider/data_source_region_test.go b/pkg/provider/data_source_region_test.go index 74bff5d6..a7b36fce 100644 --- a/pkg/provider/data_source_region_test.go +++ b/pkg/provider/data_source_region_test.go @@ -8,38 +8,38 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestAccDataSourceRegions_basic(t *testing.T) { +func TestAccDataSourceRegion_basic(t *testing.T) { var ( acc_env_vars_checklist = []string{ - "BA_TF_ACC_VAR_regions_project_id", - "BA_TF_ACC_VAR_regions_provider", - "BA_TF_ACC_VAR_regions_region_id", + "BA_TF_ACC_VAR_region_project_id", + "BA_TF_ACC_VAR_region_provider", + "BA_TF_ACC_VAR_region_region_id", } - projectId = os.Getenv("BA_TF_ACC_VAR_regions_project_id") - provider = os.Getenv("BA_TF_ACC_VAR_regions_provider") - regionId = os.Getenv("BA_TF_ACC_VAR_regions_region_id") + projectId = os.Getenv("BA_TF_ACC_VAR_region_project_id") + provider = os.Getenv("BA_TF_ACC_VAR_region_provider") + regionId = os.Getenv("BA_TF_ACC_VAR_region_region_id") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) - testAccResourcePreCheck(t, "datasource regions", acc_env_vars_checklist) + testAccResourcePreCheck(t, "datasource region", acc_env_vars_checklist) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { - Config: regionsDataSourceConfig(projectId, provider, regionId), + Config: regionDataSourceConfig(projectId, provider, regionId), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair("data.biganimal_regions.test", "regions.0.name", "biganimal_regions.test", "name"), - resource.TestCheckResourceAttrPair("data.biganimal_regions.test", "regions.0.status", "biganimal_regions.test", "status"), + resource.TestCheckResourceAttrPair("data.biganimal_region.test", "region.0.name", "biganimal_region.test", "name"), + resource.TestCheckResourceAttrPair("data.biganimal_region.test", "region.0.status", "biganimal_region.test", "status"), ), }, }, }) } -func regionsDataSourceConfig(projectId, provider, regionId string) string { - return fmt.Sprintf(`data "biganimal_regions" "test" { +func regionDataSourceConfig(projectId, provider, regionId string) string { + return fmt.Sprintf(`data "biganimal_region" "test" { project_id = "%[1]s" cloud_provider = "%[2]s" region_id = "%[3]s" From f66369117da1409f281e1f91df79bfea5a23d60f Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 11 Jul 2023 14:08:33 +0800 Subject: [PATCH 47/49] fix: Removed data_source_regions --- .env.example | 9 ++++----- pkg/provider/data_source_region_test.go | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index b8db397f..97597b9a 100644 --- a/.env.example +++ b/.env.example @@ -18,8 +18,7 @@ BA_TF_ACC_VAR_region_region_id= BA_TF_ACC_VAR_pgd_project_id= BA_TF_ACC_VAR_pgd_name= -# data_source_regions -BA_TF_ACC_VAR_regions_project_id= -BA_TF_ACC_VAR_regions_provider= -BA_TF_ACC_VAR_regions_region_id= -BA_TF_ACC_VAR_regions_status= +# data_source_region +BA_TF_ACC_VAR_ds_region_project_id= +BA_TF_ACC_VAR_ds_region_provider= +BA_TF_ACC_VAR_ds_region_region_id= diff --git a/pkg/provider/data_source_region_test.go b/pkg/provider/data_source_region_test.go index a7b36fce..e41c97ca 100644 --- a/pkg/provider/data_source_region_test.go +++ b/pkg/provider/data_source_region_test.go @@ -11,34 +11,34 @@ import ( func TestAccDataSourceRegion_basic(t *testing.T) { var ( acc_env_vars_checklist = []string{ - "BA_TF_ACC_VAR_region_project_id", - "BA_TF_ACC_VAR_region_provider", - "BA_TF_ACC_VAR_region_region_id", + "BA_TF_ACC_VAR_ds_region_project_id", + "BA_TF_ACC_VAR_ds_region_provider", + "BA_TF_ACC_VAR_ds_region_region_id", } - projectId = os.Getenv("BA_TF_ACC_VAR_region_project_id") - provider = os.Getenv("BA_TF_ACC_VAR_region_provider") - regionId = os.Getenv("BA_TF_ACC_VAR_region_region_id") + projectId = os.Getenv("BA_TF_ACC_VAR_ds_region_project_id") + provider = os.Getenv("BA_TF_ACC_VAR_ds_region_provider") + regionId = os.Getenv("BA_TF_ACC_VAR_ds_region_region_id") ) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) - testAccResourcePreCheck(t, "datasource region", acc_env_vars_checklist) + testAccResourcePreCheck(t, "datasource regions", acc_env_vars_checklist) }, ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { - Config: regionDataSourceConfig(projectId, provider, regionId), + Config: regionsDataSourceConfig(projectId, provider, regionId), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair("data.biganimal_region.test", "region.0.name", "biganimal_region.test", "name"), - resource.TestCheckResourceAttrPair("data.biganimal_region.test", "region.0.status", "biganimal_region.test", "status"), + resource.TestCheckResourceAttrPair("data.biganimal_region.test", "regions.0.name", "biganimal_region.test", "name"), + resource.TestCheckResourceAttrPair("data.biganimal_region.test", "regions.0.status", "biganimal_region.test", "status"), ), }, }, }) } -func regionDataSourceConfig(projectId, provider, regionId string) string { +func regionsDataSourceConfig(projectId, provider, regionId string) string { return fmt.Sprintf(`data "biganimal_region" "test" { project_id = "%[1]s" cloud_provider = "%[2]s" From 3d65af20ea75020d6ef80de801a93e83e7e8d8a3 Mon Sep 17 00:00:00 2001 From: Serdar Dalgic Date: Tue, 11 Jul 2023 11:53:39 +0200 Subject: [PATCH 48/49] fix: remove the unused biganimal_regions data sources. --- .../biganimal_regions/data-source.tf | 30 ------------------- .../biganimal_regions/provider.tf | 8 ----- 2 files changed, 38 deletions(-) delete mode 100644 examples/data-sources/biganimal_regions/data-source.tf delete mode 100644 examples/data-sources/biganimal_regions/provider.tf diff --git a/examples/data-sources/biganimal_regions/data-source.tf b/examples/data-sources/biganimal_regions/data-source.tf deleted file mode 100644 index 28ed9ad0..00000000 --- a/examples/data-sources/biganimal_regions/data-source.tf +++ /dev/null @@ -1,30 +0,0 @@ -variable "cloud_provider" { - type = string - description = "Cloud Provider" - - validation { - condition = contains(["aws", "azure", "bah:aws"], var.cloud_provider) - error_message = "Please select one of the supported regions: aws, azure and bah:aws." - } -} - -variable "project_id" { - type = string - description = "BigAnimal Project ID" - -} - -data "biganimal_regions" "this" { - cloud_provider = var.cloud_provider - project_id = var.project_id - // region_id = "us-west-1" //optional - // query = "eu" // optional -} - -output "regions" { - value = data.biganimal_regions.this.regions -} - -output "cloud_provider_id" { - value = data.biganimal_regions.this.cloud_provider -} diff --git a/examples/data-sources/biganimal_regions/provider.tf b/examples/data-sources/biganimal_regions/provider.tf deleted file mode 100644 index 39fa059b..00000000 --- a/examples/data-sources/biganimal_regions/provider.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - biganimal = { - source = "EnterpriseDB/biganimal" - version = "0.4.2" - } - } -} From 9acd97338e8df072b94b9d152c2f4d26269f64ce Mon Sep 17 00:00:00 2001 From: YanniHu1996 Date: Tue, 11 Jul 2023 20:13:03 +0800 Subject: [PATCH 49/49] fix: Fixed panic --- pkg/provider/resource_region.go | 5 +++++ pkg/provider/resource_region_test.go | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/provider/resource_region.go b/pkg/provider/resource_region.go index 90ae318a..8bed744e 100644 --- a/pkg/provider/resource_region.go +++ b/pkg/provider/resource_region.go @@ -163,6 +163,11 @@ func (r *regionResource) Delete(ctx context.Context, req resource.DeleteRequest, } func (r *regionResource) ensureStatueUpdated(ctx context.Context, region Region) frameworkdiag.Diagnostics { + if region.Status == nil { + status := api.REGION_ACTIVE + region.Status = &status + } + diags := frameworkdiag.Diagnostics{} if err := r.client.Update(ctx, *region.Status, *region.ProjectID, *region.CloudProvider, *region.RegionID); err != nil { if appendDiagFromBAErr(err, &diags) { diff --git a/pkg/provider/resource_region_test.go b/pkg/provider/resource_region_test.go index aeb5bca6..6ef15c68 100644 --- a/pkg/provider/resource_region_test.go +++ b/pkg/provider/resource_region_test.go @@ -21,6 +21,11 @@ func TestAccResourceRegion_basic(t *testing.T) { provider = os.Getenv("BA_TF_ACC_VAR_region_provider") regionID = os.Getenv("BA_TF_ACC_VAR_region_region_id") + regionConfigWithDefault = `resource "biganimal_region" "this" { + project_id = "%s" + cloud_provider = "%s" + region_id = "%s" +}` regionConfig = `resource "biganimal_region" "this" { status = "%s" project_id = "%s" @@ -37,7 +42,7 @@ func TestAccResourceRegion_basic(t *testing.T) { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, Steps: []resource.TestStep{ { - Config: fmt.Sprintf(regionConfig, api.REGION_ACTIVE, projectID, provider, regionID), + Config: fmt.Sprintf(regionConfigWithDefault, projectID, provider, regionID), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("biganimal_region.this", "status", api.REGION_ACTIVE), ),