feat: auto generated files and new structure (#4)
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Reviewed-on: #4
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
This commit is contained in:
parent
979220be66
commit
9f41c4da7f
1283 changed files with 273211 additions and 4614 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,39 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func DatabaseResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The owner of the database.",
|
||||
MarkdownDescription: "The owner of the database.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseModel struct {
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
|
||||
postgresflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavors/datasources_gen"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
|
|
@ -143,9 +143,9 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
|
|||
Computed: true,
|
||||
},
|
||||
},
|
||||
CustomType: postgresflex.StorageClassesType{
|
||||
CustomType: postgresflexalphaGen.StorageClassesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: postgresflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -210,9 +210,9 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
model.MinGb = types.Int64Value(*f.MinGB)
|
||||
|
||||
if f.StorageClasses == nil {
|
||||
model.StorageClasses = types.ListNull(postgresflex.StorageClassesType{
|
||||
model.StorageClasses = types.ListNull(postgresflexalphaGen.StorageClassesType{
|
||||
ObjectType: basetypes.ObjectType{
|
||||
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: postgresflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
|
@ -220,8 +220,8 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
for _, sc := range *f.StorageClasses {
|
||||
scList = append(
|
||||
scList,
|
||||
postgresflex.NewStorageClassesValueMust(
|
||||
postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
postgresflexalphaGen.NewStorageClassesValueMust(
|
||||
postgresflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"class": types.StringValue(*sc.Class),
|
||||
"max_io_per_sec": types.Int64Value(*sc.MaxIoPerSec),
|
||||
|
|
@ -231,9 +231,9 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
)
|
||||
}
|
||||
storageClassesList := types.ListValueMust(
|
||||
postgresflex.StorageClassesType{
|
||||
postgresflexalphaGen.StorageClassesType{
|
||||
ObjectType: basetypes.ObjectType{
|
||||
AttrTypes: postgresflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: postgresflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
scList,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflex
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
|
|
@ -14,42 +12,18 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
func FlavorsDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"cpu": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"node_type": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"storage_class": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"flavors": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"cpu": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The cpu count of the instance.",
|
||||
MarkdownDescription: "The cpu count of the instance.",
|
||||
|
|
@ -70,7 +44,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||
},
|
||||
"memory": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The memory of the instance in Gibibyte.",
|
||||
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||
|
|
@ -81,7 +54,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
|
||||
},
|
||||
"node_type": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "defines the nodeType it can be either single or replica",
|
||||
MarkdownDescription: "defines the nodeType it can be either single or replica",
|
||||
|
|
@ -205,9 +177,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
}
|
||||
|
||||
type FlavorsModel struct {
|
||||
Cpu types.Int64 `tfsdk:"cpu"`
|
||||
Ram types.Int64 `tfsdk:"ram"`
|
||||
NodeType types.String `tfsdk:"node_type"`
|
||||
Flavors types.List `tfsdk:"flavors"`
|
||||
Page types.Int64 `tfsdk:"page"`
|
||||
Pagination PaginationValue `tfsdk:"pagination"`
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
postgresflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavors/datasources_gen"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &flavorsDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &flavorsDataSource{}
|
||||
)
|
||||
|
||||
func NewFlavorsDataSource() datasource.DataSource {
|
||||
return &flavorsDataSource{}
|
||||
}
|
||||
|
||||
type flavorsDataSource struct {
|
||||
client *postgresflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavors"
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = postgresflexalphaGen.FlavorsDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (d *flavorsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := postgresflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "Postgres Flex version client configured")
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data postgresflexalphaGen.FlavorsModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
|
||||
// Example data value setting
|
||||
// data.Id = types.StringValue("example-id")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package sqlserverflex
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
|
|
@ -14,42 +12,18 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
func FlavorsDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"cpu": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"node_type": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"storage_class": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
//Description: "The cpu count of the instance.",
|
||||
//MarkdownDescription: "The cpu count of the instance.",
|
||||
},
|
||||
"flavors": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"cpu": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The cpu count of the instance.",
|
||||
MarkdownDescription: "The cpu count of the instance.",
|
||||
|
|
@ -70,7 +44,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||
},
|
||||
"memory": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The memory of the instance in Gibibyte.",
|
||||
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||
|
|
@ -81,7 +54,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
|
||||
},
|
||||
"node_type": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "defines the nodeType it can be either single or replica",
|
||||
MarkdownDescription: "defines the nodeType it can be either single or replica",
|
||||
|
|
@ -205,9 +177,6 @@ func FlavorDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
}
|
||||
|
||||
type FlavorsModel struct {
|
||||
Cpu types.Int64 `tfsdk:"cpu"`
|
||||
Ram types.Int64 `tfsdk:"ram"`
|
||||
NodeType types.String `tfsdk:"node_type"`
|
||||
Flavors types.List `tfsdk:"flavors"`
|
||||
Page types.Int64 `tfsdk:"page"`
|
||||
Pagination PaginationValue `tfsdk:"pagination"`
|
||||
|
|
@ -7,18 +7,13 @@ import (
|
|||
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
postgresflexalpha2 "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/datasources_gen"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
|
|
@ -38,20 +33,12 @@ type instanceDataSource struct {
|
|||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (r *instanceDataSource) Metadata(
|
||||
_ context.Context,
|
||||
req datasource.MetadataRequest,
|
||||
resp *datasource.MetadataResponse,
|
||||
) {
|
||||
func (r *instanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_instance"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (r *instanceDataSource) Configure(
|
||||
ctx context.Context,
|
||||
req datasource.ConfigureRequest,
|
||||
resp *datasource.ConfigureResponse,
|
||||
) {
|
||||
func (r *instanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
|
|
@ -67,131 +54,13 @@ func (r *instanceDataSource) Configure(
|
|||
}
|
||||
|
||||
// Schema defines the schema for the data source.
|
||||
func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Postgres Flex instance data source schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal data source. ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
|
||||
"instance_id": "ID of the PostgresFlex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Instance name.",
|
||||
"acl": "The Access Control List (ACL) for the PostgresFlex instance.",
|
||||
"region": "The resource region. If not defined, the provider region is used.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Computed: true,
|
||||
},
|
||||
"backup_schedule": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
"flavor_id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"replicas": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
"storage": schema.SingleNestedAttribute{
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"class": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"size": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
// the region cannot be found, so it has to be passed
|
||||
Optional: true,
|
||||
Description: descriptions["region"],
|
||||
},
|
||||
"encryption": schema.SingleNestedAttribute{
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"key_id": schema.StringAttribute{
|
||||
Description: descriptions["key_id"],
|
||||
Computed: true,
|
||||
},
|
||||
"key_version": schema.StringAttribute{
|
||||
Description: descriptions["key_version"],
|
||||
Computed: true,
|
||||
},
|
||||
"keyring_id": schema.StringAttribute{
|
||||
Description: descriptions["keyring_id"],
|
||||
Computed: true,
|
||||
},
|
||||
"service_account": schema.StringAttribute{
|
||||
Description: descriptions["service_account"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
Description: descriptions["encryption"],
|
||||
},
|
||||
"network": schema.SingleNestedAttribute{
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"access_scope": schema.StringAttribute{
|
||||
Description: descriptions["access_scope"],
|
||||
Computed: true,
|
||||
},
|
||||
"acl": schema.ListAttribute{
|
||||
Description: descriptions["acl"],
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
"instance_address": schema.StringAttribute{
|
||||
Description: descriptions["instance_address"],
|
||||
Computed: true,
|
||||
},
|
||||
"router_address": schema.StringAttribute{
|
||||
Description: descriptions["router_address"],
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
Description: descriptions["network"],
|
||||
},
|
||||
},
|
||||
}
|
||||
func (r *instanceDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = postgresflexalpha2.InstanceDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *instanceDataSource) Read(
|
||||
ctx context.Context,
|
||||
req datasource.ReadRequest,
|
||||
resp *datasource.ReadResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha2.InstanceModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -224,43 +93,12 @@ func (r *instanceDataSource) Read(
|
|||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
|
||||
err = mapGetDataInstanceResponseToModel(ctx, &model, instanceResp)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error reading instance",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,217 +3,165 @@ package postgresflexalpha
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
postgresflexalphadatasource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/datasources_gen"
|
||||
postgresflexalpharesource "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/resources_gen"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
)
|
||||
|
||||
func mapFields(
|
||||
ctx context.Context,
|
||||
resp *postgresflex.GetInstanceResponse,
|
||||
model *Model,
|
||||
storage *storageModel,
|
||||
encryption *encryptionModel,
|
||||
network *networkModel,
|
||||
region string,
|
||||
) error {
|
||||
if resp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
instance := resp
|
||||
func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalpharesource.InstanceModel, resp *postgresflex.GetInstanceResponse) error {
|
||||
tflog.Info(ctx, ">>>> MSH DEBUG <<<<", map[string]interface{}{
|
||||
"id": m.Id.ValueString(),
|
||||
"instance_id": m.InstanceId.ValueString(),
|
||||
"backup_schedule": m.BackupSchedule.ValueString(),
|
||||
"flavor_id": m.FlavorId.ValueString(),
|
||||
"encryption.kek_key_id": m.Encryption.KekKeyId.ValueString(),
|
||||
"encryption.kek_key_ring_id": m.Encryption.KekKeyRingId.ValueString(),
|
||||
"encryption.kek_key_version": m.Encryption.KekKeyVersion.ValueString(),
|
||||
"encryption.service_account": m.Encryption.ServiceAccount.ValueString(),
|
||||
"is_deletable": m.IsDeletable.ValueBool(),
|
||||
"name": m.Name.ValueString(),
|
||||
"status": m.Status.ValueString(),
|
||||
"retention_days": m.RetentionDays.ValueInt64(),
|
||||
"replicas": m.Replicas.ValueInt64(),
|
||||
"network.instance_address": m.Network.InstanceAddress.ValueString(),
|
||||
"network.router_address": m.Network.RouterAddress.ValueString(),
|
||||
"version": m.Version.ValueString(),
|
||||
"network.acl": m.Network.Acl.String(),
|
||||
})
|
||||
|
||||
var instanceId string
|
||||
if model.InstanceId.ValueString() != "" {
|
||||
instanceId = model.InstanceId.ValueString()
|
||||
} else if instance.Id != nil {
|
||||
instanceId = *instance.Id
|
||||
} else {
|
||||
return fmt.Errorf("instance id not present")
|
||||
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
|
||||
if resp.HasEncryption() {
|
||||
m.Encryption = postgresflexalpharesource.NewEncryptionValueMust(
|
||||
m.Encryption.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"kek_key_id": types.StringValue(resp.Encryption.GetKekKeyId()),
|
||||
"kek_key_ring_id": types.StringValue(resp.Encryption.GetKekKeyRingId()),
|
||||
"kek_key_version": types.StringValue(resp.Encryption.GetKekKeyVersion()),
|
||||
"service_account": types.StringValue(resp.Encryption.GetServiceAccount()),
|
||||
},
|
||||
)
|
||||
}
|
||||
m.FlavorId = types.StringValue(resp.GetFlavorId())
|
||||
if m.Id.IsNull() || m.Id.IsUnknown() {
|
||||
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
|
||||
}
|
||||
m.InstanceId = types.StringPointerValue(resp.Id)
|
||||
|
||||
var encryptionValues map[string]attr.Value
|
||||
if instance.Encryption == nil {
|
||||
encryptionValues = map[string]attr.Value{
|
||||
"keyring_id": encryption.KeyRingId,
|
||||
"key_id": encryption.KeyId,
|
||||
"key_version": encryption.KeyVersion,
|
||||
"service_account": encryption.ServiceAccount,
|
||||
}
|
||||
} else {
|
||||
encryptionValues = map[string]attr.Value{
|
||||
"keyring_id": types.StringValue(*instance.Encryption.KekKeyRingId),
|
||||
"key_id": types.StringValue(*instance.Encryption.KekKeyId),
|
||||
"key_version": types.StringValue(*instance.Encryption.KekKeyVersion),
|
||||
"service_account": types.StringValue(*instance.Encryption.ServiceAccount),
|
||||
}
|
||||
}
|
||||
encryptionObject, diags := types.ObjectValue(encryptionTypes, encryptionValues)
|
||||
//m.IsDeletable = types.BoolUnknown()
|
||||
//if isDel, ok := resp.GetIsDeletableOk(); ok {
|
||||
// m.IsDeletable = types.BoolValue(isDel)
|
||||
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
|
||||
//}
|
||||
|
||||
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating encryption: %w", core.DiagsToError(diags))
|
||||
return fmt.Errorf("failed converting network acl from response")
|
||||
}
|
||||
|
||||
var networkValues map[string]attr.Value
|
||||
if instance.Network == nil {
|
||||
networkValues = map[string]attr.Value{
|
||||
"acl": network.ACL,
|
||||
"access_scope": network.AccessScope,
|
||||
"instance_address": network.InstanceAddress,
|
||||
"router_address": network.RouterAddress,
|
||||
}
|
||||
} else {
|
||||
aclList, diags := types.ListValueFrom(ctx, types.StringType, *instance.Network.Acl)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating network (acl list): %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
networkValues = map[string]attr.Value{
|
||||
"acl": aclList,
|
||||
"access_scope": types.StringPointerValue((*string)(instance.Network.AccessScope)),
|
||||
"instance_address": types.StringPointerValue(instance.Network.InstanceAddress),
|
||||
"router_address": types.StringPointerValue(instance.Network.RouterAddress),
|
||||
}
|
||||
netInstAdd := types.StringValue("")
|
||||
if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok {
|
||||
netInstAdd = types.StringValue(instAdd)
|
||||
}
|
||||
networkObject, diags := types.ObjectValue(networkTypes, networkValues)
|
||||
|
||||
netRtrAdd := types.StringValue("")
|
||||
if rtrAdd, ok := resp.Network.GetRouterAddressOk(); ok {
|
||||
netRtrAdd = types.StringValue(rtrAdd)
|
||||
}
|
||||
|
||||
net, diags := postgresflexalpharesource.NewNetworkValue(
|
||||
postgresflexalpharesource.NetworkValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"access_scope": basetypes.NewStringValue(string(resp.Network.GetAccessScope())),
|
||||
"acl": netAcl,
|
||||
"instance_address": netInstAdd,
|
||||
"router_address": netRtrAdd,
|
||||
},
|
||||
)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating network: %w", core.DiagsToError(diags))
|
||||
return fmt.Errorf("failed converting network from response")
|
||||
}
|
||||
|
||||
var storageValues map[string]attr.Value
|
||||
if instance.Storage == nil {
|
||||
storageValues = map[string]attr.Value{
|
||||
"class": storage.Class,
|
||||
"size": storage.Size,
|
||||
}
|
||||
} else {
|
||||
storageValues = map[string]attr.Value{
|
||||
"class": types.StringValue(*instance.Storage.PerformanceClass),
|
||||
"size": types.Int64PointerValue(instance.Storage.Size),
|
||||
}
|
||||
}
|
||||
storageObject, diags := types.ObjectValue(storageTypes, storageValues)
|
||||
m.Network = net
|
||||
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))
|
||||
m.RetentionDays = types.Int64Value(resp.GetRetentionDays())
|
||||
|
||||
m.Name = types.StringValue(resp.GetName())
|
||||
|
||||
//m.Status = types.StringUnknown()
|
||||
//if status, ok := resp.GetStatusOk(); ok {
|
||||
// m.Status = types.StringValue(string(status))
|
||||
//}
|
||||
m.Status = types.StringValue(string(resp.GetStatus()))
|
||||
|
||||
storage, diags := postgresflexalpharesource.NewStorageValue(
|
||||
postgresflexalpharesource.StorageValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"performance_class": types.StringValue(resp.Storage.GetPerformanceClass()),
|
||||
"size": types.Int64Value(resp.Storage.GetSize()),
|
||||
},
|
||||
)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating storage: %w", core.DiagsToError(diags))
|
||||
return fmt.Errorf("failed converting storage from response")
|
||||
}
|
||||
m.Storage = storage
|
||||
|
||||
if instance.Replicas == nil {
|
||||
diags.AddError("error mapping fields", "replicas is nil")
|
||||
return fmt.Errorf("replicas is nil")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
|
||||
model.InstanceId = types.StringValue(instanceId)
|
||||
model.Name = types.StringPointerValue(instance.Name)
|
||||
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
|
||||
model.FlavorId = types.StringPointerValue(instance.FlavorId)
|
||||
model.Replicas = types.Int64Value(int64(*instance.Replicas))
|
||||
model.Storage = storageObject
|
||||
model.Version = types.StringPointerValue(instance.Version)
|
||||
model.Region = types.StringValue(region)
|
||||
model.Encryption = encryptionObject
|
||||
model.Network = networkObject
|
||||
m.Version = types.StringValue(resp.GetVersion())
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(
|
||||
model *Model,
|
||||
storage *storageModel,
|
||||
enc *encryptionModel,
|
||||
net *networkModel,
|
||||
) (*postgresflex.CreateInstanceRequestPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
if storage == nil {
|
||||
return nil, fmt.Errorf("nil storage")
|
||||
func mapGetDataInstanceResponseToModel(ctx context.Context, m *postgresflexalphadatasource.InstanceModel, resp *postgresflex.GetInstanceResponse) error {
|
||||
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
|
||||
//nolint:gocritic
|
||||
// m.Encryption = postgresflexalpharesource.EncryptionValue{
|
||||
// KekKeyId: types.StringValue(resp.Encryption.GetKekKeyId()),
|
||||
// KekKeyRingId: types.StringValue(resp.Encryption.GetKekKeyRingId()),
|
||||
// KekKeyVersion: types.StringValue(resp.Encryption.GetKekKeyVersion()),
|
||||
// ServiceAccount: types.StringValue(resp.Encryption.GetServiceAccount()),
|
||||
// }
|
||||
m.FlavorId = types.StringValue(resp.GetFlavorId())
|
||||
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
|
||||
m.InstanceId = types.StringPointerValue(resp.Id)
|
||||
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
|
||||
m.Name = types.StringValue(resp.GetName())
|
||||
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("failed converting network acl from response")
|
||||
}
|
||||
|
||||
var replVal int32
|
||||
if !model.Replicas.IsNull() && !model.Replicas.IsUnknown() {
|
||||
if model.Replicas.ValueInt64() > math.MaxInt32 {
|
||||
return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64())
|
||||
}
|
||||
replVal = int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
|
||||
}
|
||||
|
||||
storagePayload := &postgresflex.CreateInstanceRequestPayloadGetStorageArgType{
|
||||
PerformanceClass: conversion.StringValueToPointer(storage.Class),
|
||||
Size: conversion.Int64ValueToPointer(storage.Size),
|
||||
}
|
||||
|
||||
encryptionPayload := &postgresflex.CreateInstanceRequestPayloadGetEncryptionArgType{}
|
||||
if enc != nil {
|
||||
encryptionPayload.KekKeyId = conversion.StringValueToPointer(enc.KeyId)
|
||||
encryptionPayload.KekKeyVersion = conversion.StringValueToPointer(enc.KeyVersion)
|
||||
encryptionPayload.KekKeyRingId = conversion.StringValueToPointer(enc.KeyRingId)
|
||||
encryptionPayload.ServiceAccount = conversion.StringValueToPointer(enc.ServiceAccount)
|
||||
}
|
||||
|
||||
var aclElements []string
|
||||
if net != nil && !net.ACL.IsNull() && !net.ACL.IsUnknown() {
|
||||
aclElements = make([]string, 0, len(net.ACL.Elements()))
|
||||
diags := net.ACL.ElementsAs(context.TODO(), &aclElements, false)
|
||||
if diags.HasError() {
|
||||
return nil, fmt.Errorf("creating network: %w", core.DiagsToError(diags))
|
||||
}
|
||||
}
|
||||
|
||||
if len(aclElements) < 1 {
|
||||
return nil, fmt.Errorf("no acl elements found")
|
||||
}
|
||||
|
||||
networkPayload := &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{}
|
||||
if net != nil {
|
||||
networkPayload = &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{
|
||||
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(conversion.StringValueToPointer(net.AccessScope)),
|
||||
Acl: &aclElements,
|
||||
}
|
||||
}
|
||||
|
||||
return &postgresflex.CreateInstanceRequestPayload{
|
||||
Acl: &aclElements,
|
||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||
Encryption: encryptionPayload,
|
||||
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Network: networkPayload,
|
||||
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(&replVal),
|
||||
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
|
||||
Storage: storagePayload,
|
||||
Version: conversion.StringValueToPointer(model.Version),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(
|
||||
model *Model,
|
||||
storage *storageModel,
|
||||
_ *networkModel,
|
||||
) (*postgresflex.UpdateInstancePartiallyRequestPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
if storage == nil {
|
||||
return nil, fmt.Errorf("nil storage")
|
||||
}
|
||||
|
||||
return &postgresflex.UpdateInstancePartiallyRequestPayload{
|
||||
// Acl: postgresflexalpha.UpdateInstancePartiallyRequestPayloadGetAclAttributeType{
|
||||
// Items: &acl,
|
||||
// },
|
||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||
FlavorId: conversion.StringValueToPointer(model.FlavorId),
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
// Replicas: conversion.Int64ValueToPointer(model.Replicas),
|
||||
Storage: &postgresflex.StorageUpdate{
|
||||
Size: conversion.Int64ValueToPointer(storage.Size),
|
||||
net, diags := postgresflexalphadatasource.NewNetworkValue(
|
||||
postgresflexalphadatasource.NetworkValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"access_scope": types.StringValue(string(resp.Network.GetAccessScope())),
|
||||
"acl": netAcl,
|
||||
"instance_address": types.StringValue(resp.Network.GetInstanceAddress()),
|
||||
"router_address": types.StringValue(resp.Network.GetRouterAddress()),
|
||||
},
|
||||
Version: conversion.StringValueToPointer(model.Version),
|
||||
}, nil
|
||||
)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("failed converting network from response")
|
||||
}
|
||||
|
||||
m.Network = net
|
||||
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))
|
||||
m.RetentionDays = types.Int64Value(resp.GetRetentionDays())
|
||||
m.Status = types.StringValue(string(resp.GetStatus()))
|
||||
storage, diags := postgresflexalphadatasource.NewStorageValue(
|
||||
postgresflexalphadatasource.StorageValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"performance_class": types.StringValue(resp.Storage.GetPerformanceClass()),
|
||||
"size": types.Int64Value(resp.Storage.GetSize()),
|
||||
},
|
||||
)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("failed converting storage from response")
|
||||
}
|
||||
m.Storage = storage
|
||||
m.Version = types.StringValue(resp.GetVersion())
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
||||
FlavorId types.String `tfsdk:"flavor_id"`
|
||||
Replicas types.Int64 `tfsdk:"replicas"`
|
||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||
Storage types.Object `tfsdk:"storage"`
|
||||
Version types.String `tfsdk:"version"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Encryption types.Object `tfsdk:"encryption"`
|
||||
Network types.Object `tfsdk:"network"`
|
||||
}
|
||||
|
||||
type encryptionModel struct {
|
||||
KeyRingId types.String `tfsdk:"keyring_id"`
|
||||
KeyId types.String `tfsdk:"key_id"`
|
||||
KeyVersion types.String `tfsdk:"key_version"`
|
||||
ServiceAccount types.String `tfsdk:"service_account"`
|
||||
}
|
||||
|
||||
var encryptionTypes = map[string]attr.Type{
|
||||
"keyring_id": basetypes.StringType{},
|
||||
"key_id": basetypes.StringType{},
|
||||
"key_version": basetypes.StringType{},
|
||||
"service_account": basetypes.StringType{},
|
||||
}
|
||||
|
||||
type networkModel struct {
|
||||
ACL types.List `tfsdk:"acl"`
|
||||
AccessScope types.String `tfsdk:"access_scope"`
|
||||
InstanceAddress types.String `tfsdk:"instance_address"`
|
||||
RouterAddress types.String `tfsdk:"router_address"`
|
||||
}
|
||||
|
||||
var networkTypes = map[string]attr.Type{
|
||||
"acl": basetypes.ListType{ElemType: types.StringType},
|
||||
"access_scope": basetypes.StringType{},
|
||||
"instance_address": basetypes.StringType{},
|
||||
"router_address": basetypes.StringType{},
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Storage
|
||||
type storageModel struct {
|
||||
Class types.String `tfsdk:"class"`
|
||||
Size types.Int64 `tfsdk:"size"`
|
||||
}
|
||||
|
||||
// Types corresponding to storageModel
|
||||
var storageTypes = map[string]attr.Type{
|
||||
"class": basetypes.StringType{},
|
||||
"size": basetypes.Int64Type{},
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
fields:
|
||||
- name: 'backup_schedule'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'encryption.kek_key_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.kek_key_ring_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.kek_key_version'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.service_account'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'flavor_id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'instance_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'is_deletable'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'name'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'network.access_scope'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'network.acl'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'network.instance_address'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'network.router_address'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'project_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'region'
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'replicas'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'retention_days'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'status'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'storage'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'storage.performance_class'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'storage.size'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'version'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
|
@ -2,36 +2,29 @@ package postgresflexalpha
|
|||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
postgresflexalpha "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/resources_gen"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
wait "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/wait/postgresflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
)
|
||||
|
||||
const packageName = "postgresflexalpha"
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &instanceResource{}
|
||||
|
|
@ -39,7 +32,7 @@ var (
|
|||
_ resource.ResourceWithImportState = &instanceResource{}
|
||||
_ resource.ResourceWithModifyPlan = &instanceResource{}
|
||||
_ resource.ResourceWithValidateConfig = &instanceResource{}
|
||||
// _ resource.ResourceWithIdentity = &instanceResource{}
|
||||
_ resource.ResourceWithIdentity = &instanceResource{}
|
||||
)
|
||||
|
||||
// NewInstanceResource is a helper function to simplify the provider implementation.
|
||||
|
|
@ -53,12 +46,14 @@ type instanceResource struct {
|
|||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (r *instanceResource) ValidateConfig(
|
||||
ctx context.Context,
|
||||
req resource.ValidateConfigRequest,
|
||||
resp *resource.ValidateConfigResponse,
|
||||
) {
|
||||
var data Model
|
||||
type InstanceResourceIdentityModel struct {
|
||||
ProjectID types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
InstanceID types.String `tfsdk:"instance_id"`
|
||||
}
|
||||
|
||||
func (r *instanceResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
|
||||
var data postgresflexalpha.InstanceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -77,12 +72,8 @@ func (r *instanceResource) ValidateConfig(
|
|||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
func (r *instanceResource) ModifyPlan(
|
||||
ctx context.Context,
|
||||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel Model
|
||||
func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel postgresflexalpha.InstanceModel
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
|
|
@ -92,7 +83,7 @@ func (r *instanceResource) ModifyPlan(
|
|||
return
|
||||
}
|
||||
|
||||
var planModel Model
|
||||
var planModel postgresflexalpha.InstanceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -134,252 +125,54 @@ func (r *instanceResource) Configure(
|
|||
tflog.Info(ctx, "Postgres Flex instance client configured")
|
||||
}
|
||||
|
||||
/*
|
||||
until tfplugingen framework can handle plan modifiers, we use a function to add them
|
||||
*/
|
||||
|
||||
//go:embed planModifiers.yaml
|
||||
var modifiersFileByte []byte
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
|
||||
"instance_id": "ID of the PostgresFlex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Instance name.",
|
||||
"backup_schedule": "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.",
|
||||
"retention_days": "The days of the retention period.",
|
||||
"flavor_id": "The ID of the flavor.",
|
||||
"replicas": "The number of replicas.",
|
||||
"storage": "The block of the storage configuration.",
|
||||
"storage_class": "The storage class used.",
|
||||
"storage_size": "The disk size of the storage.",
|
||||
"region": "The resource region. If not defined, the provider region is used.",
|
||||
"version": "The database version used.",
|
||||
"encryption": "The encryption block.",
|
||||
"keyring_id": "KeyRing ID of the encryption key.",
|
||||
"key_id": "Key ID of the encryption key.",
|
||||
"key_version": "Key version of the encryption key.",
|
||||
"service_account": "The service account ID of the service account.",
|
||||
"network": "The network block configuration.",
|
||||
"access_scope": "The access scope. (Either SNA or PUBLIC)",
|
||||
"acl": "The Access Control List (ACL) for the PostgresFlex instance.",
|
||||
"instance_address": "The returned instance address.",
|
||||
"router_address": "The returned router address.",
|
||||
func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
schema := postgresflexalpha.InstanceResourceSchema(ctx)
|
||||
fields, err := postgresflexUtils.ReadModifiersConfig(modifiersFileByte)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
err = postgresflexUtils.AddPlanModifiersToResourceSchema(fields, &schema)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = schema
|
||||
}
|
||||
|
||||
func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
|
||||
resp.IdentitySchema = identityschema.Schema{
|
||||
Attributes: map[string]identityschema.Attribute{
|
||||
"project_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // must be set during import by the practitioner
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
"region": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile("^[a-z]([-a-z0-9]*[a-z0-9])?$"),
|
||||
"must start with a letter, must have lower case letters, numbers or hyphens, and no hyphen at the end",
|
||||
),
|
||||
},
|
||||
},
|
||||
"backup_schedule": schema.StringAttribute{
|
||||
Required: true,
|
||||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Description: descriptions["retention_days"],
|
||||
Required: true,
|
||||
},
|
||||
"flavor_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
},
|
||||
"replicas": schema.Int64Attribute{
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"storage": schema.SingleNestedAttribute{
|
||||
Required: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"class": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: descriptions["storage_class"],
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"size": schema.Int64Attribute{
|
||||
Description: descriptions["storage_size"],
|
||||
Required: true,
|
||||
// PlanModifiers: []planmodifier.Int64{
|
||||
// TODO - req replace if new size smaller than state size
|
||||
// int64planmodifier.RequiresReplaceIf(),
|
||||
// },
|
||||
},
|
||||
},
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Description: descriptions["version"],
|
||||
Required: true,
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
// must be computed to allow for storing the override value from the provider
|
||||
Computed: true,
|
||||
Description: descriptions["region"],
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"encryption": schema.SingleNestedAttribute{
|
||||
Required: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"key_id": schema.StringAttribute{
|
||||
Description: descriptions["key_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"key_version": schema.StringAttribute{
|
||||
Description: descriptions["key_version"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"keyring_id": schema.StringAttribute{
|
||||
Description: descriptions["keyring_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"service_account": schema.StringAttribute{
|
||||
Description: descriptions["service_account"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
},
|
||||
Description: descriptions["encryption"],
|
||||
//Validators: nil,
|
||||
PlanModifiers: []planmodifier.Object{},
|
||||
},
|
||||
"network": schema.SingleNestedAttribute{
|
||||
Required: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"access_scope": schema.StringAttribute{
|
||||
Default: stringdefault.StaticString(
|
||||
"PUBLIC",
|
||||
),
|
||||
Description: descriptions["access_scope"],
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.NoSeparator(),
|
||||
stringvalidator.OneOf("SNA", "PUBLIC"),
|
||||
},
|
||||
},
|
||||
"acl": schema.ListAttribute{
|
||||
Description: descriptions["acl"],
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.List{
|
||||
listplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"instance_address": schema.StringAttribute{
|
||||
Description: descriptions["instance_address"],
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"router_address": schema.StringAttribute{
|
||||
Description: descriptions["router_address"],
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
},
|
||||
Description: descriptions["network"],
|
||||
//MarkdownDescription: "",
|
||||
//Validators: nil,
|
||||
PlanModifiers: []planmodifier.Object{},
|
||||
"instance_id": identityschema.StringAttribute{
|
||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// func (r *instanceResource) IdentitySchema(_ context.Context, _ resource.IdentitySchemaRequest, resp *resource.IdentitySchemaResponse) {
|
||||
// resp.IdentitySchema = identityschema.Schema{
|
||||
// Attributes: map[string]identityschema.Attribute{
|
||||
// "project_id": identityschema.StringAttribute{
|
||||
// RequiredForImport: true, // must be set during import by the practitioner
|
||||
// },
|
||||
// "region": identityschema.StringAttribute{
|
||||
// RequiredForImport: true, // must be set during import by the practitioner
|
||||
// },
|
||||
// "instance_id": identityschema.StringAttribute{
|
||||
// RequiredForImport: true, // must be set during import by the practitioner
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
func (r *instanceResource) Create(
|
||||
ctx context.Context,
|
||||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
var model postgresflexalpha.InstanceModel
|
||||
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -393,96 +186,57 @@ func (r *instanceResource) Create(
|
|||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var acl []string
|
||||
if !network.ACL.IsNull() && !network.ACL.IsUnknown() {
|
||||
diags = network.ACL.ElementsAs(ctx, &acl, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(&model, storage, encryption, network)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating instance",
|
||||
fmt.Sprintf("Creating API payload: %v", err),
|
||||
)
|
||||
var netAcl []string
|
||||
diag := model.Network.Acl.ElementsAs(ctx, &netAcl, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diag.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if model.Replicas.ValueInt64() > math.MaxInt32 {
|
||||
resp.Diagnostics.AddError("invalid int32 value", "provided int64 value does not fit into int32")
|
||||
return
|
||||
}
|
||||
replVal := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
|
||||
payload := modelToCreateInstancePayload(netAcl, model, replVal)
|
||||
|
||||
// Create new instance
|
||||
createResp, err := r.client.CreateInstanceRequest(
|
||||
ctx,
|
||||
projectId,
|
||||
region,
|
||||
).CreateInstanceRequestPayload(*payload).Execute()
|
||||
createResp, err := r.client.CreateInstanceRequest(ctx, projectId, region).CreateInstanceRequestPayload(payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "error creating instance", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
instanceId := *createResp.Id
|
||||
instanceId, ok := createResp.GetIdOk()
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "error creating instance", "could not find instance id in response")
|
||||
return
|
||||
}
|
||||
|
||||
model.InstanceId = types.StringValue(instanceId)
|
||||
model.Id = utils.BuildInternalTerraformId(projectId, region, instanceId)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
|
||||
// Set data returned by API in identity
|
||||
identity := InstanceResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
InstanceID: types.StringValue(instanceId),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating instance",
|
||||
fmt.Sprintf("Wait handler error: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Wait handler error: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
err = mapGetInstanceResponseToModel(ctx, &model, waitResp)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating instance",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Error creating model: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
|
@ -492,55 +246,97 @@ func (r *instanceResource) Create(
|
|||
tflog.Info(ctx, "Postgres Flex instance created")
|
||||
}
|
||||
|
||||
func modelToCreateInstancePayload(netAcl []string, model postgresflexalpha.InstanceModel, replVal int32) postgresflex.CreateInstanceRequestPayload {
|
||||
payload := postgresflex.CreateInstanceRequestPayload{
|
||||
BackupSchedule: model.BackupSchedule.ValueStringPointer(),
|
||||
Encryption: &postgresflex.InstanceEncryption{
|
||||
KekKeyId: model.Encryption.KekKeyId.ValueStringPointer(),
|
||||
KekKeyRingId: model.Encryption.KekKeyRingId.ValueStringPointer(),
|
||||
KekKeyVersion: model.Encryption.KekKeyVersion.ValueStringPointer(),
|
||||
ServiceAccount: model.Encryption.ServiceAccount.ValueStringPointer(),
|
||||
},
|
||||
FlavorId: model.FlavorId.ValueStringPointer(),
|
||||
Name: model.Name.ValueStringPointer(),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(
|
||||
model.Network.AccessScope.ValueStringPointer(),
|
||||
),
|
||||
Acl: &netAcl,
|
||||
InstanceAddress: model.Network.InstanceAddress.ValueStringPointer(),
|
||||
RouterAddress: model.Network.RouterAddress.ValueStringPointer(),
|
||||
},
|
||||
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(&replVal),
|
||||
RetentionDays: model.RetentionDays.ValueInt64Pointer(),
|
||||
Storage: &postgresflex.StorageCreate{
|
||||
PerformanceClass: model.Storage.PerformanceClass.ValueStringPointer(),
|
||||
Size: model.Storage.Size.ValueInt64Pointer(),
|
||||
},
|
||||
Version: model.Version.ValueStringPointer(),
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *instanceResource) Read(
|
||||
ctx context.Context,
|
||||
req resource.ReadRequest,
|
||||
resp *resource.ReadResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
functionErrorSummary := "read instance failed"
|
||||
|
||||
var model postgresflexalpha.InstanceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Read identity data
|
||||
var identityData InstanceResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
// projectId := model.ProjectId.ValueString()
|
||||
// region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
// instanceId := model.InstanceId.ValueString()
|
||||
|
||||
var projectId string
|
||||
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
||||
projectId = model.ProjectId.ValueString()
|
||||
} else {
|
||||
if identityData.ProjectID.IsNull() || identityData.ProjectID.IsUnknown() {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "project_id not found in config")
|
||||
return
|
||||
}
|
||||
projectId = identityData.ProjectID.ValueString()
|
||||
}
|
||||
|
||||
var region string
|
||||
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
||||
region = r.providerData.GetRegionWithOverride(model.Region)
|
||||
} else {
|
||||
if identityData.Region.IsNull() || identityData.Region.IsUnknown() {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "region not found in config")
|
||||
return
|
||||
}
|
||||
region = r.providerData.GetRegionWithOverride(identityData.Region)
|
||||
}
|
||||
|
||||
var instanceId string
|
||||
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
|
||||
instanceId = model.InstanceId.ValueString()
|
||||
} else {
|
||||
if identityData.InstanceID.IsNull() || identityData.InstanceID.IsUnknown() {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "instance_id not found in config")
|
||||
return
|
||||
}
|
||||
instanceId = identityData.InstanceID.ValueString()
|
||||
}
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
var storage = storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, &storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, &network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, &encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
instanceResp, err := r.client.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
|
|
@ -548,21 +344,32 @@ func (r *instanceResource) Read(
|
|||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", err.Error())
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, instanceResp, &model, &storage, &encryption, &network, region)
|
||||
respInstanceID, ok := instanceResp.GetIdOk()
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, "response provided no ID")
|
||||
return
|
||||
}
|
||||
if !model.InstanceId.IsUnknown() && !model.InstanceId.IsNull() {
|
||||
if respInstanceID != instanceId {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
functionErrorSummary,
|
||||
"ID in response did not match ID in state",
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = mapGetInstanceResponseToModel(ctx, &model, instanceResp)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error reading instance",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, functionErrorSummary, fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -572,16 +379,24 @@ func (r *instanceResource) Read(
|
|||
return
|
||||
}
|
||||
|
||||
// Set data returned by API in identity
|
||||
identity := InstanceResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
InstanceID: types.StringValue(instanceId),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Postgres Flex instance read")
|
||||
}
|
||||
|
||||
// Update updates the resource and sets the updated Terraform state on success.
|
||||
func (r *instanceResource) Update(
|
||||
ctx context.Context,
|
||||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha.InstanceModel
|
||||
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -590,68 +405,70 @@ func (r *instanceResource) Update(
|
|||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
// Read identity data
|
||||
var identityData InstanceResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
//if model.InstanceId.IsNull() || model.InstanceId.IsUnknown() {
|
||||
// core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", "instanceId is null or unknown")
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//if model.ProjectId.IsNull() || model.ProjectId.IsUnknown() {
|
||||
// core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", "projectId is null or unknown")
|
||||
// return
|
||||
//}
|
||||
|
||||
//projectId := model.ProjectId.ValueString()
|
||||
//instanceId := model.InstanceId.ValueString()
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
instanceId := identityData.InstanceID.ValueString()
|
||||
region := model.Region.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// nolint:gocritic // need that code later
|
||||
// var acl []string
|
||||
// if !(model.ACL.IsNull() || model.ACL.IsUnknown()) {
|
||||
// diags = model.ACL.ElementsAs(ctx, &acl, false)
|
||||
// resp.Diagnostics.Append(diags...)
|
||||
// if resp.Diagnostics.HasError() {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
var storage = &storageModel{}
|
||||
if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var network = &networkModel{}
|
||||
if !model.Network.IsNull() && !model.Network.IsUnknown() {
|
||||
diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var encryption = &encryptionModel{}
|
||||
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
|
||||
diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(&model, storage, network)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error updating instance",
|
||||
fmt.Sprintf("Creating API payload: %v", err),
|
||||
)
|
||||
var netAcl []string
|
||||
diag := model.Network.Acl.ElementsAs(ctx, &netAcl, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if diag.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
if model.Replicas.ValueInt64() > math.MaxInt32 {
|
||||
resp.Diagnostics.AddError("invalid int32 value", "provided int64 value does not fit into int32")
|
||||
return
|
||||
}
|
||||
replInt32 := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
|
||||
|
||||
payload := postgresflex.UpdateInstanceRequestPayload{
|
||||
BackupSchedule: model.BackupSchedule.ValueStringPointer(),
|
||||
FlavorId: model.FlavorId.ValueStringPointer(),
|
||||
Name: model.Name.ValueStringPointer(),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(
|
||||
model.Network.AccessScope.ValueStringPointer(),
|
||||
),
|
||||
Acl: &netAcl,
|
||||
},
|
||||
Replicas: postgresflex.UpdateInstanceRequestPayloadGetReplicasAttributeType(&replInt32),
|
||||
RetentionDays: model.RetentionDays.ValueInt64Pointer(),
|
||||
Storage: &postgresflex.StorageUpdate{
|
||||
Size: model.Storage.Size.ValueInt64Pointer(),
|
||||
},
|
||||
Version: model.Version.ValueStringPointer(),
|
||||
}
|
||||
|
||||
// Update existing instance
|
||||
err = r.client.UpdateInstancePartiallyRequest(
|
||||
err := r.client.UpdateInstanceRequest(
|
||||
ctx,
|
||||
projectId,
|
||||
region,
|
||||
instanceId,
|
||||
).UpdateInstancePartiallyRequestPayload(*payload).Execute()
|
||||
).UpdateInstanceRequestPayload(payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
|
||||
return
|
||||
|
|
@ -659,34 +476,18 @@ func (r *instanceResource) Update(
|
|||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
waitResp, err := wait.PartialUpdateInstanceWaitHandler(
|
||||
ctx,
|
||||
r.client,
|
||||
projectId,
|
||||
region,
|
||||
instanceId,
|
||||
).WaitWithContext(ctx)
|
||||
waitResp, err := wait.PartialUpdateInstanceWaitHandler(ctx, r.client, projectId, region, instanceId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error updating instance",
|
||||
fmt.Sprintf("Instance update waiting: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
|
||||
err = mapGetInstanceResponseToModel(ctx, &model, waitResp)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error updating instance",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -696,12 +497,9 @@ func (r *instanceResource) Update(
|
|||
}
|
||||
|
||||
// Delete deletes the resource and removes the Terraform state on success.
|
||||
func (r *instanceResource) Delete(
|
||||
ctx context.Context,
|
||||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha.InstanceModel
|
||||
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -740,25 +538,46 @@ func (r *instanceResource) Delete(
|
|||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,instance_id
|
||||
func (r *instanceResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
// The expected format of the resource import identifier is: project_id,region,instance_id
|
||||
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing instance",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[instance_id] Got: %q", req.ID),
|
||||
)
|
||||
if req.ID != "" {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing instance",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[instance_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("region"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||
var identityData InstanceResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(
|
||||
resp.State.SetAttribute(
|
||||
ctx,
|
||||
path.Root("id"),
|
||||
utils.BuildInternalTerraformId(
|
||||
identityData.ProjectID.ValueString(),
|
||||
identityData.Region.ValueString(),
|
||||
identityData.InstanceID.ValueString(),
|
||||
),
|
||||
)...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), identityData.ProjectID.ValueString())...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), identityData.Region.ValueString())...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), identityData.InstanceID.ValueString())...)
|
||||
|
||||
tflog.Info(ctx, "Postgres Flex instance state imported")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
)
|
||||
|
||||
// type postgresFlexClientMocked struct {
|
||||
|
|
@ -26,608 +20,6 @@ import (
|
|||
// return c.getFlavorsResp, nil
|
||||
// }
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
const testRegion = "region"
|
||||
tests := []struct {
|
||||
description string
|
||||
state Model
|
||||
input *postgresflex.GetInstanceResponse
|
||||
storage *storageModel
|
||||
encryption *encryptionModel
|
||||
network *networkModel
|
||||
region string
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values does exactly mean what?",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Replicas: types.Int64Value(1),
|
||||
},
|
||||
&postgresflex.GetInstanceResponse{
|
||||
FlavorId: utils.Ptr("flavor_id"),
|
||||
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(1))),
|
||||
},
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{
|
||||
ACL: types.ListValueMust(
|
||||
types.StringType, []attr.Value{
|
||||
types.StringValue("0.0.0.0/0"),
|
||||
},
|
||||
),
|
||||
},
|
||||
testRegion,
|
||||
Model{
|
||||
Id: types.StringValue("pid,region,iid"),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringNull(),
|
||||
FlavorId: types.StringValue("flavor_id"),
|
||||
//ACL: types.ListNull(types.StringType),
|
||||
BackupSchedule: types.StringNull(),
|
||||
Replicas: types.Int64Value(1),
|
||||
Encryption: types.ObjectValueMust(
|
||||
encryptionTypes, map[string]attr.Value{
|
||||
"keyring_id": types.StringNull(),
|
||||
"key_id": types.StringNull(),
|
||||
"key_version": types.StringNull(),
|
||||
"service_account": types.StringNull(),
|
||||
},
|
||||
),
|
||||
Storage: types.ObjectValueMust(
|
||||
storageTypes, map[string]attr.Value{
|
||||
"class": types.StringNull(),
|
||||
"size": types.Int64Null(),
|
||||
},
|
||||
),
|
||||
Network: types.ObjectValueMust(
|
||||
networkTypes, map[string]attr.Value{
|
||||
"acl": types.ListValueMust(
|
||||
types.StringType, []attr.Value{
|
||||
types.StringValue("0.0.0.0/0"),
|
||||
},
|
||||
),
|
||||
"access_scope": types.StringNull(),
|
||||
"instance_address": types.StringNull(),
|
||||
"router_address": types.StringNull(),
|
||||
},
|
||||
),
|
||||
Version: types.StringNull(),
|
||||
Region: types.StringValue(testRegion),
|
||||
},
|
||||
true,
|
||||
},
|
||||
// {
|
||||
// "acl_unordered",
|
||||
// Model{
|
||||
// InstanceId: types.StringValue("iid"),
|
||||
// ProjectId: types.StringValue("pid"),
|
||||
// // ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
// // types.StringValue("ip2"),
|
||||
// // types.StringValue(""),
|
||||
// // types.StringValue("ip1"),
|
||||
// // }),
|
||||
// },
|
||||
// &postgresflex.GetInstanceResponse{
|
||||
// // Acl: &[]string{
|
||||
// // "",
|
||||
// // "ip1",
|
||||
// // "ip2",
|
||||
// // },
|
||||
// BackupSchedule: utils.Ptr("schedule"),
|
||||
// FlavorId: nil,
|
||||
// Id: utils.Ptr("iid"),
|
||||
// Name: utils.Ptr("name"),
|
||||
// Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
|
||||
// Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
|
||||
// Storage: nil,
|
||||
// Version: utils.Ptr("version"),
|
||||
// },
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(12),
|
||||
// RAM: types.Int64Value(34),
|
||||
// },
|
||||
// &storageModel{
|
||||
// Class: types.StringValue("class"),
|
||||
// Size: types.Int64Value(78),
|
||||
// },
|
||||
// &encryptionModel{},
|
||||
// &networkModel{},
|
||||
// testRegion,
|
||||
// Model{
|
||||
// Id: types.StringValue("pid,region,iid"),
|
||||
// InstanceId: types.StringValue("iid"),
|
||||
// ProjectId: types.StringValue("pid"),
|
||||
// Name: types.StringValue("name"),
|
||||
// // ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
// // types.StringValue("ip2"),
|
||||
// // types.StringValue(""),
|
||||
// // types.StringValue("ip1"),
|
||||
// // }),
|
||||
// BackupSchedule: types.StringValue("schedule"),
|
||||
// Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
||||
// "id": types.StringNull(),
|
||||
// "description": types.StringNull(),
|
||||
// "cpu": types.Int64Value(12),
|
||||
// "ram": types.Int64Value(34),
|
||||
// }),
|
||||
// Replicas: types.Int64Value(56),
|
||||
// Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||
// "class": types.StringValue("class"),
|
||||
// "size": types.Int64Value(78),
|
||||
// }),
|
||||
// Version: types.StringValue("version"),
|
||||
// Region: types.StringValue(testRegion),
|
||||
// },
|
||||
// true,
|
||||
// },
|
||||
{
|
||||
"nil_response",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
nil,
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
testRegion,
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&postgresflex.GetInstanceResponse{},
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
testRegion,
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.description, func(t *testing.T) {
|
||||
err := mapFields(
|
||||
context.Background(),
|
||||
tt.input,
|
||||
&tt.state,
|
||||
tt.storage,
|
||||
tt.encryption,
|
||||
tt.network,
|
||||
tt.region,
|
||||
)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(tt.expected, tt.state)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
inputAcl []string
|
||||
inputStorage *storageModel
|
||||
inputEncryption *encryptionModel
|
||||
inputNetwork *networkModel
|
||||
expected *postgresflex.CreateInstanceRequestPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
&Model{
|
||||
Replicas: types.Int64Value(1),
|
||||
},
|
||||
[]string{},
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{
|
||||
ACL: types.ListValueMust(
|
||||
types.StringType, []attr.Value{
|
||||
types.StringValue("0.0.0.0/0"),
|
||||
},
|
||||
),
|
||||
},
|
||||
&postgresflex.CreateInstanceRequestPayload{
|
||||
Acl: &[]string{"0.0.0.0/0"},
|
||||
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}),
|
||||
Encryption: &postgresflex.InstanceEncryption{},
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
Acl: &[]string{"0.0.0.0/0"},
|
||||
},
|
||||
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(1))),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
[]string{},
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_acl",
|
||||
&Model{},
|
||||
nil,
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_flavor",
|
||||
&Model{},
|
||||
[]string{},
|
||||
&storageModel{},
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_storage",
|
||||
&Model{},
|
||||
[]string{},
|
||||
nil,
|
||||
&encryptionModel{},
|
||||
&networkModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.description, func(t *testing.T) {
|
||||
output, err := toCreatePayload(tt.input, tt.inputStorage, tt.inputEncryption, tt.inputNetwork)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(tt.expected, output)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestToUpdatePayload(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// description string
|
||||
// input *Model
|
||||
// inputAcl []string
|
||||
// inputFlavor *flavorModel
|
||||
// inputStorage *storageModel
|
||||
// expected *postgresflex.PartialUpdateInstancePayload
|
||||
// isValid bool
|
||||
// }{
|
||||
// {
|
||||
// "default_values",
|
||||
// &Model{},
|
||||
// []string{},
|
||||
// &flavorModel{},
|
||||
// &storageModel{},
|
||||
// &postgresflex.PartialUpdateInstancePayload{
|
||||
// Acl: &postgresflex.ACL{
|
||||
// Items: &[]string{},
|
||||
// },
|
||||
// Storage: &postgresflex.Storage{},
|
||||
// },
|
||||
// true,
|
||||
// },
|
||||
// {
|
||||
// "simple_values",
|
||||
// &Model{
|
||||
// BackupSchedule: types.StringValue("schedule"),
|
||||
// Name: types.StringValue("name"),
|
||||
// Replicas: types.Int64Value(12),
|
||||
// Version: types.StringValue("version"),
|
||||
// },
|
||||
// []string{
|
||||
// "ip_1",
|
||||
// "ip_2",
|
||||
// },
|
||||
// &flavorModel{
|
||||
// Id: types.StringValue("flavor_id"),
|
||||
// },
|
||||
// &storageModel{
|
||||
// Class: types.StringValue("class"),
|
||||
// Size: types.Int64Value(34),
|
||||
// },
|
||||
// &postgresflex.PartialUpdateInstancePayload{
|
||||
// Acl: &postgresflex.ACL{
|
||||
// Items: &[]string{
|
||||
// "ip_1",
|
||||
// "ip_2",
|
||||
// },
|
||||
// },
|
||||
// BackupSchedule: utils.Ptr("schedule"),
|
||||
// FlavorId: utils.Ptr("flavor_id"),
|
||||
// Name: utils.Ptr("name"),
|
||||
// Replicas: utils.Ptr(int64(12)),
|
||||
// Storage: &postgresflex.Storage{
|
||||
// Class: utils.Ptr("class"),
|
||||
// Size: utils.Ptr(int64(34)),
|
||||
// },
|
||||
// Version: utils.Ptr("version"),
|
||||
// },
|
||||
// true,
|
||||
// },
|
||||
// {
|
||||
// "null_fields_and_int_conversions",
|
||||
// &Model{
|
||||
// BackupSchedule: types.StringNull(),
|
||||
// Name: types.StringNull(),
|
||||
// Replicas: types.Int64Value(2123456789),
|
||||
// Version: types.StringNull(),
|
||||
// },
|
||||
// []string{
|
||||
// "",
|
||||
// },
|
||||
// &flavorModel{
|
||||
// Id: types.StringNull(),
|
||||
// },
|
||||
// &storageModel{
|
||||
// Class: types.StringNull(),
|
||||
// Size: types.Int64Null(),
|
||||
// },
|
||||
// &postgresflex.PartialUpdateInstancePayload{
|
||||
// Acl: &postgresflex.ACL{
|
||||
// Items: &[]string{
|
||||
// "",
|
||||
// },
|
||||
// },
|
||||
// BackupSchedule: nil,
|
||||
// FlavorId: nil,
|
||||
// Name: nil,
|
||||
// Replicas: utils.Ptr(int64(2123456789)),
|
||||
// Storage: &postgresflex.Storage{
|
||||
// Class: nil,
|
||||
// Size: nil,
|
||||
// },
|
||||
// Version: nil,
|
||||
// },
|
||||
// true,
|
||||
// },
|
||||
// {
|
||||
// "nil_model",
|
||||
// nil,
|
||||
// []string{},
|
||||
// &flavorModel{},
|
||||
// &storageModel{},
|
||||
// nil,
|
||||
// false,
|
||||
// },
|
||||
// {
|
||||
// "nil_acl",
|
||||
// &Model{},
|
||||
// nil,
|
||||
// &flavorModel{},
|
||||
// &storageModel{},
|
||||
// nil,
|
||||
// false,
|
||||
// },
|
||||
// {
|
||||
// "nil_flavor",
|
||||
// &Model{},
|
||||
// []string{},
|
||||
// nil,
|
||||
// &storageModel{},
|
||||
// nil,
|
||||
// false,
|
||||
// },
|
||||
// {
|
||||
// "nil_storage",
|
||||
// &Model{},
|
||||
// []string{},
|
||||
// &flavorModel{},
|
||||
// nil,
|
||||
// nil,
|
||||
// false,
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.description, func(t *testing.T) {
|
||||
// output, err := toUpdatePayload(tt.input, tt.inputAcl, tt.inputFlavor, tt.inputStorage)
|
||||
// if !tt.isValid && err == nil {
|
||||
// t.Fatalf("Should have failed")
|
||||
// }
|
||||
// if tt.isValid && err != nil {
|
||||
// t.Fatalf("Should not have failed: %v", err)
|
||||
// }
|
||||
// if tt.isValid {
|
||||
// diff := cmp.Diff(output, tt.expected)
|
||||
// if diff != "" {
|
||||
// t.Fatalf("Data does not match: %s", diff)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func TestLoadFlavorId(t *testing.T) {
|
||||
// tests := []struct {
|
||||
// description string
|
||||
// inputFlavor *flavorModel
|
||||
// mockedResp *postgresflex.ListFlavorsResponse
|
||||
// expected *flavorModel
|
||||
// getFlavorsFails bool
|
||||
// isValid bool
|
||||
// }{
|
||||
// {
|
||||
// "ok_flavor",
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// &postgresflex.ListFlavorsResponse{
|
||||
// Flavors: &[]postgresflex.Flavor{
|
||||
// {
|
||||
// Id: utils.Ptr("fid-1"),
|
||||
// Cpu: utils.Ptr(int64(2)),
|
||||
// Description: utils.Ptr("description"),
|
||||
// Ram: utils.Ptr(int64(8)),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// &flavorModel{
|
||||
// Id: types.StringValue("fid-1"),
|
||||
// Description: types.StringValue("description"),
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// false,
|
||||
// true,
|
||||
// },
|
||||
// {
|
||||
// "ok_flavor_2",
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// &postgresflex.ListFlavorsResponse{
|
||||
// Flavors: &[]postgresflex.Flavor{
|
||||
// {
|
||||
// Id: utils.Ptr("fid-1"),
|
||||
// Cpu: utils.Ptr(int64(2)),
|
||||
// Description: utils.Ptr("description"),
|
||||
// Ram: utils.Ptr(int64(8)),
|
||||
// },
|
||||
// {
|
||||
// Id: utils.Ptr("fid-2"),
|
||||
// Cpu: utils.Ptr(int64(1)),
|
||||
// Description: utils.Ptr("description"),
|
||||
// Ram: utils.Ptr(int64(4)),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// &flavorModel{
|
||||
// Id: types.StringValue("fid-1"),
|
||||
// Description: types.StringValue("description"),
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// false,
|
||||
// true,
|
||||
// },
|
||||
// {
|
||||
// "no_matching_flavor",
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// &postgresflex.ListFlavorsResponse{
|
||||
// Flavors: &[]postgresflex.Flavor{
|
||||
// {
|
||||
// Id: utils.Ptr("fid-1"),
|
||||
// Cpu: utils.Ptr(int64(1)),
|
||||
// Description: utils.Ptr("description"),
|
||||
// Ram: utils.Ptr(int64(8)),
|
||||
// },
|
||||
// {
|
||||
// Id: utils.Ptr("fid-2"),
|
||||
// Cpu: utils.Ptr(int64(1)),
|
||||
// Description: utils.Ptr("description"),
|
||||
// Ram: utils.Ptr(int64(4)),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// false,
|
||||
// false,
|
||||
// },
|
||||
// {
|
||||
// "nil_response",
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// &postgresflex.ListFlavorsResponse{},
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// false,
|
||||
// false,
|
||||
// },
|
||||
// {
|
||||
// "error_response",
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// &postgresflex.ListFlavorsResponse{},
|
||||
// &flavorModel{
|
||||
// CPU: types.Int64Value(2),
|
||||
// RAM: types.Int64Value(8),
|
||||
// },
|
||||
// true,
|
||||
// false,
|
||||
// },
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.description, func(t *testing.T) {
|
||||
// client := &postgresFlexClientMocked{
|
||||
// returnError: tt.getFlavorsFails,
|
||||
// getFlavorsResp: tt.mockedResp,
|
||||
// }
|
||||
// model := &Model{
|
||||
// ProjectId: types.StringValue("pid"),
|
||||
// }
|
||||
// flavorModel := &flavorModel{
|
||||
// CPU: tt.inputFlavor.CPU,
|
||||
// RAM: tt.inputFlavor.RAM,
|
||||
// }
|
||||
// err := loadFlavorId(context.Background(), client, model, flavorModel)
|
||||
// if !tt.isValid && err == nil {
|
||||
// t.Fatalf("Should have failed")
|
||||
// }
|
||||
// if tt.isValid && err != nil {
|
||||
// t.Fatalf("Should not have failed: %v", err)
|
||||
// }
|
||||
// if tt.isValid {
|
||||
// diff := cmp.Diff(flavorModel, tt.expected)
|
||||
// if diff != "" {
|
||||
// t.Fatalf("Data does not match: %s", diff)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestNewInstanceResource(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -639,12 +31,10 @@ func TestNewInstanceResource(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.name, func(t *testing.T) {
|
||||
if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
|
||||
}
|
||||
},
|
||||
)
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,33 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
// The fwresource import alias is so there is no collision
|
||||
// with the more typical acceptance testing import:
|
||||
// "github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
)
|
||||
|
||||
func TestInstanceResourceSchema(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
schemaRequest := fwresource.SchemaRequest{}
|
||||
schemaResponse := &fwresource.SchemaResponse{}
|
||||
|
||||
// Instantiate the resource.Resource and call its Schema method
|
||||
NewInstanceResource().Schema(ctx, schemaRequest, schemaResponse)
|
||||
|
||||
if schemaResponse.Diagnostics.HasError() {
|
||||
t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
|
||||
}
|
||||
|
||||
// Validate the schema
|
||||
diagnostics := schemaResponse.Schema.ValidateImplementation(ctx)
|
||||
|
||||
if diagnostics.HasError() {
|
||||
t.Fatalf("Schema validation diagnostics: %+v", diagnostics)
|
||||
}
|
||||
}
|
||||
1
stackit/internal/services/postgresflexalpha/main.go
Normal file
1
stackit/internal/services/postgresflexalpha/main.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package postgresflexalpha
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package postgresflex_test
|
||||
package postgresflexalpha_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -20,6 +21,11 @@ import (
|
|||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed testdata/resource-complete.tf
|
||||
resourceSecurityGroupMinConfig string //nolint:unused // needs implementation
|
||||
)
|
||||
|
||||
// Instance resource data
|
||||
var instanceResource = map[string]string{
|
||||
"project_id": testutil.ProjectId,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func RolesDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"instance_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"roles": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Description: "List of all role names available in the instance",
|
||||
MarkdownDescription: "List of all role names available in the instance",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type RolesModel struct {
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Roles types.List `tfsdk:"roles"`
|
||||
}
|
||||
1
stackit/internal/services/postgresflexalpha/testdata/resource-complete.tf
vendored
Normal file
1
stackit/internal/services/postgresflexalpha/testdata/resource-complete.tf
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func UserDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"connection_string": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The connection string for the user to the instance.",
|
||||
MarkdownDescription: "The connection string for the user to the instance.",
|
||||
},
|
||||
"host": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The host of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The host of the instance in which the user belongs to.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The name of the user.",
|
||||
MarkdownDescription: "The name of the user.",
|
||||
},
|
||||
"port": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The port of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The port of the instance in which the user belongs to.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"roles": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Description: "A list of user roles.",
|
||||
MarkdownDescription: "A list of user roles.",
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The current status of the user.",
|
||||
MarkdownDescription: "The current status of the user.",
|
||||
},
|
||||
"user_id": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type UserModel struct {
|
||||
ConnectionString types.String `tfsdk:"connection_string"`
|
||||
Host types.String `tfsdk:"host"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Port types.Int64 `tfsdk:"port"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Roles types.List `tfsdk:"roles"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
UserId types.Int64 `tfsdk:"user_id"`
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,105 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func UserResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"connection_string": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The connection string for the user to the instance.",
|
||||
MarkdownDescription: "The connection string for the user to the instance.",
|
||||
},
|
||||
"host": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The host of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The host of the instance in which the user belongs to.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the user.",
|
||||
MarkdownDescription: "The name of the user.",
|
||||
},
|
||||
"password": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The password for the user.",
|
||||
MarkdownDescription: "The password for the user.",
|
||||
},
|
||||
"port": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The port of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The port of the instance in which the user belongs to.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"roles": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "A list containing the user roles for the instance.",
|
||||
MarkdownDescription: "A list containing the user roles for the instance.",
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The current status of the user.",
|
||||
MarkdownDescription: "The current status of the user.",
|
||||
},
|
||||
"user_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type UserModel struct {
|
||||
ConnectionString types.String `tfsdk:"connection_string"`
|
||||
Host types.String `tfsdk:"host"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
Port types.Int64 `tfsdk:"port"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Roles types.List `tfsdk:"roles"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
UserId types.Int64 `tfsdk:"user_id"`
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/iancoleman/strcase"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Field struct {
|
||||
Name string `yaml:"name"`
|
||||
Modifiers []*string `yaml:"modifiers"`
|
||||
}
|
||||
|
||||
type Fields struct {
|
||||
Fields []*Field `yaml:"fields"`
|
||||
}
|
||||
|
||||
var validModifiers = []string{
|
||||
"UseStateForUnknown",
|
||||
"RequiresReplace",
|
||||
}
|
||||
|
||||
func ReadModifiersConfig(content []byte) (*Fields, error) {
|
||||
var fields Fields
|
||||
err := yaml.Unmarshal(content, &fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fields, nil
|
||||
}
|
||||
|
||||
func AddPlanModifiersToResourceSchema(fields *Fields, s *schema.Schema) error {
|
||||
err := validateFields(fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resAttr, err := handleAttributes("", s.Attributes, fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Attributes = resAttr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleAttributes(prefix string, attributes map[string]schema.Attribute, fields *Fields) (map[string]schema.Attribute, error) {
|
||||
fieldMap := fieldListToMap(fields)
|
||||
for attrName, attrValue := range attributes {
|
||||
attrNameSnake := strcase.ToSnake(attrName)
|
||||
if prefix != "" {
|
||||
attrNameSnake = prefix + "." + attrNameSnake
|
||||
}
|
||||
switch reflect.TypeOf(attrValue).String() {
|
||||
case "schema.BoolAttribute":
|
||||
if field, ok := fieldMap[attrNameSnake]; ok {
|
||||
res, err := handleBoolPlanModifiers(attrValue, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes[attrName] = res
|
||||
}
|
||||
case "schema.Int64Attribute":
|
||||
if field, ok := fieldMap[attrNameSnake]; ok {
|
||||
res, err := handleInt64PlanModifiers(attrValue, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes[attrName] = res
|
||||
}
|
||||
case "schema.StringAttribute":
|
||||
if field, ok := fieldMap[attrNameSnake]; ok {
|
||||
res, err := handleStringPlanModifiers(attrValue, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes[attrName] = res
|
||||
}
|
||||
case "schema.ListAttribute":
|
||||
if field, ok := fieldMap[attrNameSnake]; ok {
|
||||
res, err := handleListPlanModifiers(attrValue, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attributes[attrName] = res
|
||||
}
|
||||
case "schema.SingleNestedAttribute":
|
||||
nested, ok := attrValue.(schema.SingleNestedAttribute)
|
||||
if !ok {
|
||||
if _, ok := attrValue.(interface {
|
||||
GetAttributes() map[string]schema.Attribute
|
||||
}); ok {
|
||||
return nil, fmt.Errorf("unsupported type for single nested attribute")
|
||||
}
|
||||
}
|
||||
|
||||
res, err := handleAttributes(attrName, nested.Attributes, fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nested.Attributes = res
|
||||
attributes[attrName] = nested
|
||||
default:
|
||||
slog.Warn("type currently not supported", "type", reflect.TypeOf(attrValue).String())
|
||||
}
|
||||
}
|
||||
return attributes, nil
|
||||
}
|
||||
|
||||
func handleBoolPlanModifiers(
|
||||
attr schema.Attribute,
|
||||
fields []*string,
|
||||
) (schema.Attribute, error) {
|
||||
a, ok := attr.(schema.BoolAttribute)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("field is not a string attribute")
|
||||
}
|
||||
|
||||
for _, v := range fields {
|
||||
switch *v {
|
||||
case "RequiresReplace":
|
||||
a.PlanModifiers = append(a.PlanModifiers, boolplanmodifier.RequiresReplace())
|
||||
case "UseStateForUnknown":
|
||||
a.PlanModifiers = append(a.PlanModifiers, boolplanmodifier.UseStateForUnknown())
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func handleStringPlanModifiers(
|
||||
attr schema.Attribute,
|
||||
fields []*string,
|
||||
) (schema.Attribute, error) {
|
||||
a, ok := attr.(schema.StringAttribute)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("field is not a string attribute")
|
||||
}
|
||||
|
||||
for _, v := range fields {
|
||||
switch *v {
|
||||
case "RequiresReplace":
|
||||
a.PlanModifiers = append(a.PlanModifiers, stringplanmodifier.RequiresReplace())
|
||||
case "UseStateForUnknown":
|
||||
a.PlanModifiers = append(a.PlanModifiers, stringplanmodifier.UseStateForUnknown())
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func handleInt64PlanModifiers(
|
||||
attr schema.Attribute,
|
||||
fields []*string,
|
||||
) (schema.Attribute, error) {
|
||||
a, ok := attr.(schema.Int64Attribute)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("field is not a string attribute")
|
||||
}
|
||||
|
||||
for _, v := range fields {
|
||||
switch *v {
|
||||
case "RequiresReplace":
|
||||
a.PlanModifiers = append(a.PlanModifiers, int64planmodifier.RequiresReplace())
|
||||
case "UseStateForUnknown":
|
||||
a.PlanModifiers = append(a.PlanModifiers, int64planmodifier.UseStateForUnknown())
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func handleListPlanModifiers(
|
||||
attr schema.Attribute,
|
||||
fields []*string,
|
||||
) (schema.Attribute, error) {
|
||||
a, ok := attr.(schema.ListAttribute)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("field is not a string attribute")
|
||||
}
|
||||
|
||||
for _, v := range fields {
|
||||
switch *v {
|
||||
case "RequiresReplace":
|
||||
a.PlanModifiers = append(a.PlanModifiers, listplanmodifier.RequiresReplace())
|
||||
case "UseStateForUnknown":
|
||||
a.PlanModifiers = append(a.PlanModifiers, listplanmodifier.UseStateForUnknown())
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func validateFields(fields *Fields) error {
|
||||
if fields == nil {
|
||||
return nil
|
||||
}
|
||||
for _, field := range fields.Fields {
|
||||
for _, modifier := range field.Modifiers {
|
||||
if *modifier == "" {
|
||||
return fmt.Errorf("modifier %s is required", *modifier)
|
||||
}
|
||||
if !slices.Contains(validModifiers, *modifier) {
|
||||
return fmt.Errorf("modifier %s is invalid", *modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fieldListToMap(fields *Fields) map[string][]*string {
|
||||
res := make(map[string][]*string)
|
||||
|
||||
if fields != nil {
|
||||
for _, field := range fields.Fields {
|
||||
res[field.Name] = field.Modifiers
|
||||
}
|
||||
} else {
|
||||
slog.Warn("no fields available")
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
@ -0,0 +1,569 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func VersionsDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"versions": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"beta": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
|
||||
MarkdownDescription: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
|
||||
},
|
||||
"deprecated": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
|
||||
MarkdownDescription: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
|
||||
},
|
||||
"recommend": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Flag if the version is recommend by the STACKIT Team.",
|
||||
MarkdownDescription: "Flag if the version is recommend by the STACKIT Team.",
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The postgres version used for the instance.",
|
||||
MarkdownDescription: "The postgres version used for the instance.",
|
||||
},
|
||||
},
|
||||
CustomType: VersionsType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: VersionsValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Computed: true,
|
||||
Description: "A list containing available postgres versions.",
|
||||
MarkdownDescription: "A list containing available postgres versions.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type VersionsModel struct {
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Versions types.List `tfsdk:"versions"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = VersionsType{}
|
||||
|
||||
type VersionsType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t VersionsType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(VersionsType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t VersionsType) String() string {
|
||||
return "VersionsType"
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
betaAttribute, ok := attributes["beta"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`beta is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
betaVal, ok := betaAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
|
||||
}
|
||||
|
||||
deprecatedAttribute, ok := attributes["deprecated"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`deprecated is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
|
||||
}
|
||||
|
||||
recommendAttribute, ok := attributes["recommend"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`recommend is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
|
||||
}
|
||||
|
||||
versionAttribute, ok := attributes["version"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`version is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
versionVal, ok := versionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return VersionsValue{
|
||||
Beta: betaVal,
|
||||
Deprecated: deprecatedVal,
|
||||
Recommend: recommendVal,
|
||||
Version: versionVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewVersionsValueNull() VersionsValue {
|
||||
return VersionsValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionsValueUnknown() VersionsValue {
|
||||
return VersionsValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (VersionsValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing VersionsValue Attribute Value",
|
||||
"While creating a VersionsValue value, a missing attribute value was detected. "+
|
||||
"A VersionsValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid VersionsValue Attribute Type",
|
||||
"While creating a VersionsValue value, an invalid attribute value was detected. "+
|
||||
"A VersionsValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra VersionsValue Attribute Value",
|
||||
"While creating a VersionsValue value, an extra attribute value was detected. "+
|
||||
"A VersionsValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra VersionsValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
betaAttribute, ok := attributes["beta"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`beta is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
betaVal, ok := betaAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
|
||||
}
|
||||
|
||||
deprecatedAttribute, ok := attributes["deprecated"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`deprecated is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
|
||||
}
|
||||
|
||||
recommendAttribute, ok := attributes["recommend"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`recommend is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
|
||||
}
|
||||
|
||||
versionAttribute, ok := attributes["version"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`version is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
versionVal, ok := versionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
return VersionsValue{
|
||||
Beta: betaVal,
|
||||
Deprecated: deprecatedVal,
|
||||
Recommend: recommendVal,
|
||||
Version: versionVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewVersionsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) VersionsValue {
|
||||
object, diags := NewVersionsValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewVersionsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewVersionsValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewVersionsValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewVersionsValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewVersionsValueMust(VersionsValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueType(ctx context.Context) attr.Value {
|
||||
return VersionsValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = VersionsValue{}
|
||||
|
||||
type VersionsValue struct {
|
||||
Beta basetypes.BoolValue `tfsdk:"beta"`
|
||||
Deprecated basetypes.StringValue `tfsdk:"deprecated"`
|
||||
Recommend basetypes.BoolValue `tfsdk:"recommend"`
|
||||
Version basetypes.StringValue `tfsdk:"version"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v VersionsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 4)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["beta"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["deprecated"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["recommend"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["version"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 4)
|
||||
|
||||
val, err = v.Beta.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["beta"] = val
|
||||
|
||||
val, err = v.Deprecated.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["deprecated"] = val
|
||||
|
||||
val, err = v.Recommend.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["recommend"] = val
|
||||
|
||||
val, err = v.Version.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["version"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v VersionsValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v VersionsValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v VersionsValue) String() string {
|
||||
return "VersionsValue"
|
||||
}
|
||||
|
||||
func (v VersionsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"beta": basetypes.BoolType{},
|
||||
"deprecated": basetypes.StringType{},
|
||||
"recommend": basetypes.BoolType{},
|
||||
"version": basetypes.StringType{},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"beta": v.Beta,
|
||||
"deprecated": v.Deprecated,
|
||||
"recommend": v.Recommend,
|
||||
"version": v.Version,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v VersionsValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(VersionsValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Beta.Equal(other.Beta) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Deprecated.Equal(other.Deprecated) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Recommend.Equal(other.Recommend) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Version.Equal(other.Version) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v VersionsValue) Type(ctx context.Context) attr.Type {
|
||||
return VersionsType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v VersionsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"beta": basetypes.BoolType{},
|
||||
"deprecated": basetypes.StringType{},
|
||||
"recommend": basetypes.BoolType{},
|
||||
"version": basetypes.StringType{},
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
|
||||
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = (*databaseDataSource)(nil)
|
||||
|
||||
func NewDatabaseDataSource() datasource.DataSource {
|
||||
return &databaseDataSource{}
|
||||
}
|
||||
|
||||
type databaseDataSource struct {
|
||||
client *sqlserverflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (d *databaseDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
|
||||
}
|
||||
|
||||
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (d *databaseDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "SQL SERVER Flex database client configured")
|
||||
}
|
||||
|
||||
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
|
||||
// Example data value setting
|
||||
// data.Id = types.StringValue("example-id")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func DatabaseDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"collation_name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
MarkdownDescription: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
},
|
||||
"compatibility_level": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "CompatibilityLevel of the Database.",
|
||||
MarkdownDescription: "CompatibilityLevel of the Database.",
|
||||
},
|
||||
"database_name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The owner of the database.",
|
||||
MarkdownDescription: "The owner of the database.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseModel struct {
|
||||
CollationName types.String `tfsdk:"collation_name"`
|
||||
CompatibilityLevel types.Int64 `tfsdk:"compatibility_level"`
|
||||
DatabaseName types.String `tfsdk:"database_name"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,217 @@
|
|||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/resources_gen"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &databaseResource{}
|
||||
_ resource.ResourceWithConfigure = &databaseResource{}
|
||||
_ resource.ResourceWithImportState = &databaseResource{}
|
||||
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
||||
)
|
||||
|
||||
func NewDatabaseResource() resource.Resource {
|
||||
return &databaseResource{}
|
||||
}
|
||||
|
||||
type databaseResource struct {
|
||||
client *sqlserverflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
|
||||
}
|
||||
|
||||
func (r *databaseResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = sqlserverflexalphaGen.DatabaseResourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *databaseResource) Configure(
|
||||
ctx context.Context,
|
||||
req resource.ConfigureRequest,
|
||||
resp *resource.ConfigureResponse,
|
||||
) {
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClientConfigOptions := []config.ConfigurationOption{
|
||||
config.WithCustomAuth(r.providerData.RoundTripper),
|
||||
utils.UserAgentConfigOption(r.providerData.Version),
|
||||
}
|
||||
if r.providerData.PostgresFlexCustomEndpoint != "" {
|
||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint))
|
||||
} else {
|
||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion()))
|
||||
}
|
||||
apiClient, err := sqlserverflexalpha.NewAPIClient(apiClientConfigOptions...)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Error configuring API client",
|
||||
fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database client configured")
|
||||
}
|
||||
|
||||
func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Create API call logic
|
||||
|
||||
// Example data value setting
|
||||
// data.DatabaseId = types.StringValue("id-from-response")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database created")
|
||||
}
|
||||
|
||||
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database read")
|
||||
}
|
||||
|
||||
func (r *databaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Update API call logic
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database updated")
|
||||
}
|
||||
|
||||
func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Delete API call logic
|
||||
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database deleted")
|
||||
}
|
||||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
func (r *databaseResource) ModifyPlan(
|
||||
ctx context.Context,
|
||||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel sqlserverflexalphaGen.DatabaseModel
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var planModel sqlserverflexalphaGen.DatabaseModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
|
||||
func (r *databaseResource) ImportState(
|
||||
ctx context.Context,
|
||||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
// Todo: Import logic
|
||||
if len(idParts) < 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf(
|
||||
"Expected import identifier with format [project_id],[region],..., 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("region"), idParts[1])...)
|
||||
// ... more ...
|
||||
|
||||
core.LogAndAddWarning(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Sqlserverflexalpha database imported with empty password",
|
||||
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
|
||||
)
|
||||
tflog.Info(ctx, "Sqlserverflexalpha database state imported")
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func DatabaseResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"collation": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
MarkdownDescription: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
},
|
||||
"collation_name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
MarkdownDescription: "The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.",
|
||||
},
|
||||
"compatibility": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "CompatibilityLevel of the Database.",
|
||||
MarkdownDescription: "CompatibilityLevel of the Database.",
|
||||
},
|
||||
"compatibility_level": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "CompatibilityLevel of the Database.",
|
||||
MarkdownDescription: "CompatibilityLevel of the Database.",
|
||||
},
|
||||
"database_name": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"owner": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The owner of the database.",
|
||||
MarkdownDescription: "The owner of the database.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseModel struct {
|
||||
Collation types.String `tfsdk:"collation"`
|
||||
CollationName types.String `tfsdk:"collation_name"`
|
||||
Compatibility types.Int64 `tfsdk:"compatibility"`
|
||||
CompatibilityLevel types.Int64 `tfsdk:"compatibility_level"`
|
||||
DatabaseName types.String `tfsdk:"database_name"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
|
|
@ -5,24 +5,25 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||
|
||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha"
|
||||
sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor/datasources_gen"
|
||||
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &flavorDataSource{}
|
||||
_ datasource.DataSource = &flavorDataSource{}
|
||||
_ datasource.DataSourceWithConfigure = &flavorDataSource{}
|
||||
)
|
||||
|
||||
type FlavorModel struct {
|
||||
|
|
@ -144,9 +145,9 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
|
|||
Computed: true,
|
||||
},
|
||||
},
|
||||
CustomType: sqlserverflex.StorageClassesType{
|
||||
CustomType: sqlserverflexalphaGen.StorageClassesType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -211,9 +212,9 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
model.MinGb = types.Int64Value(*f.MinGB)
|
||||
|
||||
if f.StorageClasses == nil {
|
||||
model.StorageClasses = types.ListNull(sqlserverflex.StorageClassesType{
|
||||
model.StorageClasses = types.ListNull(sqlserverflexalphaGen.StorageClassesType{
|
||||
ObjectType: basetypes.ObjectType{
|
||||
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
|
@ -221,8 +222,8 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
for _, sc := range *f.StorageClasses {
|
||||
scList = append(
|
||||
scList,
|
||||
sqlserverflex.NewStorageClassesValueMust(
|
||||
sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
sqlserverflexalphaGen.NewStorageClassesValueMust(
|
||||
sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
map[string]attr.Value{
|
||||
"class": types.StringValue(*sc.Class),
|
||||
"max_io_per_sec": types.Int64Value(*sc.MaxIoPerSec),
|
||||
|
|
@ -232,9 +233,9 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
|
|||
)
|
||||
}
|
||||
storageClassesList := types.ListValueMust(
|
||||
sqlserverflex.StorageClassesType{
|
||||
sqlserverflexalphaGen.StorageClassesType{
|
||||
ObjectType: basetypes.ObjectType{
|
||||
AttrTypes: sqlserverflex.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
scList,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,201 +4,62 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
)
|
||||
|
||||
type flavorsClient interface {
|
||||
GetFlavorsRequestExecute(
|
||||
type flavorsClientReader interface {
|
||||
GetFlavorsRequest(
|
||||
ctx context.Context,
|
||||
projectId, region string,
|
||||
page, size *int64,
|
||||
sort *sqlserverflex.FlavorSort,
|
||||
) (*sqlserverflex.GetFlavorsResponse, error)
|
||||
) sqlserverflexalpha.ApiGetFlavorsRequestRequest
|
||||
}
|
||||
|
||||
// func loadFlavorId(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel, storage *storageModel) error {
|
||||
// if model == nil {
|
||||
// return fmt.Errorf("nil model")
|
||||
// }
|
||||
// if flavor == nil {
|
||||
// return fmt.Errorf("nil flavor")
|
||||
// }
|
||||
// cpu := flavor.CPU.ValueInt64()
|
||||
// if cpu == 0 {
|
||||
// return fmt.Errorf("nil CPU")
|
||||
// }
|
||||
// ram := flavor.RAM.ValueInt64()
|
||||
// if ram == 0 {
|
||||
// return fmt.Errorf("nil RAM")
|
||||
// }
|
||||
//
|
||||
// nodeType := flavor.NodeType.ValueString()
|
||||
// if nodeType == "" {
|
||||
// if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
|
||||
// return fmt.Errorf("nil NodeType")
|
||||
// }
|
||||
// switch model.Replicas.ValueInt64() {
|
||||
// case 1:
|
||||
// nodeType = "Single"
|
||||
// case 3:
|
||||
// nodeType = "Replica"
|
||||
// default:
|
||||
// return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// storageClass := conversion.StringValueToPointer(storage.Class)
|
||||
// if storageClass == nil {
|
||||
// return fmt.Errorf("nil StorageClass")
|
||||
// }
|
||||
// storageSize := conversion.Int64ValueToPointer(storage.Size)
|
||||
// if storageSize == nil {
|
||||
// return fmt.Errorf("nil StorageSize")
|
||||
// }
|
||||
//
|
||||
// projectId := model.ProjectId.ValueString()
|
||||
// region := model.Region.ValueString()
|
||||
//
|
||||
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// avl := ""
|
||||
// foundFlavorCount := 0
|
||||
// var foundFlavors []string
|
||||
// for _, f := range flavorList {
|
||||
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||
// continue
|
||||
// }
|
||||
// if !strings.EqualFold(*f.NodeType, nodeType) {
|
||||
// continue
|
||||
// }
|
||||
// if *f.Cpu == cpu && *f.Memory == ram {
|
||||
// var useSc *sqlserverflex.FlavorStorageClassesStorageClass
|
||||
// for _, sc := range *f.StorageClasses {
|
||||
// if *sc.Class != *storageClass {
|
||||
// continue
|
||||
// }
|
||||
// if *storageSize < *f.MinGB || *storageSize > *f.MaxGB {
|
||||
// return fmt.Errorf("storage size %d out of bounds (min: %d - max: %d)", *storageSize, *f.MinGB, *f.MaxGB)
|
||||
// }
|
||||
// useSc = &sc
|
||||
// }
|
||||
// if useSc == nil {
|
||||
// return fmt.Errorf("no storage class found for %s", *storageClass)
|
||||
// }
|
||||
//
|
||||
// flavor.Id = types.StringValue(*f.Id)
|
||||
// flavor.Description = types.StringValue(*f.Description)
|
||||
// foundFlavors = append(foundFlavors, fmt.Sprintf("%s (%d/%d - %s)", *f.Id, *f.Cpu, *f.Memory, *f.NodeType))
|
||||
// foundFlavorCount++
|
||||
// }
|
||||
// for _, cls := range *f.StorageClasses {
|
||||
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM, storage %s (min: %d - max: %d)", avl, *f.Cpu, *f.Memory, *cls.Class, *f.MinGB, *f.MaxGB)
|
||||
// }
|
||||
// }
|
||||
// if foundFlavorCount > 1 {
|
||||
// return fmt.Errorf(
|
||||
// "number of flavors returned: %d\nmultiple flavors found: %d flavors\n %s",
|
||||
// len(flavorList),
|
||||
// foundFlavorCount,
|
||||
// strings.Join(foundFlavors, "\n "),
|
||||
// )
|
||||
// }
|
||||
// if flavor.Id.ValueString() == "" {
|
||||
// return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
|
||||
func getAllFlavors(ctx context.Context, client flavorsClient, projectId, region string) (
|
||||
[]sqlserverflex.ListFlavors,
|
||||
func getAllFlavors(ctx context.Context, client flavorsClientReader, projectId, region string) (
|
||||
[]sqlserverflexalpha.ListFlavors,
|
||||
error,
|
||||
) {
|
||||
if projectId == "" || region == "" {
|
||||
return nil, fmt.Errorf("listing sqlserverflex flavors: projectId and region are required")
|
||||
}
|
||||
var flavorList []sqlserverflex.ListFlavors
|
||||
|
||||
page := int64(1)
|
||||
size := int64(10)
|
||||
sort := sqlserverflex.FLAVORSORT_INDEX_ASC
|
||||
counter := 0
|
||||
for {
|
||||
res, err := client.GetFlavorsRequestExecute(ctx, projectId, region, &page, &size, &sort)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing sqlserverflex flavors: %w", err)
|
||||
}
|
||||
if res.Flavors == nil {
|
||||
return nil, fmt.Errorf("finding flavors for project %s", projectId)
|
||||
}
|
||||
pagination := res.GetPagination()
|
||||
flavors := res.GetFlavors()
|
||||
flavorList = append(flavorList, flavors...)
|
||||
|
||||
if *pagination.TotalRows < int64(len(flavorList)) {
|
||||
return nil, fmt.Errorf("total rows is smaller than current accumulated list - that should not happen")
|
||||
}
|
||||
if *pagination.TotalRows == int64(len(flavorList)) {
|
||||
break
|
||||
}
|
||||
page++
|
||||
|
||||
if page > *pagination.TotalPages {
|
||||
break
|
||||
}
|
||||
|
||||
// implement a breakpoint
|
||||
counter++
|
||||
if counter > 1000 {
|
||||
panic("too many pagination results")
|
||||
}
|
||||
getAllFilter := func(_ sqlserverflexalpha.ListFlavors) bool { return true }
|
||||
flavorList, err := getFlavorsByFilter(ctx, client, projectId, region, getAllFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return flavorList, nil
|
||||
}
|
||||
|
||||
// func getFlavorModelById(ctx context.Context, client flavorsClient, model *Model, flavor *flavorModel) error {
|
||||
// if model == nil {
|
||||
// return fmt.Errorf("nil model")
|
||||
// }
|
||||
// if flavor == nil {
|
||||
// return fmt.Errorf("nil flavor")
|
||||
// }
|
||||
// id := conversion.StringValueToPointer(flavor.Id)
|
||||
// if id == nil {
|
||||
// return fmt.Errorf("nil flavor ID")
|
||||
// }
|
||||
//
|
||||
// flavor.Id = types.StringValue("")
|
||||
//
|
||||
// projectId := model.ProjectId.ValueString()
|
||||
// region := model.Region.ValueString()
|
||||
//
|
||||
// flavorList, err := getAllFlavors(ctx, client, projectId, region)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// avl := ""
|
||||
// for _, f := range flavorList {
|
||||
// if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||
// continue
|
||||
// }
|
||||
// if *f.Id == *id {
|
||||
// flavor.Id = types.StringValue(*f.Id)
|
||||
// flavor.Description = types.StringValue(*f.Description)
|
||||
// flavor.CPU = types.Int64Value(*f.Cpu)
|
||||
// flavor.RAM = types.Int64Value(*f.Memory)
|
||||
// flavor.NodeType = types.StringValue(*f.NodeType)
|
||||
// break
|
||||
// }
|
||||
// avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||
// }
|
||||
// if flavor.Id.ValueString() == "" {
|
||||
// return fmt.Errorf("couldn't find flavor, available specs are: %s", avl)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
// getFlavorsByFilter is a helper function to retrieve flavors using a filtern function.
|
||||
// Hint: The API does not have a GetFlavors endpoint, only ListFlavors
|
||||
func getFlavorsByFilter(
|
||||
ctx context.Context,
|
||||
client flavorsClientReader,
|
||||
projectId, region string,
|
||||
filter func(db sqlserverflexalpha.ListFlavors) bool,
|
||||
) ([]sqlserverflexalpha.ListFlavors, error) {
|
||||
if projectId == "" || region == "" {
|
||||
return nil, fmt.Errorf("listing sqlserverflexalpha flavors: projectId and region are required")
|
||||
}
|
||||
|
||||
const pageSize = 25
|
||||
|
||||
var result = make([]sqlserverflexalpha.ListFlavors, 0)
|
||||
|
||||
for page := int64(1); ; page++ {
|
||||
res, err := client.GetFlavorsRequest(ctx, projectId, region).
|
||||
Page(page).Size(pageSize).Sort(sqlserverflexalpha.FLAVORSORT_INDEX_ASC).Execute()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("requesting flavors list (page %d): %w", page, err)
|
||||
}
|
||||
|
||||
// If the API returns no flavors, we have reached the end of the list.
|
||||
if res.Flavors == nil || len(*res.Flavors) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, flavor := range *res.Flavors {
|
||||
if filter(flavor) {
|
||||
result = append(result, flavor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
package sqlserverFlexAlphaFlavor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
)
|
||||
|
||||
type mockRequest struct {
|
||||
executeFunc func() (*sqlserverflexalpha.GetFlavorsResponse, error)
|
||||
}
|
||||
|
||||
func (m *mockRequest) Page(_ int64) sqlserverflexalpha.ApiGetFlavorsRequestRequest { return m }
|
||||
func (m *mockRequest) Size(_ int64) sqlserverflexalpha.ApiGetFlavorsRequestRequest { return m }
|
||||
func (m *mockRequest) Sort(_ sqlserverflexalpha.FlavorSort) sqlserverflexalpha.ApiGetFlavorsRequestRequest {
|
||||
return m
|
||||
}
|
||||
func (m *mockRequest) Execute() (*sqlserverflexalpha.GetFlavorsResponse, error) {
|
||||
return m.executeFunc()
|
||||
}
|
||||
|
||||
type mockFlavorsClient struct {
|
||||
executeRequest func() sqlserverflexalpha.ApiGetFlavorsRequestRequest
|
||||
}
|
||||
|
||||
func (m *mockFlavorsClient) GetFlavorsRequest(_ context.Context, _, _ string) sqlserverflexalpha.ApiGetFlavorsRequestRequest {
|
||||
return m.executeRequest()
|
||||
}
|
||||
|
||||
var mockResp = func(page int64) (*sqlserverflexalpha.GetFlavorsResponse, error) {
|
||||
if page == 1 {
|
||||
return &sqlserverflexalpha.GetFlavorsResponse{
|
||||
Flavors: &[]sqlserverflexalpha.ListFlavors{
|
||||
{Id: utils.Ptr("flavor-1"), Description: utils.Ptr("first")},
|
||||
{Id: utils.Ptr("flavor-2"), Description: utils.Ptr("second")},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
if page == 2 {
|
||||
return &sqlserverflexalpha.GetFlavorsResponse{
|
||||
Flavors: &[]sqlserverflexalpha.ListFlavors{
|
||||
{Id: utils.Ptr("flavor-3"), Description: utils.Ptr("three")},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &sqlserverflexalpha.GetFlavorsResponse{
|
||||
Flavors: &[]sqlserverflexalpha.ListFlavors{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestGetFlavorsByFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
projectId string
|
||||
region string
|
||||
mockErr error
|
||||
filter func(sqlserverflexalpha.ListFlavors) bool
|
||||
wantCount int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
description: "Success - Get all flavors (2 pages)",
|
||||
projectId: "pid", region: "reg",
|
||||
filter: func(_ sqlserverflexalpha.ListFlavors) bool { return true },
|
||||
wantCount: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
description: "Success - Filter flavors by description",
|
||||
projectId: "pid", region: "reg",
|
||||
filter: func(f sqlserverflexalpha.ListFlavors) bool { return *f.Description == "first" },
|
||||
wantCount: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
description: "Error - Missing parameters",
|
||||
projectId: "", region: "reg",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.description, func(t *testing.T) {
|
||||
var currentPage int64
|
||||
client := &mockFlavorsClient{
|
||||
executeRequest: func() sqlserverflexalpha.ApiGetFlavorsRequestRequest {
|
||||
return &mockRequest{
|
||||
executeFunc: func() (*sqlserverflexalpha.GetFlavorsResponse, error) {
|
||||
currentPage++
|
||||
return mockResp(currentPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
actual, err := getFlavorsByFilter(context.Background(), client, tt.projectId, tt.region, tt.filter)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getFlavorsByFilter() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !tt.wantErr && len(actual) != tt.wantCount {
|
||||
t.Errorf("getFlavorsByFilter() got %d flavors, want %d", len(actual), tt.wantCount)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllFlavors(t *testing.T) {
|
||||
var currentPage int64
|
||||
client := &mockFlavorsClient{
|
||||
executeRequest: func() sqlserverflexalpha.ApiGetFlavorsRequestRequest {
|
||||
return &mockRequest{
|
||||
executeFunc: func() (*sqlserverflexalpha.GetFlavorsResponse, error) {
|
||||
currentPage++
|
||||
return mockResp(currentPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
res, err := getAllFlavors(context.Background(), client, "pid", "reg")
|
||||
if err != nil {
|
||||
t.Errorf("getAllFlavors() unexpected error: %v", err)
|
||||
}
|
||||
if len(res) != 3 {
|
||||
t.Errorf("getAllFlavors() expected 3 flavor, got %d", len(res))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package postgresFlexAlphaFlavor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha"
|
||||
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &flavorListDataSource{}
|
||||
)
|
||||
|
||||
// NewFlavorListDataSource is a helper function to simplify the provider implementation.
|
||||
func NewFlavorListDataSource() datasource.DataSource {
|
||||
return &flavorListDataSource{}
|
||||
}
|
||||
|
||||
// flavorDataSource is the data source implementation.
|
||||
type flavorListDataSource struct {
|
||||
client *postgresflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (r *flavorListDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_postgresflexalpha_flavorlist"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (r *flavorListDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := postgresflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "Postgres Flex flavors client configured")
|
||||
}
|
||||
|
||||
func (r *flavorListDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = postgresflex.FlavorDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
func (r *flavorListDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var model postgresflex.FlavorModel
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Postgres Flex flavors read")
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||
|
||||
sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavors/datasources_gen"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = (*flavorsDataSource)(nil)
|
||||
|
||||
func NewFlavorsDataSource() datasource.DataSource {
|
||||
return &flavorsDataSource{}
|
||||
}
|
||||
|
||||
type flavorsDataSource struct {
|
||||
client *sqlserverflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_flavors"
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = sqlserverflexalphaGen.FlavorsDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (d *flavorsDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "SQL SERVER Flex flavors client configured")
|
||||
}
|
||||
|
||||
func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data sqlserverflexalphaGen.FlavorsModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
|
||||
// Example data value setting
|
||||
// data.Id = types.StringValue("example-id")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,120 @@
|
|||
fields:
|
||||
- name: 'id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'instance_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'project_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
- validate.UUID
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'name'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'backup_schedule'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'encryption.kek_key_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.kek_key_version'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.kek_key_ring_id'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'encryption.service_account'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'network.access_scope'
|
||||
validators:
|
||||
- validate.NoSeparator
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'network.acl'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'network.instance_address'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'network.router_address'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'status'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'region'
|
||||
modifiers:
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'retention_days'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'edition'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'version'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'replicas'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'storage'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'storage.class'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'storage.size'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
||||
- name: 'flavor_id'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
- 'RequiresReplace'
|
||||
|
||||
- name: 'is_deletable'
|
||||
modifiers:
|
||||
- 'UseStateForUnknown'
|
||||
|
|
@ -33,7 +33,8 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha/wait"
|
||||
wait "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
)
|
||||
|
||||
|
|
@ -338,7 +339,7 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
|
|||
Description: descriptions["status"],
|
||||
},
|
||||
"encryption": schema.SingleNestedAttribute{
|
||||
Required: true,
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.Object{
|
||||
objectplanmodifier.RequiresReplace(),
|
||||
objectplanmodifier.UseStateForUnknown(),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
1
stackit/internal/services/sqlserverflexalpha/main.go
Normal file
1
stackit/internal/services/sqlserverflexalpha/main.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package sqlserverflexalpha
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package sqlserverflex_test
|
||||
package sqlserverflexalpha_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
variable "project_id" {}
|
||||
variable "name" {}
|
||||
variable "acl1" {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
variable "project_id" {}
|
||||
variable "name" {}
|
||||
variable "flavor_cpu" {}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,111 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
)
|
||||
|
||||
func UserResourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"default_database": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The default database for a user of the instance.",
|
||||
MarkdownDescription: "The default database for a user of the instance.",
|
||||
},
|
||||
"host": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The host of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The host of the instance in which the user belongs to.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the instance.",
|
||||
MarkdownDescription: "The ID of the instance.",
|
||||
},
|
||||
"password": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The password for the user.",
|
||||
MarkdownDescription: "The password for the user.",
|
||||
},
|
||||
"port": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The port of the instance in which the user belongs to.",
|
||||
MarkdownDescription: "The port of the instance in which the user belongs to.",
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"roles": schema.ListAttribute{
|
||||
ElementType: types.StringType,
|
||||
Required: true,
|
||||
Description: "A list containing the user roles for the instance.",
|
||||
MarkdownDescription: "A list containing the user roles for the instance.",
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The current status of the user.",
|
||||
MarkdownDescription: "The current status of the user.",
|
||||
},
|
||||
"uri": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The connection string for the user to the instance.",
|
||||
MarkdownDescription: "The connection string for the user to the instance.",
|
||||
},
|
||||
"user_id": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Description: "The ID of the user.",
|
||||
MarkdownDescription: "The ID of the user.",
|
||||
},
|
||||
"username": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the user.",
|
||||
MarkdownDescription: "The name of the user.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type UserModel struct {
|
||||
DefaultDatabase types.String `tfsdk:"default_database"`
|
||||
Host types.String `tfsdk:"host"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
Port types.Int64 `tfsdk:"port"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Roles types.List `tfsdk:"roles"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
Uri types.String `tfsdk:"uri"`
|
||||
UserId types.Int64 `tfsdk:"user_id"`
|
||||
Username types.String `tfsdk:"username"`
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||
"github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||
sqlserverflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||
|
||||
sqlserverflexalphaGen "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/version/datasources_gen"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = (*versionDataSource)(nil)
|
||||
_ datasource.DataSourceWithConfigure = (*versionDataSource)(nil)
|
||||
)
|
||||
|
||||
func NewVersionDataSource() datasource.DataSource {
|
||||
return &versionDataSource{}
|
||||
}
|
||||
|
||||
type versionDataSource struct {
|
||||
client *sqlserverflexalpha.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (d *versionDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_version"
|
||||
}
|
||||
|
||||
func (d *versionDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = sqlserverflexalphaGen.VersionDataSourceSchema(ctx)
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (d *versionDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
d.client = apiClient
|
||||
tflog.Info(ctx, "SQL SERVER Flex version client configured")
|
||||
}
|
||||
|
||||
func (d *versionDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data sqlserverflexalphaGen.VersionModel
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
|
||||
// Example data value setting
|
||||
// data.Id = types.StringValue("example-id")
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
|
@ -0,0 +1,569 @@
|
|||
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-go/tftypes"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
)
|
||||
|
||||
func VersionDataSourceSchema(ctx context.Context) schema.Schema {
|
||||
return schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The STACKIT project ID.",
|
||||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf(
|
||||
"eu01",
|
||||
),
|
||||
},
|
||||
},
|
||||
"versions": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"beta": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
|
||||
MarkdownDescription: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
|
||||
},
|
||||
"deprecated": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
|
||||
MarkdownDescription: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
|
||||
},
|
||||
"recommend": schema.BoolAttribute{
|
||||
Computed: true,
|
||||
Description: "Flag if the version is recommend by the STACKIT Team.",
|
||||
MarkdownDescription: "Flag if the version is recommend by the STACKIT Team.",
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The sqlserver version used for the instance.",
|
||||
MarkdownDescription: "The sqlserver version used for the instance.",
|
||||
},
|
||||
},
|
||||
CustomType: VersionsType{
|
||||
ObjectType: types.ObjectType{
|
||||
AttrTypes: VersionsValue{}.AttributeTypes(ctx),
|
||||
},
|
||||
},
|
||||
},
|
||||
Computed: true,
|
||||
Description: "A list containing available sqlserver versions.",
|
||||
MarkdownDescription: "A list containing available sqlserver versions.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type VersionModel struct {
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Versions types.List `tfsdk:"versions"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = VersionsType{}
|
||||
|
||||
type VersionsType struct {
|
||||
basetypes.ObjectType
|
||||
}
|
||||
|
||||
func (t VersionsType) Equal(o attr.Type) bool {
|
||||
other, ok := o.(VersionsType)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return t.ObjectType.Equal(other.ObjectType)
|
||||
}
|
||||
|
||||
func (t VersionsType) String() string {
|
||||
return "VersionsType"
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributes := in.Attributes()
|
||||
|
||||
betaAttribute, ok := attributes["beta"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`beta is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
betaVal, ok := betaAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
|
||||
}
|
||||
|
||||
deprecatedAttribute, ok := attributes["deprecated"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`deprecated is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
|
||||
}
|
||||
|
||||
recommendAttribute, ok := attributes["recommend"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`recommend is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
|
||||
}
|
||||
|
||||
versionAttribute, ok := attributes["version"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`version is missing from object`)
|
||||
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
versionVal, ok := versionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return VersionsValue{
|
||||
Beta: betaVal,
|
||||
Deprecated: deprecatedVal,
|
||||
Recommend: recommendVal,
|
||||
Version: versionVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewVersionsValueNull() VersionsValue {
|
||||
return VersionsValue{
|
||||
state: attr.ValueStateNull,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionsValueUnknown() VersionsValue {
|
||||
return VersionsValue{
|
||||
state: attr.ValueStateUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
func NewVersionsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (VersionsValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
|
||||
ctx := context.Background()
|
||||
|
||||
for name, attributeType := range attributeTypes {
|
||||
attribute, ok := attributes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Missing VersionsValue Attribute Value",
|
||||
"While creating a VersionsValue value, a missing attribute value was detected. "+
|
||||
"A VersionsValue must contain values for all attributes, even if null or unknown. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !attributeType.Equal(attribute.Type(ctx)) {
|
||||
diags.AddError(
|
||||
"Invalid VersionsValue Attribute Type",
|
||||
"While creating a VersionsValue value, an invalid attribute value was detected. "+
|
||||
"A VersionsValue must use a matching attribute type for the value. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
|
||||
fmt.Sprintf("VersionsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for name := range attributes {
|
||||
_, ok := attributeTypes[name]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Extra VersionsValue Attribute Value",
|
||||
"While creating a VersionsValue value, an extra attribute value was detected. "+
|
||||
"A VersionsValue must not contain values beyond the expected attribute types. "+
|
||||
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
|
||||
fmt.Sprintf("Extra VersionsValue Attribute Name: %s", name),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
betaAttribute, ok := attributes["beta"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`beta is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
betaVal, ok := betaAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
|
||||
}
|
||||
|
||||
deprecatedAttribute, ok := attributes["deprecated"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`deprecated is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
|
||||
}
|
||||
|
||||
recommendAttribute, ok := attributes["recommend"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`recommend is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
|
||||
}
|
||||
|
||||
versionAttribute, ok := attributes["version"]
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Missing",
|
||||
`version is missing from object`)
|
||||
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
versionVal, ok := versionAttribute.(basetypes.StringValue)
|
||||
|
||||
if !ok {
|
||||
diags.AddError(
|
||||
"Attribute Wrong Type",
|
||||
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
|
||||
}
|
||||
|
||||
if diags.HasError() {
|
||||
return NewVersionsValueUnknown(), diags
|
||||
}
|
||||
|
||||
return VersionsValue{
|
||||
Beta: betaVal,
|
||||
Deprecated: deprecatedVal,
|
||||
Recommend: recommendVal,
|
||||
Version: versionVal,
|
||||
state: attr.ValueStateKnown,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func NewVersionsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) VersionsValue {
|
||||
object, diags := NewVersionsValue(attributeTypes, attributes)
|
||||
|
||||
if diags.HasError() {
|
||||
// This could potentially be added to the diag package.
|
||||
diagsStrings := make([]string, 0, len(diags))
|
||||
|
||||
for _, diagnostic := range diags {
|
||||
diagsStrings = append(diagsStrings, fmt.Sprintf(
|
||||
"%s | %s | %s",
|
||||
diagnostic.Severity(),
|
||||
diagnostic.Summary(),
|
||||
diagnostic.Detail()))
|
||||
}
|
||||
|
||||
panic("NewVersionsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
|
||||
if in.Type() == nil {
|
||||
return NewVersionsValueNull(), nil
|
||||
}
|
||||
|
||||
if !in.Type().Equal(t.TerraformType(ctx)) {
|
||||
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
|
||||
}
|
||||
|
||||
if !in.IsKnown() {
|
||||
return NewVersionsValueUnknown(), nil
|
||||
}
|
||||
|
||||
if in.IsNull() {
|
||||
return NewVersionsValueNull(), nil
|
||||
}
|
||||
|
||||
attributes := map[string]attr.Value{}
|
||||
|
||||
val := map[string]tftypes.Value{}
|
||||
|
||||
err := in.As(&val)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range val {
|
||||
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attributes[k] = a
|
||||
}
|
||||
|
||||
return NewVersionsValueMust(VersionsValue{}.AttributeTypes(ctx), attributes), nil
|
||||
}
|
||||
|
||||
func (t VersionsType) ValueType(ctx context.Context) attr.Value {
|
||||
return VersionsValue{}
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectValuable = VersionsValue{}
|
||||
|
||||
type VersionsValue struct {
|
||||
Beta basetypes.BoolValue `tfsdk:"beta"`
|
||||
Deprecated basetypes.StringValue `tfsdk:"deprecated"`
|
||||
Recommend basetypes.BoolValue `tfsdk:"recommend"`
|
||||
Version basetypes.StringValue `tfsdk:"version"`
|
||||
state attr.ValueState
|
||||
}
|
||||
|
||||
func (v VersionsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
|
||||
attrTypes := make(map[string]tftypes.Type, 4)
|
||||
|
||||
var val tftypes.Value
|
||||
var err error
|
||||
|
||||
attrTypes["beta"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["deprecated"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
attrTypes["recommend"] = basetypes.BoolType{}.TerraformType(ctx)
|
||||
attrTypes["version"] = basetypes.StringType{}.TerraformType(ctx)
|
||||
|
||||
objectType := tftypes.Object{AttributeTypes: attrTypes}
|
||||
|
||||
switch v.state {
|
||||
case attr.ValueStateKnown:
|
||||
vals := make(map[string]tftypes.Value, 4)
|
||||
|
||||
val, err = v.Beta.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["beta"] = val
|
||||
|
||||
val, err = v.Deprecated.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["deprecated"] = val
|
||||
|
||||
val, err = v.Recommend.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["recommend"] = val
|
||||
|
||||
val, err = v.Version.ToTerraformValue(ctx)
|
||||
|
||||
if err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
vals["version"] = val
|
||||
|
||||
if err := tftypes.ValidateValue(objectType, vals); err != nil {
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
|
||||
}
|
||||
|
||||
return tftypes.NewValue(objectType, vals), nil
|
||||
case attr.ValueStateNull:
|
||||
return tftypes.NewValue(objectType, nil), nil
|
||||
case attr.ValueStateUnknown:
|
||||
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
|
||||
}
|
||||
}
|
||||
|
||||
func (v VersionsValue) IsNull() bool {
|
||||
return v.state == attr.ValueStateNull
|
||||
}
|
||||
|
||||
func (v VersionsValue) IsUnknown() bool {
|
||||
return v.state == attr.ValueStateUnknown
|
||||
}
|
||||
|
||||
func (v VersionsValue) String() string {
|
||||
return "VersionsValue"
|
||||
}
|
||||
|
||||
func (v VersionsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"beta": basetypes.BoolType{},
|
||||
"deprecated": basetypes.StringType{},
|
||||
"recommend": basetypes.BoolType{},
|
||||
"version": basetypes.StringType{},
|
||||
}
|
||||
|
||||
if v.IsNull() {
|
||||
return types.ObjectNull(attributeTypes), diags
|
||||
}
|
||||
|
||||
if v.IsUnknown() {
|
||||
return types.ObjectUnknown(attributeTypes), diags
|
||||
}
|
||||
|
||||
objVal, diags := types.ObjectValue(
|
||||
attributeTypes,
|
||||
map[string]attr.Value{
|
||||
"beta": v.Beta,
|
||||
"deprecated": v.Deprecated,
|
||||
"recommend": v.Recommend,
|
||||
"version": v.Version,
|
||||
})
|
||||
|
||||
return objVal, diags
|
||||
}
|
||||
|
||||
func (v VersionsValue) Equal(o attr.Value) bool {
|
||||
other, ok := o.(VersionsValue)
|
||||
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != other.state {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.state != attr.ValueStateKnown {
|
||||
return true
|
||||
}
|
||||
|
||||
if !v.Beta.Equal(other.Beta) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Deprecated.Equal(other.Deprecated) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Recommend.Equal(other.Recommend) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !v.Version.Equal(other.Version) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (v VersionsValue) Type(ctx context.Context) attr.Type {
|
||||
return VersionsType{
|
||||
basetypes.ObjectType{
|
||||
AttrTypes: v.AttributeTypes(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (v VersionsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
|
||||
return map[string]attr.Type{
|
||||
"beta": basetypes.BoolType{},
|
||||
"deprecated": basetypes.StringType{},
|
||||
"recommend": basetypes.BoolType{},
|
||||
"version": basetypes.StringType{},
|
||||
}
|
||||
}
|
||||
202
stackit/internal/wait/postgresflexalpha/wait.go
Normal file
202
stackit/internal/wait/postgresflexalpha/wait.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/wait"
|
||||
)
|
||||
|
||||
// "READY" "PENDING" "PROGRESSING" "FAILURE" "UNKNOWN" "TERMINATING"
|
||||
const (
|
||||
InstanceStateEmpty = ""
|
||||
InstanceStateProgressing = "PROGRESSING"
|
||||
InstanceStateSuccess = "READY"
|
||||
InstanceStateFailed = "FAILURE"
|
||||
InstanceStateTerminating = "TERMINATING"
|
||||
InstanceStateUnknown = "UNKNOWN"
|
||||
InstanceStatePending = "PENDING"
|
||||
)
|
||||
|
||||
// APIClientInstanceInterface Interface needed for tests
|
||||
type APIClientInstanceInterface interface {
|
||||
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (
|
||||
*postgresflex.GetInstanceResponse,
|
||||
error,
|
||||
)
|
||||
|
||||
ListUsersRequestExecute(
|
||||
ctx context.Context,
|
||||
projectId string,
|
||||
region string,
|
||||
instanceId string,
|
||||
) (*postgresflex.ListUserResponse, error)
|
||||
}
|
||||
|
||||
// APIClientUserInterface Interface needed for tests
|
||||
type APIClientUserInterface interface {
|
||||
GetUserRequestExecute(ctx context.Context, projectId, region, instanceId string, userId int64) (
|
||||
*postgresflex.GetUserResponse,
|
||||
error,
|
||||
)
|
||||
}
|
||||
|
||||
// CreateInstanceWaitHandler will wait for instance creation
|
||||
func CreateInstanceWaitHandler(
|
||||
ctx context.Context, a APIClientInstanceInterface, projectId, region,
|
||||
instanceId string,
|
||||
) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
|
||||
instanceCreated := false
|
||||
var instanceGetResponse *postgresflex.GetInstanceResponse
|
||||
maxWait := time.Minute * 45
|
||||
startTime := time.Now()
|
||||
extendedTimeout := 0
|
||||
|
||||
handler := wait.New(
|
||||
func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
|
||||
if !instanceCreated {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
tflog.Debug(ctx, "waiting for instance ready", map[string]interface{}{
|
||||
"status": *s.Status,
|
||||
})
|
||||
switch *s.Status {
|
||||
default:
|
||||
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||
case InstanceStateEmpty:
|
||||
return false, nil, nil
|
||||
case InstanceStatePending:
|
||||
return false, nil, nil
|
||||
case InstanceStateUnknown:
|
||||
return false, nil, nil
|
||||
case InstanceStateProgressing:
|
||||
if time.Since(startTime) < maxWait {
|
||||
return false, nil, nil
|
||||
}
|
||||
tflog.Warn(
|
||||
ctx,
|
||||
fmt.Sprintf(
|
||||
"Wait handler still got status %s after %v for instance: %s",
|
||||
InstanceStateProgressing,
|
||||
maxWait,
|
||||
instanceId,
|
||||
),
|
||||
)
|
||||
if extendedTimeout < 3 {
|
||||
maxWait = maxWait + time.Minute*5
|
||||
extendedTimeout = extendedTimeout + 1
|
||||
if *s.Network.AccessScope == "SNA" {
|
||||
ready := true
|
||||
if s.Network == nil || s.Network.InstanceAddress == nil {
|
||||
tflog.Warn(ctx, "Waiting for instance_address")
|
||||
ready = false
|
||||
}
|
||||
if s.Network.RouterAddress == nil {
|
||||
tflog.Warn(ctx, "Waiting for router_address")
|
||||
ready = false
|
||||
}
|
||||
if !ready {
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
if s.IsDeletable == nil {
|
||||
tflog.Warn(ctx, "Waiting for is_deletable")
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
instanceCreated = true
|
||||
instanceGetResponse = s
|
||||
case InstanceStateSuccess:
|
||||
if *s.Network.AccessScope == "SNA" {
|
||||
if s.Network == nil || s.Network.InstanceAddress == nil {
|
||||
tflog.Warn(ctx, "Waiting for instance_address")
|
||||
return false, nil, nil
|
||||
}
|
||||
if s.Network.RouterAddress == nil {
|
||||
tflog.Info(ctx, "Waiting for router_address")
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
instanceCreated = true
|
||||
instanceGetResponse = s
|
||||
case InstanceStateFailed:
|
||||
tflog.Warn(ctx, fmt.Sprintf("Wait handler got status FAILURE for instance: %s", instanceId))
|
||||
return false, nil, nil
|
||||
// API responds with FAILURE for some seconds and then the instance goes to READY
|
||||
// return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
|
||||
}
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Waiting for instance (calling list users")
|
||||
// // User operations aren't available right after an instance is deemed successful
|
||||
// // To check if they are, perform a users request
|
||||
_, err = a.ListUsersRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err == nil {
|
||||
return true, instanceGetResponse, nil
|
||||
}
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) // nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if !ok {
|
||||
return false, nil, err
|
||||
}
|
||||
if oapiErr.StatusCode < 500 {
|
||||
return true, instanceGetResponse, fmt.Errorf(
|
||||
"users request after instance creation returned %d status code",
|
||||
oapiErr.StatusCode,
|
||||
)
|
||||
}
|
||||
return false, nil, nil
|
||||
},
|
||||
)
|
||||
// Sleep before wait is set because sometimes API returns 404 right after creation request
|
||||
handler.SetTimeout(90 * time.Minute).SetSleepBeforeWait(30 * time.Second)
|
||||
return handler
|
||||
}
|
||||
|
||||
// PartialUpdateInstanceWaitHandler will wait for instance update
|
||||
func PartialUpdateInstanceWaitHandler(
|
||||
ctx context.Context, a APIClientInstanceInterface, projectId, region,
|
||||
instanceId string,
|
||||
) *wait.AsyncActionHandler[postgresflex.GetInstanceResponse] {
|
||||
handler := wait.New(
|
||||
func() (waitFinished bool, response *postgresflex.GetInstanceResponse, err error) {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch *s.Status {
|
||||
default:
|
||||
return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Status)
|
||||
case InstanceStateEmpty:
|
||||
return false, nil, nil
|
||||
case InstanceStatePending:
|
||||
return false, nil, nil
|
||||
case InstanceStateProgressing:
|
||||
return false, nil, nil
|
||||
case InstanceStateSuccess:
|
||||
return true, s, nil
|
||||
case InstanceStateTerminating:
|
||||
return false, nil, nil
|
||||
case InstanceStateUnknown:
|
||||
return false, nil, nil
|
||||
case InstanceStateFailed:
|
||||
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
|
||||
}
|
||||
},
|
||||
)
|
||||
handler.SetTimeout(45 * time.Minute).SetSleepBeforeWait(30 * time.Second)
|
||||
return handler
|
||||
}
|
||||
335
stackit/internal/wait/postgresflexalpha/wait_test.go
Normal file
335
stackit/internal/wait/postgresflexalpha/wait_test.go
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
)
|
||||
|
||||
// Used for testing instance operations
|
||||
type apiClientInstanceMocked struct {
|
||||
instanceId string
|
||||
instanceState string
|
||||
instanceNetwork postgresflex.InstanceNetwork
|
||||
instanceIsForceDeleted bool
|
||||
instanceGetFails bool
|
||||
usersGetErrorStatus int
|
||||
}
|
||||
|
||||
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(
|
||||
_ context.Context,
|
||||
_, _, _ string,
|
||||
) (*postgresflex.GetInstanceResponse, error) {
|
||||
if a.instanceGetFails {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 500,
|
||||
}
|
||||
}
|
||||
|
||||
if a.instanceIsForceDeleted {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 404,
|
||||
}
|
||||
}
|
||||
|
||||
return &postgresflex.GetInstanceResponse{
|
||||
Id: &a.instanceId,
|
||||
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(&a.instanceState),
|
||||
Network: postgresflex.GetInstanceResponseGetNetworkAttributeType(&a.instanceNetwork),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *apiClientInstanceMocked) ListUsersRequestExecute(
|
||||
_ context.Context,
|
||||
_, _, _ string,
|
||||
) (*postgresflex.ListUserResponse, error) {
|
||||
if a.usersGetErrorStatus != 0 {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: a.usersGetErrorStatus,
|
||||
}
|
||||
}
|
||||
|
||||
aux := int64(0)
|
||||
return &postgresflex.ListUserResponse{
|
||||
Pagination: &postgresflex.Pagination{
|
||||
TotalRows: &aux,
|
||||
},
|
||||
Users: &[]postgresflex.ListUser{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestCreateInstanceWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
instanceNetwork postgresflex.InstanceNetwork
|
||||
usersGetErrorStatus int
|
||||
wantErr bool
|
||||
wantRes *postgresflex.GetInstanceResponse
|
||||
}{
|
||||
{
|
||||
desc: "create_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: false,
|
||||
wantRes: &postgresflex.GetInstanceResponse{
|
||||
Id: utils.Ptr("foo-bar"),
|
||||
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateSuccess)),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "create_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "create_failed_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateEmpty,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "instance_get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "users_get_fails",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
usersGetErrorStatus: 500,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "users_get_fails_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
usersGetErrorStatus: 400,
|
||||
wantErr: true,
|
||||
wantRes: &postgresflex.GetInstanceResponse{
|
||||
Id: utils.Ptr("foo-bar"),
|
||||
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateSuccess)),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "fail when response has no instance address",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")),
|
||||
Acl: nil,
|
||||
InstanceAddress: nil,
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProgressing,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(utils.Ptr("SNA")),
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceNetwork: tt.instanceNetwork,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
usersGetErrorStatus: tt.usersGetErrorStatus,
|
||||
}
|
||||
|
||||
handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
|
||||
|
||||
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if !cmp.Equal(gotRes, tt.wantRes) {
|
||||
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateInstanceWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
instanceNetwork postgresflex.InstanceNetwork
|
||||
wantErr bool
|
||||
wantRes *postgresflex.GetInstanceResponse
|
||||
}{
|
||||
{
|
||||
desc: "update_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: false,
|
||||
wantRes: &postgresflex.GetInstanceResponse{
|
||||
Id: utils.Ptr("foo-bar"),
|
||||
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateSuccess)),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "update_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: &postgresflex.GetInstanceResponse{
|
||||
Id: utils.Ptr("foo-bar"),
|
||||
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(InstanceStateFailed)),
|
||||
Network: &postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "update_failed_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateEmpty,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProgressing,
|
||||
instanceNetwork: postgresflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.1"),
|
||||
},
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceNetwork: tt.instanceNetwork,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
}
|
||||
|
||||
handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", "", instanceId)
|
||||
|
||||
gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if !cmp.Equal(gotRes, tt.wantRes) {
|
||||
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
120
stackit/internal/wait/sqlserverflexalpha/wait.go
Normal file
120
stackit/internal/wait/sqlserverflexalpha/wait.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
InstanceStateEmpty = ""
|
||||
InstanceStateProcessing = "Progressing"
|
||||
InstanceStateUnknown = "Unknown"
|
||||
InstanceStateSuccess = "Ready"
|
||||
InstanceStateFailed = "Failed"
|
||||
)
|
||||
|
||||
// APIClientInstanceInterface Interface needed for tests
|
||||
type APIClientInstanceInterface interface {
|
||||
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
|
||||
}
|
||||
|
||||
// CreateInstanceWaitHandler will wait for instance creation
|
||||
func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch strings.ToLower(string(*s.Status)) {
|
||||
case strings.ToLower(InstanceStateSuccess):
|
||||
if s.Network.InstanceAddress == nil {
|
||||
tflog.Info(ctx, "Waiting for instance_address")
|
||||
return false, nil, nil
|
||||
}
|
||||
if s.Network.RouterAddress == nil {
|
||||
tflog.Info(ctx, "Waiting for router_address")
|
||||
return false, nil, nil
|
||||
}
|
||||
return true, s, nil
|
||||
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
||||
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
|
||||
default:
|
||||
tflog.Info(ctx, "Wait (create) received unknown status", map[string]interface{}{
|
||||
"instanceId": instanceId,
|
||||
"status": s.Status,
|
||||
})
|
||||
return false, s, nil
|
||||
}
|
||||
})
|
||||
handler.SetTimeout(45 * time.Minute)
|
||||
handler.SetSleepBeforeWait(15 * time.Second)
|
||||
return handler
|
||||
}
|
||||
|
||||
// UpdateInstanceWaitHandler will wait for instance update
|
||||
func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
switch strings.ToLower(string(*s.Status)) {
|
||||
case strings.ToLower(InstanceStateSuccess):
|
||||
return true, s, nil
|
||||
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
||||
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
|
||||
default:
|
||||
tflog.Info(ctx, "Wait (update) received unknown status", map[string]interface{}{
|
||||
"instanceId": instanceId,
|
||||
"status": s.Status,
|
||||
})
|
||||
return false, s, nil
|
||||
}
|
||||
})
|
||||
handler.SetSleepBeforeWait(15 * time.Second)
|
||||
handler.SetTimeout(45 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
|
||||
// PartialUpdateInstanceWaitHandler will wait for instance update
|
||||
func PartialUpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||
return UpdateInstanceWaitHandler(ctx, a, projectId, instanceId, region)
|
||||
}
|
||||
|
||||
// DeleteInstanceWaitHandler will wait for instance deletion
|
||||
func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[struct{}] {
|
||||
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
|
||||
_, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||
if err == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
ok := errors.As(err, &oapiErr)
|
||||
if !ok {
|
||||
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
||||
}
|
||||
if oapiErr.StatusCode != http.StatusNotFound {
|
||||
return false, nil, err
|
||||
}
|
||||
return true, nil, nil
|
||||
})
|
||||
handler.SetTimeout(15 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
260
stackit/internal/wait/sqlserverflexalpha/wait_test.go
Normal file
260
stackit/internal/wait/sqlserverflexalpha/wait_test.go
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// Copyright (c) STACKIT
|
||||
|
||||
package sqlserverflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
sqlserverflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/sqlserverflexalpha"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
)
|
||||
|
||||
// Used for testing instance operations
|
||||
type apiClientInstanceMocked struct {
|
||||
instanceId string
|
||||
instanceState string
|
||||
instanceNetwork sqlserverflex.InstanceNetwork
|
||||
instanceIsDeleted bool
|
||||
instanceGetFails bool
|
||||
}
|
||||
|
||||
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(_ context.Context, _, _, _ string) (*sqlserverflex.GetInstanceResponse, error) {
|
||||
if a.instanceGetFails {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 500,
|
||||
}
|
||||
}
|
||||
|
||||
if a.instanceIsDeleted {
|
||||
return nil, &oapierror.GenericOpenAPIError{
|
||||
StatusCode: 404,
|
||||
}
|
||||
}
|
||||
|
||||
return &sqlserverflex.GetInstanceResponse{
|
||||
Id: &a.instanceId,
|
||||
Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(&a.instanceState),
|
||||
Network: &a.instanceNetwork,
|
||||
}, nil
|
||||
}
|
||||
func TestCreateInstanceWaitHandler(t *testing.T) {
|
||||
t.Skip("skipping - needs refactoring")
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
instanceNetwork sqlserverflex.InstanceNetwork
|
||||
usersGetErrorStatus int
|
||||
wantErr bool
|
||||
wantRes *sqlserverflex.GetInstanceResponse
|
||||
}{
|
||||
{
|
||||
desc: "create_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
instanceNetwork: sqlserverflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.2"),
|
||||
},
|
||||
wantErr: false,
|
||||
wantRes: &sqlserverflex.GetInstanceResponse{
|
||||
BackupSchedule: nil,
|
||||
Edition: nil,
|
||||
Encryption: nil,
|
||||
FlavorId: nil,
|
||||
Id: nil,
|
||||
IsDeletable: nil,
|
||||
Name: nil,
|
||||
Network: &sqlserverflex.InstanceNetwork{
|
||||
AccessScope: nil,
|
||||
Acl: nil,
|
||||
InstanceAddress: utils.Ptr("10.0.0.1"),
|
||||
RouterAddress: utils.Ptr("10.0.0.2"),
|
||||
},
|
||||
Replicas: nil,
|
||||
RetentionDays: nil,
|
||||
Status: nil,
|
||||
Storage: nil,
|
||||
Version: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "create_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "create_failed_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateEmpty,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "instance_get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProcessing,
|
||||
wantErr: true,
|
||||
wantRes: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
}
|
||||
|
||||
handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
|
||||
|
||||
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if !cmp.Equal(gotRes, tt.wantRes) {
|
||||
t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateInstanceWaitHandler(t *testing.T) {
|
||||
t.Skip("skipping - needs refactoring")
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
wantErr bool
|
||||
wantResp bool
|
||||
}{
|
||||
{
|
||||
desc: "update_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
wantErr: false,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "update_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "update_failed_2",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateEmpty,
|
||||
wantErr: true,
|
||||
wantResp: true,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
wantResp: false,
|
||||
},
|
||||
{
|
||||
desc: "timeout",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateProcessing,
|
||||
wantErr: true,
|
||||
wantResp: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
}
|
||||
|
||||
var wantRes *sqlserverflex.GetInstanceResponse
|
||||
if tt.wantResp {
|
||||
wantRes = &sqlserverflex.GetInstanceResponse{
|
||||
Id: &instanceId,
|
||||
Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(tt.instanceState)),
|
||||
}
|
||||
}
|
||||
|
||||
handler := UpdateInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
|
||||
|
||||
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !cmp.Equal(gotRes, wantRes) {
|
||||
t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteInstanceWaitHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
instanceGetFails bool
|
||||
instanceState string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "delete_succeeded",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateSuccess,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
desc: "delete_failed",
|
||||
instanceGetFails: false,
|
||||
instanceState: InstanceStateFailed,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "get_fails",
|
||||
instanceGetFails: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
instanceId := "foo-bar"
|
||||
|
||||
apiClient := &apiClientInstanceMocked{
|
||||
instanceGetFails: tt.instanceGetFails,
|
||||
instanceIsDeleted: tt.instanceState == InstanceStateSuccess,
|
||||
instanceId: instanceId,
|
||||
instanceState: tt.instanceState,
|
||||
}
|
||||
|
||||
handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
|
||||
|
||||
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue