fix: fix sqlserverflexalpha (#61)
All checks were successful
Publish / Check GoReleaser config (push) Successful in 5s
Publish / Publish provider (push) Successful in 12m55s

## 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: #61
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:
Marcel_Henselin 2026-02-13 16:23:42 +00:00 committed by Marcel_Henselin
parent d90236b02e
commit 55a0917a86
Signed by: tf-provider.git.onstackit.cloud
GPG key ID: 6D7E8A1ED8955A9C
25 changed files with 1973 additions and 2504 deletions

1
go.mod
View file

@ -18,7 +18,6 @@ require (
github.com/spf13/cobra v1.10.2 github.com/spf13/cobra v1.10.2
github.com/stackitcloud/stackit-sdk-go/core v0.21.0 github.com/stackitcloud/stackit-sdk-go/core v0.21.0
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.1
github.com/teambition/rrule-go v1.8.2 github.com/teambition/rrule-go v1.8.2
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

2
go.sum
View file

@ -174,8 +174,6 @@ github.com/stackitcloud/stackit-sdk-go/core v0.21.0 h1:QXZqiaO7U/4IpTkJfzt4dt6Qx
github.com/stackitcloud/stackit-sdk-go/core v0.21.0/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ= github.com/stackitcloud/stackit-sdk-go/core v0.21.0/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ=
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha h1:ugpMOMUZGB0yXsWcfe97F7GCdjlexbjFuGD8ZeyMSts= github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha h1:ugpMOMUZGB0yXsWcfe97F7GCdjlexbjFuGD8ZeyMSts=
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha/go.mod h1:v5VGvTxLcCdJJmblbhqYalt/MFHcElDfYoy15CMhaWs= github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.23-alpha/go.mod h1:v5VGvTxLcCdJJmblbhqYalt/MFHcElDfYoy15CMhaWs=
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.1 h1:6MJdy1xmdE+uOo/F8mR5HSldjPSHpdhwuqS3u9m2EWQ=
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.4.1/go.mod h1:XLr3ZfrT1g8ZZMm7A6RXOPBuhBkikdUN2o/+/Y+Hu+g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=

View file

@ -10,34 +10,34 @@ import (
"github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
// dataSourceModel maps the data source schema data. sqlserverflexalphaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
type dataSourceModel struct { sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
sqlserverflexalphaGen.DatabaseModel )
TerraformID types.String `tfsdk:"id"`
}
var _ datasource.DataSource = (*databaseDataSource)(nil) var _ datasource.DataSource = (*databaseDataSource)(nil)
// NewDatabaseDataSource creates a new database data source. const errorPrefix = "[sqlserverflexalpha - Database]"
func NewDatabaseDataSource() datasource.DataSource { func NewDatabaseDataSource() datasource.DataSource {
return &databaseDataSource{} return &databaseDataSource{}
} }
type dataSourceModel struct {
sqlserverflexalphaGen.DatabaseModel
TerraformId types.String `tfsdk:"id"`
}
type databaseDataSource struct { type databaseDataSource struct {
client *sqlserverflexalpha.APIClient client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
// Metadata returns the data source type name.
func (d *databaseDataSource) Metadata( func (d *databaseDataSource) Metadata(
_ context.Context, _ context.Context,
req datasource.MetadataRequest, req datasource.MetadataRequest,
@ -46,16 +46,13 @@ func (d *databaseDataSource) Metadata(
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
} }
// Schema defines the data source schema.
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
s := sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx) resp.Schema = sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
s.Attributes["id"] = schema.StringAttribute{ resp.Schema.Attributes["id"] = schema.StringAttribute{
Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`," + Computed: true,
"`database_id`\\\".\",", Description: "The terraform internal identifier.",
Computed: true, MarkdownDescription: "The terraform internal identifier.",
} }
resp.Schema = s
} }
// Configure adds the provider configured client to the data source. // Configure adds the provider configured client to the data source.
@ -70,19 +67,41 @@ func (d *databaseDataSource) Configure(
return return
} }
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics) apiClientConfigOptions := []config.ConfigurationOption{
if resp.Diagnostics.HasError() { config.WithCustomAuth(d.providerData.RoundTripper),
utils.UserAgentConfigOption(d.providerData.Version),
}
if d.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(d.providerData.SQLServerFlexCustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(d.providerData.GetRegion()),
)
}
apiClient, err := sqlserverflexalphaPkg.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 return
} }
d.client = apiClient d.client = apiClient
tflog.Info(ctx, "SQL SERVER Flex alpha database client configured") tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
} }
// Read retrieves the resource's state from the API.
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var model dataSourceModel var data dataSourceModel
diags := req.Config.Get(ctx, &model) // Read Terraform configuration data into the model
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
@ -90,22 +109,17 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
// Extract identifiers from the plan // Extract identifiers from the plan
projectId := model.ProjectId.ValueString() projectId := data.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString() region := d.providerData.GetRegionWithOverride(data.Region)
region := d.providerData.GetRegionWithOverride(model.Region) instanceId := data.InstanceId.ValueString()
databaseName := model.DatabaseName.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
ctx = tflog.SetField(ctx, "database_name", databaseName) ctx = tflog.SetField(ctx, "instance_id", instanceId)
databaseName := data.DatabaseName.ValueString()
// Fetch database from the API
databaseResp, err := d.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute() databaseResp, err := d.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
if resp.Diagnostics.HasError() {
return
}
if err != nil { if err != nil {
handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId) handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId)
resp.State.RemoveResource(ctx) resp.State.RemoveResource(ctx)
@ -113,9 +127,8 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
} }
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
// Map response body to schema and populate Computed attribute values // Map response body to schema and populate Computed attribute values
err = mapFields(databaseResp, &model, region) err = mapFields(databaseResp, &data, region)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
@ -126,14 +139,11 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
return return
} }
// Set refreshed state // Save data into Terraform state
diags = resp.State.Set(ctx, model) resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() { tflog.Info(ctx, "SQL Server Flex beta database read")
return
}
tflog.Info(ctx, "SQL Server Flex alpha database read")
} }
// handleReadError centralizes API error handling for the Read operation. // handleReadError centralizes API error handling for the Read operation.

View file

@ -41,7 +41,7 @@ func mapFields(source *sqlserverflexalpha.GetDatabaseResponse, model *dataSource
model.CompatibilityLevel = types.Int64Value(source.GetCompatibilityLevel()) model.CompatibilityLevel = types.Int64Value(source.GetCompatibilityLevel())
model.CollationName = types.StringValue(source.GetCollationName()) model.CollationName = types.StringValue(source.GetCollationName())
model.TerraformID = utils.BuildInternalTerraformId( model.TerraformId = utils.BuildInternalTerraformId(
model.ProjectId.ValueString(), model.ProjectId.ValueString(),
region, region,
model.InstanceId.ValueString(), model.InstanceId.ValueString(),
@ -80,6 +80,12 @@ func mapResourceFields(source *sqlserverflexalpha.GetDatabaseResponse, model *re
model.ProjectId = types.StringValue(model.ProjectId.ValueString()) model.ProjectId = types.StringValue(model.ProjectId.ValueString())
model.InstanceId = types.StringValue(model.InstanceId.ValueString()) model.InstanceId = types.StringValue(model.InstanceId.ValueString())
model.Compatibility = types.Int64Value(source.GetCompatibilityLevel())
model.CompatibilityLevel = types.Int64Value(source.GetCompatibilityLevel())
model.Collation = types.StringValue(source.GetCollationName()) // it does not come back from api
model.CollationName = types.StringValue(source.GetCollationName())
return nil return nil
} }

View file

@ -35,7 +35,7 @@ func TestMapFields(t *testing.T) {
Name: utils.Ptr("my-db"), Name: utils.Ptr("my-db"),
CollationName: utils.Ptr("collation"), CollationName: utils.Ptr("collation"),
CompatibilityLevel: utils.Ptr(int64(150)), CompatibilityLevel: utils.Ptr(int64(150)),
Owner: utils.Ptr("\"my-owner\""), Owner: utils.Ptr("my-owner"),
}, },
model: &dataSourceModel{ model: &dataSourceModel{
DatabaseModel: datasource.DatabaseModel{ DatabaseModel: datasource.DatabaseModel{
@ -58,7 +58,7 @@ func TestMapFields(t *testing.T) {
CompatibilityLevel: types.Int64Value(150), CompatibilityLevel: types.Int64Value(150),
CollationName: types.StringValue("collation"), CollationName: types.StringValue("collation"),
}, },
TerraformID: types.StringValue("my-project,eu01,my-instance,my-db"), TerraformId: types.StringValue("my-project,eu01,my-instance,my-db"),
}, },
}, },
}, },
@ -127,7 +127,7 @@ func TestMapResourceFields(t *testing.T) {
source: &sqlserverflexalpha.GetDatabaseResponse{ source: &sqlserverflexalpha.GetDatabaseResponse{
Id: utils.Ptr(int64(1)), Id: utils.Ptr(int64(1)),
Name: utils.Ptr("my-db"), Name: utils.Ptr("my-db"),
Owner: utils.Ptr("\"my-owner\""), Owner: utils.Ptr("my-owner"),
}, },
model: &resourceModel{ model: &resourceModel{
ProjectId: types.StringValue("my-project"), ProjectId: types.StringValue("my-project"),
@ -137,13 +137,17 @@ func TestMapResourceFields(t *testing.T) {
}, },
expected: expected{ expected: expected{
model: &resourceModel{ model: &resourceModel{
Id: types.Int64Value(1), Id: types.Int64Value(1),
Name: types.StringValue("my-db"), Name: types.StringValue("my-db"),
DatabaseName: types.StringValue("my-db"), Compatibility: types.Int64Value(0),
InstanceId: types.StringValue("my-instance"), CompatibilityLevel: types.Int64Value(0),
ProjectId: types.StringValue("my-project"), Collation: types.StringValue(""),
Region: types.StringValue("eu01"), CollationName: types.StringValue(""),
Owner: types.StringValue("my-owner"), DatabaseName: types.StringValue("my-db"),
InstanceId: types.StringValue("my-instance"),
ProjectId: types.StringValue("my-project"),
Region: types.StringValue("eu01"),
Owner: types.StringValue("my-owner"),
}, },
}, },
}, },

View file

@ -31,7 +31,7 @@ fields:
- name: 'owner' - name: 'owner'
modifiers: modifiers:
- 'RequiresReplace' - 'UseStateForUnknown'
- name: 'database_name' - name: 'database_name'
modifiers: modifiers:
@ -43,6 +43,7 @@ fields:
- name: 'compatibility' - name: 'compatibility'
modifiers: modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace' - 'RequiresReplace'
- name: 'compatibility_level' - name: 'compatibility_level'

View file

@ -6,8 +6,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
@ -19,11 +19,12 @@ import (
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
wait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/resources_gen" sqlserverflexalphaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/resources_gen"
) )
var ( var (
@ -46,9 +47,13 @@ func NewDatabaseResource() resource.Resource {
} }
// resourceModel describes the resource data model. // resourceModel describes the resource data model.
type resourceModel = sqlserverflexalphaGen.DatabaseModel type resourceModel = sqlserverflexalphaResGen.DatabaseModel
type databaseResource struct {
client *sqlserverflexalpha.APIClient
providerData core.ProviderData
}
// DatabaseResourceIdentityModel describes the resource's identity attributes.
type DatabaseResourceIdentityModel struct { type DatabaseResourceIdentityModel struct {
ProjectID types.String `tfsdk:"project_id"` ProjectID types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"` Region types.String `tfsdk:"region"`
@ -56,12 +61,11 @@ type DatabaseResourceIdentityModel struct {
DatabaseName types.String `tfsdk:"database_name"` DatabaseName types.String `tfsdk:"database_name"`
} }
type databaseResource struct { func (r *databaseResource) Metadata(
client *sqlserverflexalpha.APIClient _ context.Context,
providerData core.ProviderData req resource.MetadataRequest,
} resp *resource.MetadataResponse,
) {
func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
} }
@ -69,7 +73,7 @@ func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequ
var modifiersFileByte []byte var modifiersFileByte []byte
func (r *databaseResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { func (r *databaseResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
s := sqlserverflexalphaGen.DatabaseResourceSchema(ctx) s := sqlserverflexalphaResGen.DatabaseResourceSchema(ctx)
fields, err := utils.ReadModifiersConfig(modifiersFileByte) fields, err := utils.ReadModifiersConfig(modifiersFileByte)
if err != nil { if err != nil {
@ -124,10 +128,10 @@ func (r *databaseResource) Configure(
config.WithCustomAuth(r.providerData.RoundTripper), config.WithCustomAuth(r.providerData.RoundTripper),
utils.UserAgentConfigOption(r.providerData.Version), utils.UserAgentConfigOption(r.providerData.Version),
} }
if r.providerData.PostgresFlexCustomEndpoint != "" { if r.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append( apiClientConfigOptions = append(
apiClientConfigOptions, apiClientConfigOptions,
config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint), config.WithEndpoint(r.providerData.SQLServerFlexCustomEndpoint),
) )
} else { } else {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion())) apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion()))
@ -148,50 +152,74 @@ func (r *databaseResource) Configure(
} }
func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var model resourceModel var data resourceModel
createErr := "DB create error"
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString() projectId := data.ProjectId.ValueString()
region := model.Region.ValueString() region := data.Region.ValueString()
instanceId := model.InstanceId.ValueString() instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
// Generate API request body from model databaseName := data.Name.ValueString()
payload, err := toCreatePayload(&model) ctx = tflog.SetField(ctx, "database_name", databaseName)
payLoad := sqlserverflexalpha.CreateDatabaseRequestPayload{}
if !data.Collation.IsNull() && !data.Collation.IsUnknown() {
payLoad.Collation = data.Collation.ValueStringPointer()
}
if !data.Compatibility.IsNull() && !data.Compatibility.IsUnknown() {
payLoad.Compatibility = data.Compatibility.ValueInt64Pointer()
}
payLoad.Name = data.Name.ValueStringPointer()
payLoad.Owner = data.Owner.ValueStringPointer()
_, err := wait.WaitForUserWaitHandler(
ctx,
r.client,
projectId,
instanceId,
region,
data.Owner.ValueString(),
).
SetSleepBeforeWait(10 * time.Second).
WaitWithContext(ctx)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
&resp.Diagnostics, &resp.Diagnostics,
"Error creating database", createErr,
fmt.Sprintf("Creating API payload: %v", err), fmt.Sprintf("Calling API: %v", err),
) )
return return
} }
// Create new database
databaseResp, err := r.client.CreateDatabaseRequest( createResp, err := r.client.CreateDatabaseRequest(ctx, projectId, region, instanceId).
ctx, CreateDatabaseRequestPayload(payLoad).
projectId, Execute()
region,
instanceId,
).CreateDatabaseRequestPayload(*payload).Execute()
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating database", fmt.Sprintf("Calling API: %v", err)) core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
fmt.Sprintf("Calling API: %v", err),
)
return return
} }
ctx = core.LogResponse(ctx) if createResp == nil || createResp.Id == nil {
if databaseResp == nil || databaseResp.Id == nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
&resp.Diagnostics, &resp.Diagnostics,
@ -201,11 +229,9 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return return
} }
databaseId := *databaseResp.Id databaseId := *createResp.Id
databaseName := model.DatabaseName.String()
ctx = tflog.SetField(ctx, "database_id", databaseId) ctx = tflog.SetField(ctx, "database_id", databaseId)
ctx = tflog.SetField(ctx, "database_name", databaseName)
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
@ -221,6 +247,69 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return return
} }
// TODO: is this necessary to wait for the database-> API say 200 ?
waitResp, err := wait.CreateDatabaseWaitHandler(
ctx,
r.client,
projectId,
instanceId,
region,
databaseName,
).SetSleepBeforeWait(
30 * time.Second,
).SetTimeout(
15 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
fmt.Sprintf("Database creation waiting: %v", err),
)
return
}
if waitResp.Id == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
"Database creation waiting: returned id is nil",
)
return
}
if *waitResp.Id != databaseId {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
"Database creation waiting: returned id is different",
)
return
}
if *waitResp.Owner != data.Owner.ValueString() {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
"Database creation waiting: returned owner is different",
)
return
}
if *waitResp.Name != data.Name.ValueString() {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
createErr,
"Database creation waiting: returned name is different",
)
return
}
database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute() database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
@ -233,7 +322,7 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
} }
// Map response body to schema // Map response body to schema
err = mapResourceFields(database, &model, region) err = mapResourceFields(database, &data, region)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
@ -245,11 +334,13 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
} }
// Set state to fully populated data // Set state to fully populated data
resp.Diagnostics.Append(resp.State.Set(ctx, model)...) resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
// Save data into Terraform state
tflog.Info(ctx, "sqlserverflexalpha.Database created") tflog.Info(ctx, "sqlserverflexalpha.Database created")
} }
@ -310,7 +401,7 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
return return
} }
// Set data returned by API in identity // Save identity into Terraform state
identity := DatabaseResourceIdentityModel{ identity := DatabaseResourceIdentityModel{
ProjectID: types.StringValue(projectId), ProjectID: types.StringValue(projectId),
Region: types.StringValue(region), Region: types.StringValue(region),
@ -372,7 +463,13 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques
// Delete existing record set // Delete existing record set
err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName) err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
if err != nil { if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting database", fmt.Sprintf("Calling API: %v", err)) core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error deleting database",
fmt.Sprintf(
"Calling API: %v\nname: %s, region: %s, instanceId: %s", err, databaseName, region, instanceId))
return
} }
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
@ -388,11 +485,13 @@ func (r *databaseResource) ModifyPlan(
req resource.ModifyPlanRequest, req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse, resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform ) { // nolint:gocritic // function signature required by Terraform
var configModel resourceModel
// skip initial empty configuration to avoid follow-up errors // skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() { if req.Config.Raw.IsNull() {
return return
} }
var configModel resourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...) resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
@ -409,6 +508,23 @@ func (r *databaseResource) ModifyPlan(
return return
} }
var identityModel DatabaseResourceIdentityModel
identityModel.ProjectID = planModel.ProjectId
identityModel.Region = planModel.Region
if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
identityModel.InstanceID = planModel.InstanceId
}
if !planModel.Name.IsNull() && !planModel.Name.IsUnknown() {
identityModel.DatabaseName = planModel.Name
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityModel)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...) resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
@ -416,7 +532,7 @@ func (r *databaseResource) ModifyPlan(
} }
// ImportState imports a resource into the Terraform state on success. // ImportState imports a resource into the Terraform state on success.
// The expected import identifier format is: [project_id],[region],[instance_id],[database_id] // The expected format of the resource import identifier is: project_id,zone_id,record_set_id
func (r *databaseResource) ImportState( func (r *databaseResource) ImportState(
ctx context.Context, ctx context.Context,
req resource.ImportStateRequest, req resource.ImportStateRequest,
@ -432,36 +548,31 @@ func (r *databaseResource) ImportState(
ctx, &resp.Diagnostics, ctx, &resp.Diagnostics,
"Error importing database", "Error importing database",
fmt.Sprintf( fmt.Sprintf(
"Expected import identifier with format [project_id],[region],[instance_id],[database_name], got %q", "Expected import identifier with format: [project_id],[region],[instance_id],[database_name] Got: %q",
req.ID, req.ID,
), ),
) )
return return
} }
databaseId, err := strconv.ParseInt(idParts[3], 10, 64)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error importing database",
fmt.Sprintf("Invalid database_id format: %q. It must be a valid integer.", idParts[3]),
)
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...) 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("region"), idParts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), databaseId)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), idParts[3])...)
core.LogAndAddWarning( var identityData DatabaseResourceIdentityModel
ctx, identityData.ProjectID = types.StringValue(idParts[0])
&resp.Diagnostics, identityData.Region = types.StringValue(idParts[1])
"Sqlserverflexalpha database imported with empty password", identityData.InstanceID = types.StringValue(idParts[2])
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.", identityData.DatabaseName = types.StringValue(idParts[3])
)
tflog.Info(ctx, "Sqlserverflexalpha database state imported") resp.Diagnostics.Append(resp.Identity.Set(ctx, &identityData)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "sqlserverflexalpha database state imported")
return
} }
// If no ID is provided, attempt to read identity attributes from the import configuration // If no ID is provided, attempt to read identity attributes from the import configuration
@ -481,7 +592,7 @@ func (r *databaseResource) ImportState(
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), databaseName)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), databaseName)...)
tflog.Info(ctx, "Sqlserverflexalpha database state imported") tflog.Info(ctx, "sqlserverflexalpha database state imported")
} }
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model. // extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.

View file

@ -1,4 +1,4 @@
package sqlserverFlexAlphaFlavor package sqlserverflexalphaFlavor
import ( import (
"context" "context"
@ -10,14 +10,14 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor/datasources_gen" sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor/datasources_gen"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
) )
// Ensure the implementation satisfies the expected interfaces. // Ensure the implementation satisfies the expected interfaces.
@ -26,11 +26,6 @@ var (
_ datasource.DataSourceWithConfigure = &flavorDataSource{} _ datasource.DataSourceWithConfigure = &flavorDataSource{}
) )
// NewFlavorDataSource is a helper function to simplify the provider implementation.
func NewFlavorDataSource() datasource.DataSource {
return &flavorDataSource{}
}
type FlavorModel struct { type FlavorModel struct {
ProjectId types.String `tfsdk:"project_id"` ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"` Region types.String `tfsdk:"region"`
@ -46,39 +41,58 @@ type FlavorModel struct {
StorageClasses types.List `tfsdk:"storage_classes"` StorageClasses types.List `tfsdk:"storage_classes"`
} }
// NewFlavorDataSource is a helper function to simplify the provider implementation.
func NewFlavorDataSource() datasource.DataSource {
return &flavorDataSource{}
}
// flavorDataSource is the data source implementation. // flavorDataSource is the data source implementation.
type flavorDataSource struct { type flavorDataSource struct {
client *sqlserverflexalpha.APIClient client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
// Metadata returns the data source type name. // Metadata returns the data source type name.
func (r *flavorDataSource) Metadata( func (r *flavorDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
_ context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_flavor" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_flavor"
} }
// Configure adds the provider configured client to the data source. // Configure adds the provider configured client to the data source.
func (r *flavorDataSource) Configure( func (r *flavorDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
var ok bool var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok { if !ok {
return return
} }
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) apiClientConfigOptions := []config.ConfigurationOption{
if resp.Diagnostics.HasError() { config.WithCustomAuth(r.providerData.RoundTripper),
utils.UserAgentConfigOption(r.providerData.Version),
}
if r.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(r.providerData.SQLServerFlexCustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(r.providerData.GetRegion()),
)
}
apiClient, err := sqlserverflexalphaPkg.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 return
} }
r.client = apiClient r.client = apiClient
tflog.Info(ctx, "Postgres Flex instance client configured") tflog.Info(ctx, "SQL Server Flex instance client configured")
} }
func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
@ -86,13 +100,13 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"project_id": schema.StringAttribute{ "project_id": schema.StringAttribute{
Required: true, Required: true,
Description: "The cpu count of the instance.", Description: "The project ID of the flavor.",
MarkdownDescription: "The cpu count of the instance.", MarkdownDescription: "The project ID of the flavor.",
}, },
"region": schema.StringAttribute{ "region": schema.StringAttribute{
Required: true, Required: true,
Description: "The flavor description.", Description: "The region of the flavor.",
MarkdownDescription: "The flavor description.", MarkdownDescription: "The region of the flavor.",
}, },
"cpu": schema.Int64Attribute{ "cpu": schema.Int64Attribute{
Required: true, Required: true,
@ -109,6 +123,16 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
Description: "The memory of the instance in Gibibyte.", Description: "The memory of the instance in Gibibyte.",
MarkdownDescription: "The memory of the instance in Gibibyte.", MarkdownDescription: "The memory of the instance in Gibibyte.",
}, },
"node_type": schema.StringAttribute{
Required: true,
Description: "defines the nodeType it can be either single or HA",
MarkdownDescription: "defines the nodeType it can be either single or HA",
},
"flavor_id": schema.StringAttribute{
Computed: true,
Description: "The id of the instance flavor.",
MarkdownDescription: "The id of the instance flavor.",
},
"description": schema.StringAttribute{ "description": schema.StringAttribute{
Computed: true, Computed: true,
Description: "The flavor description.", Description: "The flavor description.",
@ -116,13 +140,8 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
}, },
"id": schema.StringAttribute{ "id": schema.StringAttribute{
Computed: true, Computed: true,
Description: "The terraform id of the instance flavor.", Description: "The id of the instance flavor.",
MarkdownDescription: "The terraform id of the instance flavor.", MarkdownDescription: "The id of the instance flavor.",
},
"flavor_id": schema.StringAttribute{
Computed: true,
Description: "The flavor id of the instance flavor.",
MarkdownDescription: "The flavor id of the instance flavor.",
}, },
"max_gb": schema.Int64Attribute{ "max_gb": schema.Int64Attribute{
Computed: true, Computed: true,
@ -134,13 +153,7 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
Description: "minimum storage which is required to order in Gigabyte.", Description: "minimum storage which is required to order in Gigabyte.",
MarkdownDescription: "minimum storage which is required to order in Gigabyte.", MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
}, },
"node_type": schema.StringAttribute{
Required: true,
Description: "defines the nodeType it can be either single or replica",
MarkdownDescription: "defines the nodeType it can be either single or replica",
},
"storage_classes": schema.ListNestedAttribute{ "storage_classes": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{ NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"class": schema.StringAttribute{ "class": schema.StringAttribute{
@ -159,8 +172,89 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
}, },
}, },
}, },
Computed: true,
Description: "maximum storage which can be ordered for the flavor in Gigabyte.",
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
}, },
}, },
//Attributes: map[string]schema.Attribute{
// "project_id": schema.StringAttribute{
// Required: true,
// Description: "The cpu count of the instance.",
// MarkdownDescription: "The cpu count of the instance.",
// },
// "region": schema.StringAttribute{
// Required: true,
// Description: "The flavor description.",
// MarkdownDescription: "The flavor description.",
// },
// "cpu": schema.Int64Attribute{
// Required: true,
// Description: "The cpu count of the instance.",
// MarkdownDescription: "The cpu count of the instance.",
// },
// "ram": schema.Int64Attribute{
// Required: true,
// Description: "The memory of the instance in Gibibyte.",
// MarkdownDescription: "The memory of the instance in Gibibyte.",
// },
// "storage_class": schema.StringAttribute{
// Required: true,
// Description: "The memory of the instance in Gibibyte.",
// MarkdownDescription: "The memory of the instance in Gibibyte.",
// },
// "description": schema.StringAttribute{
// Computed: true,
// Description: "The flavor description.",
// MarkdownDescription: "The flavor description.",
// },
// "id": schema.StringAttribute{
// Computed: true,
// Description: "The terraform id of the instance flavor.",
// MarkdownDescription: "The terraform id of the instance flavor.",
// },
// "flavor_id": schema.StringAttribute{
// Computed: true,
// Description: "The flavor id of the instance flavor.",
// MarkdownDescription: "The flavor id of the instance flavor.",
// },
// "max_gb": schema.Int64Attribute{
// Computed: true,
// Description: "maximum storage which can be ordered for the flavor in Gigabyte.",
// MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
// },
// "min_gb": schema.Int64Attribute{
// Computed: true,
// Description: "minimum storage which is required to order in Gigabyte.",
// MarkdownDescription: "minimum storage which is required to order in Gigabyte.",
// },
// "node_type": schema.StringAttribute{
// Required: true,
// Description: "defines the nodeType it can be either single or replica",
// MarkdownDescription: "defines the nodeType it can be either single or replica",
// },
// "storage_classes": schema.ListNestedAttribute{
// Computed: true,
// NestedObject: schema.NestedAttributeObject{
// Attributes: map[string]schema.Attribute{
// "class": schema.StringAttribute{
// Computed: true,
// },
// "max_io_per_sec": schema.Int64Attribute{
// Computed: true,
// },
// "max_through_in_mb": schema.Int64Attribute{
// Computed: true,
// },
// },
// CustomType: sqlserverflexalphaGen.StorageClassesType{
// ObjectType: types.ObjectType{
// AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
// },
// },
// },
// },
// },
} }
} }
@ -185,7 +279,7 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
return return
} }
var foundFlavors []sqlserverflexalpha.ListFlavors var foundFlavors []sqlserverflexalphaPkg.ListFlavors
for _, flavor := range flavors { for _, flavor := range flavors {
if model.Cpu.ValueInt64() != *flavor.Cpu { if model.Cpu.ValueInt64() != *flavor.Cpu {
continue continue
@ -220,13 +314,11 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
model.MinGb = types.Int64Value(*f.MinGB) model.MinGb = types.Int64Value(*f.MinGB)
if f.StorageClasses == nil { if f.StorageClasses == nil {
model.StorageClasses = types.ListNull( model.StorageClasses = types.ListNull(sqlserverflexalphaGen.StorageClassesType{
sqlserverflexalphaGen.StorageClassesType{ ObjectType: basetypes.ObjectType{
ObjectType: basetypes.ObjectType{ AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
},
}, },
) })
} else { } else {
var scList []attr.Value var scList []attr.Value
for _, sc := range *f.StorageClasses { for _, sc := range *f.StorageClasses {
@ -259,5 +351,5 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
tflog.Info(ctx, "Postgres Flex flavors read") tflog.Info(ctx, "SQL Server Flex flavors read")
} }

View file

@ -1,4 +1,4 @@
package sqlserverFlexAlphaFlavor package sqlserverflexalphaFlavor
import ( import (
"context" "context"

View file

@ -1,4 +1,4 @@
package sqlserverFlexAlphaFlavor package sqlserverflexalphaFlavor
import ( import (
"context" "context"

View file

@ -2,30 +2,39 @@ package sqlserverflexalpha
import ( import (
"context" "context"
"fmt"
"net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource" "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-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavors/datasources_gen" sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavors/datasources_gen"
) )
// dataSourceModel maps the data source schema data.
type dataSourceModel = sqlserverflexalphaGen.FlavorsModel
var _ datasource.DataSource = (*flavorsDataSource)(nil) var _ datasource.DataSource = (*flavorsDataSource)(nil)
// TODO: Use NewFlavorsDataSource when datasource is implemented const errorPrefix = "[sqlserverflexalpha - Flavors]"
func NewFlavorsDataSource() datasource.DataSource { func NewFlavorsDataSource() datasource.DataSource {
return &flavorsDataSource{} return &flavorsDataSource{}
} }
type dataSourceModel struct {
sqlserverflexalphaGen.FlavorsModel
TerraformId types.String `tfsdk:"id"`
}
type flavorsDataSource struct { type flavorsDataSource struct {
client *sqlserverflexalpha.APIClient client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
@ -39,6 +48,11 @@ func (d *flavorsDataSource) Metadata(
func (d *flavorsDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { func (d *flavorsDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = sqlserverflexalphaGen.FlavorsDataSourceSchema(ctx) resp.Schema = sqlserverflexalphaGen.FlavorsDataSourceSchema(ctx)
resp.Schema.Attributes["id"] = schema.StringAttribute{
Computed: true,
Description: "The terraform internal identifier.",
MarkdownDescription: "The terraform internal identifier.",
}
} }
// Configure adds the provider configured client to the data source. // Configure adds the provider configured client to the data source.
@ -53,12 +67,34 @@ func (d *flavorsDataSource) Configure(
return return
} }
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics) apiClientConfigOptions := []config.ConfigurationOption{
if resp.Diagnostics.HasError() { config.WithCustomAuth(d.providerData.RoundTripper),
utils.UserAgentConfigOption(d.providerData.Version),
}
if d.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(d.providerData.SQLServerFlexCustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(d.providerData.GetRegion()),
)
}
apiClient, err := sqlserverflexalphaPkg.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 return
} }
d.client = apiClient d.client = apiClient
tflog.Info(ctx, "SQL SERVER Flex flavors client configured") tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
} }
func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
@ -71,11 +107,50 @@ func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest
return return
} }
// Todo: Read API call logic ctx = core.InitProviderContext(ctx)
// Example data value setting projectId := data.ProjectId.ValueString()
// data.Id = types.StringValue("example-id") region := d.providerData.GetRegionWithOverride(data.Region)
// TODO: implement right identifier for flavors
flavorsId := data.Flavors
// Save data into Terraform state ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// TODO: implement needed fields
ctx = tflog.SetField(ctx, "flavors_id", flavorsId)
// TODO: refactor to correct implementation
_, err := d.client.GetFlavorsRequest(ctx, projectId, region).Execute()
if err != nil {
utils.LogError(
ctx,
&resp.Diagnostics,
err,
"Reading flavors",
fmt.Sprintf("flavors with ID %q does not exist in project %q.", flavorsId, projectId),
map[int]string{
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
},
)
resp.State.RemoveResource(ctx)
return
}
ctx = core.LogResponse(ctx)
// TODO: refactor to correct implementation of internal tf id
data.TerraformId = utils.BuildInternalTerraformId(projectId, region)
// TODO: fill remaining fields
// data.Flavors = types.Sometype(apiResponse.GetFlavors())
// data.Page = types.Sometype(apiResponse.GetPage())
// data.Pagination = types.Sometype(apiResponse.GetPagination())
// data.ProjectId = types.Sometype(apiResponse.GetProjectId())
// data.Region = types.Sometype(apiResponse.GetRegion())
// data.Size = types.Sometype(apiResponse.GetSize())
// data.Sort = types.Sometype(apiResponse.GetSort())// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, fmt.Sprintf("%s read successful", errorPrefix))
} }

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package sqlserverflexalpha package sqlserverflexalpha
import ( import (
@ -7,47 +5,40 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
sqlserverflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/datasources_gen"
sqlserverflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
sqlserverflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-log/tflog"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/datasources_gen"
) )
// dataSourceModel maps the data source schema data. var _ datasource.DataSource = (*instanceDataSource)(nil)
type dataSourceModel struct {
sqlserverflexalpha2.InstanceModel
TerraformID types.String `tfsdk:"id"`
}
// Ensure the implementation satisfies the expected interfaces. const errorPrefix = "[sqlserverflexalpha - Instance]"
var (
_ datasource.DataSource = &instanceDataSource{}
)
// NewInstanceDataSource is a helper function to simplify the provider implementation.
func NewInstanceDataSource() datasource.DataSource { func NewInstanceDataSource() datasource.DataSource {
return &instanceDataSource{} return &instanceDataSource{}
} }
// instanceDataSource is the data source implementation. // dataSourceModel maps the data source schema data.
type dataSourceModel struct {
sqlserverflexalphaGen.InstanceModel
TerraformID types.String `tfsdk:"id"`
}
type instanceDataSource struct { type instanceDataSource struct {
client *sqlserverflex.APIClient client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
// Metadata returns the data source type name. func (d *instanceDataSource) Metadata(
func (r *instanceDataSource) Metadata(
_ context.Context, _ context.Context,
req datasource.MetadataRequest, req datasource.MetadataRequest,
resp *datasource.MetadataResponse, resp *datasource.MetadataResponse,
@ -55,66 +46,80 @@ func (r *instanceDataSource) Metadata(
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_instance" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_instance"
} }
func (d *instanceDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = sqlserverflexalphaGen.InstanceDataSourceSchema(ctx)
}
// Configure adds the provider configured client to the data source. // Configure adds the provider configured client to the data source.
func (r *instanceDataSource) Configure( func (d *instanceDataSource) Configure(
ctx context.Context, ctx context.Context,
req datasource.ConfigureRequest, req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse, resp *datasource.ConfigureResponse,
) { ) {
var ok bool var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok { if !ok {
return return
} }
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) apiClientConfigOptions := []config.ConfigurationOption{
if resp.Diagnostics.HasError() { config.WithCustomAuth(d.providerData.RoundTripper),
utils.UserAgentConfigOption(d.providerData.Version),
}
if d.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(d.providerData.SQLServerFlexCustomEndpoint),
)
} else {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithRegion(d.providerData.GetRegion()),
)
}
apiClient, err := sqlserverflexalphaPkg.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 return
} }
r.client = apiClient d.client = apiClient
tflog.Info(ctx, "SQLServer Flex instance client configured") tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
} }
// Schema defines the schema for the data source. func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
func (r *instanceDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { var data dataSourceModel
s := sqlserverflexalpha.InstanceDataSourceSchema(ctx)
s.Attributes["id"] = schema.StringAttribute{
Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`\\\".",
Computed: true,
}
resp.Schema = s // Read Terraform configuration data into the model
} resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
// 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 dataSourceModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
ctx = core.InitProviderContext(ctx) ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString() projectId := data.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString() region := d.providerData.GetRegionWithOverride(data.Region)
region := r.providerData.GetRegionWithOverride(model.Region) instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
instanceResp, err := r.client.GetInstanceRequest(ctx, projectId, region, instanceId).Execute() ctx = tflog.SetField(ctx, "instance_id", instanceId)
instanceResp, err := d.client.GetInstanceRequest(ctx, projectId, region, instanceId).Execute()
if err != nil { if err != nil {
utils.LogError( utils.LogError(
ctx, ctx,
&resp.Diagnostics, &resp.Diagnostics,
err, err,
"Reading instance", "Reading instance",
fmt.Sprintf("Instance with ID %q does not exist in project %q.", instanceId, projectId), fmt.Sprintf("instance with ID %q does not exist in project %q.", instanceId, projectId),
map[int]string{ map[int]string{
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId), http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
}, },
@ -125,49 +130,17 @@ func (r *instanceDataSource) Read(
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
// var storage = &storageModel{} err = mapDataResponseToModel(ctx, instanceResp, &data, resp.Diagnostics)
// 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
// }
//}
err = mapFields(ctx, instanceResp, &model, resp.Diagnostics)
// err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
&resp.Diagnostics, &resp.Diagnostics,
"Error reading instance", fmt.Sprintf("%s Read", errorPrefix),
fmt.Sprintf("Processing API payload: %v", err), fmt.Sprintf("Processing API payload: %v", err),
) )
return return
} }
// Set refreshed state
diags = resp.State.Set(ctx, model) // Save data into Terraform state
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "SQLServer Flex instance read")
} }

View file

@ -2,6 +2,7 @@ package sqlserverflexalpha
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math" "math"
@ -10,29 +11,35 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
sqlserverflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
sqlserverflexResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen" sqlserverflexalphaDataGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/datasources_gen"
sqlserverflexalphaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen"
) )
// instanceModel is a type constraint for models that can be mapped from a GetInstanceResponse. func mapResponseToModel(
type instanceModel interface {
*dataSourceModel | *resourceModel
}
func mapFields[T instanceModel](
ctx context.Context, ctx context.Context,
resp *sqlserverflex.GetInstanceResponse, resp *sqlserverflexalpha.GetInstanceResponse,
m T, m *sqlserverflexalphaResGen.InstanceModel,
tfDiags diag.Diagnostics, tfDiags diag.Diagnostics,
) error { ) error {
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
m.Edition = types.StringValue(string(resp.GetEdition()))
m.Encryption = handleEncryption(m, resp)
m.FlavorId = types.StringValue(resp.GetFlavorId())
m.Id = types.StringValue(resp.GetId())
m.InstanceId = types.StringValue(resp.GetId())
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
m.Name = types.StringValue(resp.GetName())
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl()) netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
tfDiags.Append(diags...) tfDiags.Append(diags...)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("error converting network acl response value") return fmt.Errorf(
"error converting network acl response value",
)
} }
net, diags := sqlserverflexResGen.NewNetworkValue( net, diags := sqlserverflexalphaResGen.NewNetworkValue(
sqlserverflexResGen.NetworkValue{}.AttributeTypes(ctx), sqlserverflexalphaResGen.NetworkValue{}.AttributeTypes(ctx),
map[string]attr.Value{ map[string]attr.Value{
"access_scope": types.StringValue(string(resp.Network.GetAccessScope())), "access_scope": types.StringValue(string(resp.Network.GetAccessScope())),
"acl": netAcl, "acl": netAcl,
@ -42,11 +49,15 @@ func mapFields[T instanceModel](
) )
tfDiags.Append(diags...) tfDiags.Append(diags...)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("error converting network response value") return errors.New("error converting network response value")
} }
m.Network = net
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))
m.RetentionDays = types.Int64Value(resp.GetRetentionDays())
m.Status = types.StringValue(string(resp.GetStatus()))
stor, diags := sqlserverflexResGen.NewStorageValue( stor, diags := sqlserverflexalphaResGen.NewStorageValue(
sqlserverflexResGen.StorageValue{}.AttributeTypes(ctx), sqlserverflexalphaResGen.StorageValue{}.AttributeTypes(ctx),
map[string]attr.Value{ map[string]attr.Value{
"class": types.StringValue(resp.Storage.GetClass()), "class": types.StringValue(resp.Storage.GetClass()),
"size": types.Int64Value(resp.Storage.GetSize()), "size": types.Int64Value(resp.Storage.GetSize()),
@ -56,62 +67,117 @@ func mapFields[T instanceModel](
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("error converting storage response value") return fmt.Errorf("error converting storage response value")
} }
m.Storage = stor
// The interface conversion is safe due to the type constraint. m.Version = types.StringValue(string(resp.GetVersion()))
model := any(m) return nil
}
if rm, ok := model.(*resourceModel); ok { func mapDataResponseToModel(
rm.BackupSchedule = types.StringValue(resp.GetBackupSchedule()) ctx context.Context,
rm.Edition = types.StringValue(string(resp.GetEdition())) resp *sqlserverflexalpha.GetInstanceResponse,
rm.Encryption = handleEncryption(rm.Encryption, resp) m *dataSourceModel,
rm.FlavorId = types.StringValue(resp.GetFlavorId()) tfDiags diag.Diagnostics,
rm.Id = types.StringValue(resp.GetId()) ) error {
rm.InstanceId = types.StringValue(resp.GetId()) m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
rm.IsDeletable = types.BoolValue(resp.GetIsDeletable()) m.Edition = types.StringValue(string(resp.GetEdition()))
rm.Name = types.StringValue(resp.GetName()) m.Encryption = handleDSEncryption(m, resp)
rm.Network = net m.FlavorId = types.StringValue(resp.GetFlavorId())
rm.Replicas = types.Int64Value(int64(resp.GetReplicas())) m.Id = types.StringValue(resp.GetId())
rm.RetentionDays = types.Int64Value(resp.GetRetentionDays()) m.InstanceId = types.StringValue(resp.GetId())
rm.Status = types.StringValue(string(resp.GetStatus())) m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
rm.Storage = stor m.Name = types.StringValue(resp.GetName())
rm.Version = types.StringValue(string(resp.GetVersion())) netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
} else if dm, ok := model.(*dataSourceModel); ok { tfDiags.Append(diags...)
dm.BackupSchedule = types.StringValue(resp.GetBackupSchedule()) if diags.HasError() {
dm.Edition = types.StringValue(string(resp.GetEdition())) return fmt.Errorf(
dm.Encryption = handleEncryption(dm.Encryption, resp) "error converting network acl response value",
dm.FlavorId = types.StringValue(resp.GetFlavorId()) )
dm.Id = types.StringValue(resp.GetId())
dm.InstanceId = types.StringValue(resp.GetId())
dm.IsDeletable = types.BoolValue(resp.GetIsDeletable())
dm.Name = types.StringValue(resp.GetName())
dm.Network = net
dm.Replicas = types.Int64Value(int64(resp.GetReplicas()))
dm.RetentionDays = types.Int64Value(resp.GetRetentionDays())
dm.Status = types.StringValue(string(resp.GetStatus()))
dm.Storage = stor
dm.Version = types.StringValue(string(resp.GetVersion()))
} }
net, diags := sqlserverflexalphaDataGen.NewNetworkValue(
sqlserverflexalphaDataGen.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()),
},
)
tfDiags.Append(diags...)
if diags.HasError() {
return errors.New("error converting network response value")
}
m.Network = net
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))
m.RetentionDays = types.Int64Value(resp.GetRetentionDays())
m.Status = types.StringValue(string(resp.GetStatus()))
stor, diags := sqlserverflexalphaDataGen.NewStorageValue(
sqlserverflexalphaDataGen.StorageValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"class": types.StringValue(resp.Storage.GetClass()),
"size": types.Int64Value(resp.Storage.GetSize()),
},
)
tfDiags.Append(diags...)
if diags.HasError() {
return fmt.Errorf("error converting storage response value")
}
m.Storage = stor
m.Version = types.StringValue(string(resp.GetVersion()))
return nil return nil
} }
func handleEncryption( func handleEncryption(
encryptionValue sqlserverflexResGen.EncryptionValue, m *sqlserverflexalphaResGen.InstanceModel,
resp *sqlserverflex.GetInstanceResponse, resp *sqlserverflexalpha.GetInstanceResponse,
) sqlserverflexResGen.EncryptionValue { ) sqlserverflexalphaResGen.EncryptionValue {
if !resp.HasEncryption() || if !resp.HasEncryption() ||
resp.Encryption == nil || resp.Encryption == nil ||
resp.Encryption.KekKeyId == nil || resp.Encryption.KekKeyId == nil ||
resp.Encryption.KekKeyRingId == nil || resp.Encryption.KekKeyRingId == nil ||
resp.Encryption.KekKeyVersion == nil || resp.Encryption.KekKeyVersion == nil ||
resp.Encryption.ServiceAccount == nil { resp.Encryption.ServiceAccount == nil {
if encryptionValue.IsNull() || encryptionValue.IsUnknown() { if m.Encryption.IsNull() || m.Encryption.IsUnknown() {
return sqlserverflexResGen.NewEncryptionValueNull() return sqlserverflexalphaResGen.NewEncryptionValueNull()
} }
return encryptionValue return m.Encryption
} }
enc := sqlserverflexResGen.NewEncryptionValueNull() enc := sqlserverflexalphaResGen.NewEncryptionValueNull()
if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
enc.KekKeyId = types.StringValue(kVal)
}
if kkVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
enc.KekKeyRingId = types.StringValue(kkVal)
}
if kkvVal, ok := resp.Encryption.GetKekKeyVersionOk(); ok {
enc.KekKeyVersion = types.StringValue(kkvVal)
}
if sa, ok := resp.Encryption.GetServiceAccountOk(); ok {
enc.ServiceAccount = types.StringValue(sa)
}
return enc
}
func handleDSEncryption(
m *dataSourceModel,
resp *sqlserverflexalpha.GetInstanceResponse,
) sqlserverflexalphaDataGen.EncryptionValue {
if !resp.HasEncryption() ||
resp.Encryption == nil ||
resp.Encryption.KekKeyId == nil ||
resp.Encryption.KekKeyRingId == nil ||
resp.Encryption.KekKeyVersion == nil ||
resp.Encryption.ServiceAccount == nil {
if m.Encryption.IsNull() || m.Encryption.IsUnknown() {
return sqlserverflexalphaDataGen.NewEncryptionValueNull()
}
return m.Encryption
}
enc := sqlserverflexalphaDataGen.NewEncryptionValueNull()
if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok { if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
enc.KekKeyId = types.StringValue(kVal) enc.KekKeyId = types.StringValue(kVal)
} }
@ -129,25 +195,25 @@ func handleEncryption(
func toCreatePayload( func toCreatePayload(
ctx context.Context, ctx context.Context,
model *sqlserverflexResGen.InstanceModel, model *sqlserverflexalphaResGen.InstanceModel,
) (*sqlserverflex.CreateInstanceRequestPayload, error) { ) (*sqlserverflexalpha.CreateInstanceRequestPayload, error) {
if model == nil { if model == nil {
return nil, fmt.Errorf("nil model") return nil, fmt.Errorf("nil model")
} }
storagePayload := &sqlserverflex.CreateInstanceRequestPayloadGetStorageArgType{} storagePayload := &sqlserverflexalpha.CreateInstanceRequestPayloadGetStorageArgType{}
if !model.Storage.IsNull() && !model.Storage.IsUnknown() { if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
storagePayload.Class = model.Storage.Class.ValueStringPointer() storagePayload.Class = model.Storage.Class.ValueStringPointer()
storagePayload.Size = model.Storage.Size.ValueInt64Pointer() storagePayload.Size = model.Storage.Size.ValueInt64Pointer()
} }
var encryptionPayload *sqlserverflex.CreateInstanceRequestPayloadGetEncryptionArgType = nil var encryptionPayload *sqlserverflexalpha.CreateInstanceRequestPayloadGetEncryptionArgType = nil
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() && if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() &&
!model.Encryption.KekKeyId.IsNull() && model.Encryption.KekKeyId.IsUnknown() && model.Encryption.KekKeyId.ValueString() != "" && !model.Encryption.KekKeyId.IsNull() && model.Encryption.KekKeyId.IsUnknown() && model.Encryption.KekKeyId.ValueString() != "" &&
!model.Encryption.KekKeyRingId.IsNull() && !model.Encryption.KekKeyRingId.IsUnknown() && model.Encryption.KekKeyRingId.ValueString() != "" && !model.Encryption.KekKeyRingId.IsNull() && !model.Encryption.KekKeyRingId.IsUnknown() && model.Encryption.KekKeyRingId.ValueString() != "" &&
!model.Encryption.KekKeyVersion.IsNull() && !model.Encryption.KekKeyVersion.IsUnknown() && model.Encryption.KekKeyVersion.ValueString() != "" && !model.Encryption.KekKeyVersion.IsNull() && !model.Encryption.KekKeyVersion.IsUnknown() && model.Encryption.KekKeyVersion.ValueString() != "" &&
!model.Encryption.ServiceAccount.IsNull() && !model.Encryption.ServiceAccount.IsUnknown() && model.Encryption.ServiceAccount.ValueString() != "" { !model.Encryption.ServiceAccount.IsNull() && !model.Encryption.ServiceAccount.IsUnknown() && model.Encryption.ServiceAccount.ValueString() != "" {
encryptionPayload = &sqlserverflex.CreateInstanceRequestPayloadGetEncryptionArgType{ encryptionPayload = &sqlserverflexalpha.CreateInstanceRequestPayloadGetEncryptionArgType{
KekKeyId: model.Encryption.KekKeyId.ValueStringPointer(), KekKeyId: model.Encryption.KekKeyId.ValueStringPointer(),
KekKeyRingId: model.Encryption.KekKeyVersion.ValueStringPointer(), KekKeyRingId: model.Encryption.KekKeyVersion.ValueStringPointer(),
KekKeyVersion: model.Encryption.KekKeyRingId.ValueStringPointer(), KekKeyVersion: model.Encryption.KekKeyRingId.ValueStringPointer(),
@ -155,9 +221,9 @@ func toCreatePayload(
} }
} }
networkPayload := &sqlserverflex.CreateInstanceRequestPayloadGetNetworkArgType{} networkPayload := &sqlserverflexalpha.CreateInstanceRequestPayloadGetNetworkArgType{}
if !model.Network.IsNull() && !model.Network.IsUnknown() { if !model.Network.IsNull() && !model.Network.IsUnknown() {
networkPayload.AccessScope = sqlserverflex.CreateInstanceRequestPayloadNetworkGetAccessScopeAttributeType( networkPayload.AccessScope = sqlserverflexalpha.CreateInstanceRequestPayloadNetworkGetAccessScopeAttributeType(
model.Network.AccessScope.ValueStringPointer(), model.Network.AccessScope.ValueStringPointer(),
) )
@ -169,7 +235,7 @@ func toCreatePayload(
networkPayload.Acl = &resList networkPayload.Acl = &resList
} }
return &sqlserverflex.CreateInstanceRequestPayload{ return &sqlserverflexalpha.CreateInstanceRequestPayload{
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule), BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
Encryption: encryptionPayload, Encryption: encryptionPayload,
FlavorId: conversion.StringValueToPointer(model.FlavorId), FlavorId: conversion.StringValueToPointer(model.FlavorId),
@ -177,20 +243,25 @@ func toCreatePayload(
Network: networkPayload, Network: networkPayload,
RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays), RetentionDays: conversion.Int64ValueToPointer(model.RetentionDays),
Storage: storagePayload, Storage: storagePayload,
Version: sqlserverflex.CreateInstanceRequestPayloadGetVersionAttributeType(conversion.StringValueToPointer(model.Version)), Version: sqlserverflexalpha.CreateInstanceRequestPayloadGetVersionAttributeType(
conversion.StringValueToPointer(model.Version),
),
}, nil }, nil
} }
// TODO: check func with his args
func toUpdatePayload( func toUpdatePayload(
ctx context.Context, ctx context.Context,
m *sqlserverflexResGen.InstanceModel, m *sqlserverflexalphaResGen.InstanceModel,
resp *resource.UpdateResponse, resp *resource.UpdateResponse,
) (*sqlserverflex.UpdateInstanceRequestPayload, error) { ) (*sqlserverflexalpha.UpdateInstanceRequestPayload, error) {
if m == nil {
return nil, fmt.Errorf("nil model")
}
if m.Replicas.ValueInt64() > math.MaxUint32 { if m.Replicas.ValueInt64() > math.MaxUint32 {
return nil, fmt.Errorf("replicas value is too big for uint32") return nil, fmt.Errorf("replicas value is too big for uint32")
} }
replVal := sqlserverflex.Replicas(uint32(m.Replicas.ValueInt64())) // nolint:gosec // check is performed above replVal := sqlserverflexalpha.Replicas(uint32(m.Replicas.ValueInt64())) // nolint:gosec // check is performed above
var netAcl []string var netAcl []string
diags := m.Network.Acl.ElementsAs(ctx, &netAcl, false) diags := m.Network.Acl.ElementsAs(ctx, &netAcl, false)
@ -198,16 +269,16 @@ func toUpdatePayload(
if diags.HasError() { if diags.HasError() {
return nil, fmt.Errorf("error converting model network acl value") return nil, fmt.Errorf("error converting model network acl value")
} }
return &sqlserverflex.UpdateInstanceRequestPayload{ return &sqlserverflexalpha.UpdateInstanceRequestPayload{
BackupSchedule: m.BackupSchedule.ValueStringPointer(), BackupSchedule: m.BackupSchedule.ValueStringPointer(),
FlavorId: m.FlavorId.ValueStringPointer(), FlavorId: m.FlavorId.ValueStringPointer(),
Name: m.Name.ValueStringPointer(), Name: m.Name.ValueStringPointer(),
Network: &sqlserverflex.UpdateInstanceRequestPayloadNetwork{ Network: sqlserverflexalpha.NewUpdateInstanceRequestPayloadNetwork(netAcl),
Acl: &netAcl, Replicas: &replVal,
}, RetentionDays: m.RetentionDays.ValueInt64Pointer(),
Replicas: &replVal, Storage: &sqlserverflexalpha.StorageUpdate{Size: m.Storage.Size.ValueInt64Pointer()},
RetentionDays: m.RetentionDays.ValueInt64Pointer(), Version: sqlserverflexalpha.UpdateInstanceRequestPayloadGetVersionAttributeType(
Storage: &sqlserverflex.StorageUpdate{Size: m.Storage.Size.ValueInt64Pointer()}, m.Version.ValueStringPointer(),
Version: sqlserverflex.UpdateInstanceRequestPayloadGetVersionAttributeType(m.Version.ValueStringPointer()), ),
}, nil }, nil
} }

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package sqlserverflexalpha package sqlserverflexalpha
import ( import (
@ -10,26 +8,25 @@ import (
"strings" "strings"
"time" "time"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
sqlserverflexalpha2 "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
"github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
wait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha" wait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance/resources_gen"
) )
// Ensure the implementation satisfies the expected interfaces.
var ( var (
_ resource.Resource = &instanceResource{} _ resource.Resource = &instanceResource{}
_ resource.ResourceWithConfigure = &instanceResource{} _ resource.ResourceWithConfigure = &instanceResource{}
@ -38,19 +35,17 @@ var (
_ resource.ResourceWithIdentity = &instanceResource{} _ resource.ResourceWithIdentity = &instanceResource{}
) )
// NewInstanceResource is a helper function to simplify the provider implementation.
func NewInstanceResource() resource.Resource { func NewInstanceResource() resource.Resource {
return &instanceResource{} return &instanceResource{}
} }
//nolint:unused // TODO: remove if not needed later type instanceResource struct {
var validNodeTypes []string = []string{ client *sqlserverflexalpha.APIClient
"Single", providerData core.ProviderData
"Replica",
} }
// resourceModel describes the resource data model. // resourceModel describes the resource data model.
type resourceModel = sqlserverflexalpha2.InstanceModel type resourceModel = sqlserverflexalphaResGen.InstanceModel
type InstanceResourceIdentityModel struct { type InstanceResourceIdentityModel struct {
ProjectID types.String `tfsdk:"project_id"` ProjectID types.String `tfsdk:"project_id"`
@ -58,93 +53,19 @@ type InstanceResourceIdentityModel struct {
InstanceID types.String `tfsdk:"instance_id"` InstanceID types.String `tfsdk:"instance_id"`
} }
// instanceResource is the resource implementation. func (r *instanceResource) Metadata(
type instanceResource struct {
client *sqlserverflexalpha.APIClient
providerData core.ProviderData
}
// Metadata returns the resource type name.
func (r *instanceResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_instance"
}
// Configure adds the provider configured client to the resource.
func (r *instanceResource) Configure(
ctx context.Context, ctx context.Context,
req resource.ConfigureRequest, req resource.MetadataRequest,
resp *resource.ConfigureResponse, resp *resource.MetadataResponse,
) { ) {
var ok bool resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_instance"
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
return
}
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
return
}
r.client = apiClient
tflog.Info(ctx, "SQLServer Flex instance client configured")
}
// 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
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
}
var configModel sqlserverflexalpha2.InstanceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
}
if req.Plan.Raw.IsNull() {
return
}
var planModel sqlserverflexalpha2.InstanceModel
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
}
var identityModel InstanceResourceIdentityModel
identityModel.ProjectID = planModel.ProjectId
identityModel.Region = planModel.Region
if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
identityModel.InstanceID = planModel.InstanceId
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityModel)...)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
if resp.Diagnostics.HasError() {
return
}
} }
//go:embed planModifiers.yaml //go:embed planModifiers.yaml
var modifiersFileByte []byte var modifiersFileByte []byte
// Schema defines the schema for the resource. func (r *instanceResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { s := sqlserverflexalphaResGen.InstanceResourceSchema(ctx)
schema := sqlserverflexalpha2.InstanceResourceSchema(ctx)
fields, err := utils.ReadModifiersConfig(modifiersFileByte) fields, err := utils.ReadModifiersConfig(modifiersFileByte)
if err != nil { if err != nil {
@ -152,12 +73,12 @@ func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest,
return return
} }
err = utils.AddPlanModifiersToResourceSchema(fields, &schema) err = utils.AddPlanModifiersToResourceSchema(fields, &s)
if err != nil { if err != nil {
resp.Diagnostics.AddError("error adding plan modifiers", err.Error()) resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
return return
} }
resp.Schema = schema resp.Schema = s
} }
func (r *instanceResource) IdentitySchema( func (r *instanceResource) IdentitySchema(
@ -180,15 +101,355 @@ func (r *instanceResource) IdentitySchema(
} }
} }
// Create creates the resource and sets the initial Terraform state. // Configure adds the provider configured client to the resource.
func (r *instanceResource) Create( func (r *instanceResource) Configure(
ctx context.Context, ctx context.Context,
req resource.CreateRequest, req resource.ConfigureRequest,
resp *resource.CreateResponse, 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.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(r.providerData.SQLServerFlexCustomEndpoint),
)
} 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.Instance client configured")
}
// 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 ) { // nolint:gocritic // function signature required by Terraform
var model resourceModel
diags := req.Plan.Get(ctx, &model) // skip initial empty configuration to avoid follow-up errors
resp.Diagnostics.Append(diags...) if req.Config.Raw.IsNull() {
return
}
var configModel resourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
}
if req.Plan.Raw.IsNull() {
return
}
var planModel resourceModel
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
}
}
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data resourceModel
crateErr := "[SQL Server Flex BETA - Create] error"
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := data.ProjectId.ValueString()
region := data.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// Generate API request body from model
payload, err := toCreatePayload(ctx, &data)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
crateErr,
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Create new Instance
createResp, err := r.client.CreateInstanceRequest(
ctx,
projectId,
region,
).CreateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, crateErr, fmt.Sprintf("Calling API: %v", err))
return
}
ctx = core.LogResponse(ctx)
InstanceId := *createResp.Id
// Example data value setting
data.InstanceId = types.StringValue("id-from-response")
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,
InstanceId,
region,
).SetSleepBeforeWait(
10 * time.Second,
).SetTimeout(
90 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
crateErr,
fmt.Sprintf("Instance creation waiting: %v", err),
)
return
}
if waitResp.Id == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
crateErr,
"Instance creation waiting: returned id is nil",
)
return
}
// Map response body to schema
err = mapResponseToModel(ctx, waitResp, &data, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
crateErr,
fmt.Sprintf("processing API payload: %v", err),
)
return
}
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "sqlserverflexalpha.Instance created")
}
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data resourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
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 := data.ProjectId.ValueString()
region := data.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "instance_id", instanceId)
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
if ok && oapiErr.StatusCode == http.StatusNotFound {
resp.State.RemoveResource(ctx)
return
}
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", err.Error())
return
}
ctx = core.LogResponse(ctx)
// Map response body to schema
err = mapResponseToModel(ctx, instanceResp, &data, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Save identity into Terraform state
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
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "sqlserverflexalpha.Instance read")
}
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data resourceModel
updateInstanceError := "Error updating instance"
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := data.ProjectId.ValueString()
region := data.Region.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "instance_id", instanceId)
// Generate API request body from model
payload, err := toUpdatePayload(ctx, &data, resp)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
updateInstanceError,
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Update existing instance
err = r.client.UpdateInstanceRequest(
ctx,
projectId,
region,
instanceId,
).UpdateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, updateInstanceError, err.Error())
return
}
ctx = core.LogResponse(ctx)
waitResp, err := wait.
UpdateInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).
SetSleepBeforeWait(15 * time.Second).
SetTimeout(45 * time.Minute).
WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
updateInstanceError,
fmt.Sprintf("Instance update waiting: %v", err),
)
return
}
// Map response body to schema
err = mapResponseToModel(ctx, waitResp, &data, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
updateInstanceError,
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
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
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "sqlserverflexalpha.Instance updated")
}
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data resourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
@ -207,284 +468,8 @@ func (r *instanceResource) Create(
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
// Generate API request body from model instanceId := identityData.InstanceID.ValueString()
payload, err := toCreatePayload(ctx, &model)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Create new instance
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))
return
}
ctx = core.LogResponse(ctx)
instanceId := *createResp.Id
// 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
}
utils.SetAndLogStateFields(
ctx, &resp.Diagnostics, &resp.State, map[string]any{
"id": utils.BuildInternalTerraformId(projectId, region, instanceId),
"instance_id": instanceId,
},
)
if resp.Diagnostics.HasError() {
return
}
// The creation waiter sometimes returns an error from the API: "instance with id xxx has unexpected status Failure"
// which can be avoided by sleeping before wait
waitResp, err := wait.CreateInstanceWaitHandler(
ctx,
r.client,
projectId,
instanceId,
region,
).SetSleepBeforeWait(
30 * time.Second,
).SetTimeout(
90 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Instance creation waiting: %v", err),
)
return
}
if waitResp.FlavorId == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
"Instance creation waiting: returned flavor id is nil",
)
return
}
// Map response body to schema
// err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
err = mapFields(ctx, waitResp, &model, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set state to fully populated data
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "SQLServer Flex instance created")
}
// 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 resourceModel
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)
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region)
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
if ok && oapiErr.StatusCode == http.StatusNotFound {
resp.State.RemoveResource(ctx)
return
}
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", err.Error())
return
}
ctx = core.LogResponse(ctx)
// Map response body to schema
err = mapFields(ctx, instanceResp, &model, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
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, "SQLServer 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
// Retrieve values from plan
var model resourceModel
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.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)
// Generate API request body from model
payload, err := toUpdatePayload(ctx, &model, resp)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error updating instance",
fmt.Sprintf("Creating API payload: %v", err),
)
return
}
// Update existing instance
err = r.client.UpdateInstanceRequest(
ctx,
projectId,
region,
instanceId,
).UpdateInstanceRequestPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
return
}
ctx = core.LogResponse(ctx)
waitResp, err := wait.UpdateInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).WaitWithContext(ctx)
if err != nil {
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, resp.Diagnostics)
// err = mapFields(ctx, waitResp, &model, storage, encryption, network, region)
if err != nil {
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() {
return
}
tflog.Info(ctx, "SQLServer Flex instance updated")
}
// 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
// Retrieve values from state
var model resourceModel
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.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)
// Delete existing instance // Delete existing instance
err := r.client.DeleteInstanceRequest(ctx, projectId, region, instanceId).Execute() err := r.client.DeleteInstanceRequest(ctx, projectId, region, instanceId).Execute()
@ -495,7 +480,7 @@ func (r *instanceResource) Delete(
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).WaitWithContext(ctx) delResp, err := wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, instanceId, region).WaitWithContext(ctx)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
ctx, ctx,
@ -505,16 +490,31 @@ func (r *instanceResource) Delete(
) )
return return
} }
tflog.Info(ctx, "SQLServer Flex instance deleted")
if delResp != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error deleting instance",
"wait handler returned non nil result",
)
return
}
resp.State.RemoveResource(ctx)
tflog.Info(ctx, "sqlserverflexalpha.Instance deleted")
} }
// ImportState imports a resource into the Terraform state on success. // ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,instance_id // The expected format of the resource import identifier is: project_id,zone_id,record_set_id
func (r *instanceResource) ImportState( func (r *instanceResource) ImportState(
ctx context.Context, ctx context.Context,
req resource.ImportStateRequest, req resource.ImportStateRequest,
resp *resource.ImportStateResponse, resp *resource.ImportStateResponse,
) { ) {
ctx = core.InitProviderContext(ctx)
if req.ID != "" { if req.ID != "" {
idParts := strings.Split(req.ID, core.Separator) idParts := strings.Split(req.ID, core.Separator)
@ -551,5 +551,5 @@ func (r *instanceResource) ImportState(
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), region)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), region)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), instanceId)...)
tflog.Info(ctx, "SQLServer Flex instance state imported") tflog.Info(ctx, "sqlserverflexalpha instance state imported")
} }

View file

@ -1,823 +0,0 @@
package sqlserverflexalpha
// type sqlserverflexClientMocked struct {
// returnError bool
// listFlavorsResp *sqlserverflex.GetFlavorsResponse
// }
//
// func (c *sqlserverflexClientMocked) GetFlavorsExecute(_ context.Context, _, _ string) (*sqlserverflex.GetFlavorsResponse, error) {
// if c.returnError {
// return nil, fmt.Errorf("get flavors failed")
// }
//
// return c.listFlavorsResp, nil
// }
// func TestMapFields(t *testing.T) {
// t.Skip("Skipping - needs refactoring")
// const testRegion = "region"
// tests := []struct {
// description string
// state Model
// input *sqlserverflex.GetInstanceResponse
// storage *storageModel
// encryption *encryptionModel
// network *networkModel
// region string
// expected Model
// isValid bool
// }{
// {
// "default_values",
// Model{
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Replicas: types.Int64Value(1),
// RetentionDays: types.Int64Value(1),
// Version: types.StringValue("v1"),
// Edition: types.StringValue("edition 1"),
// Status: types.StringValue("status"),
// IsDeletable: types.BoolValue(true),
// },
// &sqlserverflex.GetInstanceResponse{
// FlavorId: utils.Ptr("flavor_id"),
// Replicas: sqlserverflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(1))),
// RetentionDays: utils.Ptr(int64(1)),
// Version: sqlserverflex.GetInstanceResponseGetVersionAttributeType(utils.Ptr("v1")),
// Edition: sqlserverflex.GetInstanceResponseGetEditionAttributeType(utils.Ptr("edition 1")),
// Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
// IsDeletable: utils.Ptr(true),
// },
// &storageModel{},
// &encryptionModel{},
// &networkModel{
// ACL: types.ListNull(basetypes.StringType{}),
// },
// testRegion,
// Model{
// Id: types.StringValue("pid,region,iid"),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Name: types.StringNull(),
// BackupSchedule: types.StringNull(),
// Replicas: types.Int64Value(1),
// Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
// "class": types.StringNull(),
// "size": types.Int64Null(),
// }),
// Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
// "keyring_id": types.StringNull(),
// "key_id": types.StringNull(),
// "key_version": types.StringNull(),
// "service_account": types.StringNull(),
// }),
// Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
// "acl": types.ListNull(types.StringType),
// "access_scope": types.StringNull(),
// "instance_address": types.StringNull(),
// "router_address": types.StringNull(),
// }),
// IsDeletable: types.BoolValue(true),
// Edition: types.StringValue("edition 1"),
// Status: types.StringValue("status"),
// RetentionDays: types.Int64Value(1),
// Version: types.StringValue("v1"),
// Region: types.StringValue(testRegion),
// },
// true,
// },
// {
// "simple_values",
// Model{
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// },
// &sqlserverflex.GetInstanceResponse{
// BackupSchedule: utils.Ptr("schedule"),
// FlavorId: utils.Ptr("flavor_id"),
// Id: utils.Ptr("iid"),
// Name: utils.Ptr("name"),
// Replicas: sqlserverflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
// Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
// Storage: &sqlserverflex.Storage{
// Class: utils.Ptr("class"),
// Size: utils.Ptr(int64(78)),
// },
// Edition: sqlserverflex.GetInstanceResponseGetEditionAttributeType(utils.Ptr("edition")),
// RetentionDays: utils.Ptr(int64(1)),
// Version: sqlserverflex.GetInstanceResponseGetVersionAttributeType(utils.Ptr("version")),
// IsDeletable: utils.Ptr(true),
// Encryption: nil,
// Network: &sqlserverflex.InstanceNetwork{
// AccessScope: nil,
// Acl: &[]string{
// "ip1",
// "ip2",
// "",
// },
// InstanceAddress: nil,
// RouterAddress: nil,
// },
// },
// &storageModel{},
// &encryptionModel{},
// &networkModel{
// ACL: types.ListValueMust(basetypes.StringType{}, []attr.Value{
// types.StringValue("ip1"),
// types.StringValue("ip2"),
// types.StringValue(""),
// }),
// },
// testRegion,
// Model{
// Id: types.StringValue("pid,region,iid"),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Name: types.StringValue("name"),
// BackupSchedule: types.StringValue("schedule"),
// Replicas: types.Int64Value(56),
// Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
// "class": types.StringValue("class"),
// "size": types.Int64Value(78),
// }),
// Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
// "acl": types.ListValueMust(types.StringType, []attr.Value{
// types.StringValue("ip1"),
// types.StringValue("ip2"),
// types.StringValue(""),
// }),
// "access_scope": types.StringNull(),
// "instance_address": types.StringNull(),
// "router_address": types.StringNull(),
// }),
// Edition: types.StringValue("edition"),
// RetentionDays: types.Int64Value(1),
// Version: types.StringValue("version"),
// Region: types.StringValue(testRegion),
// IsDeletable: types.BoolValue(true),
// Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
// "keyring_id": types.StringNull(),
// "key_id": types.StringNull(),
// "key_version": types.StringNull(),
// "service_account": types.StringNull(),
// }),
// Status: types.StringValue("status"),
// },
// true,
// },
// // {
// // "simple_values_no_flavor_and_storage",
// // Model{
// // InstanceId: types.StringValue("iid"),
// // ProjectId: types.StringValue("pid"),
// // },
// // &sqlserverflex.GetInstanceResponse{
// // Acl: &[]string{
// // "ip1",
// // "ip2",
// // "",
// // },
// // BackupSchedule: utils.Ptr("schedule"),
// // FlavorId: nil,
// // Id: utils.Ptr("iid"),
// // Name: utils.Ptr("name"),
// // Replicas: sqlserverflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
// // Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
// // Storage: nil,
// // Edition: sqlserverflex.GetInstanceResponseGetEditionAttributeType(utils.Ptr("edition")),
// // RetentionDays: utils.Ptr(int64(1)),
// // Version: sqlserverflex.GetInstanceResponseGetVersionAttributeType(utils.Ptr("version")),
// // },
// // &flavorModel{
// // CPU: types.Int64Value(12),
// // RAM: types.Int64Value(34),
// // },
// // &storageModel{
// // Class: types.StringValue("class"),
// // Size: types.Int64Value(78),
// // },
// // &optionsModel{
// // Edition: types.StringValue("edition"),
// // RetentionDays: types.Int64Value(1),
// // },
// // 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("ip1"),
// // types.StringValue("ip2"),
// // types.StringValue(""),
// // }),
// // 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),
// // }),
// // Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
// // "edition": types.StringValue("edition"),
// // "retention_days": types.Int64Value(1),
// // }),
// // Version: types.StringValue("version"),
// // Region: types.StringValue(testRegion),
// // },
// // true,
// // },
// // {
// // "acls_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"),
// // }),
// // },
// // &sqlserverflex.GetInstanceResponse{
// // Acl: &[]string{
// // "",
// // "ip1",
// // "ip2",
// // },
// // BackupSchedule: utils.Ptr("schedule"),
// // FlavorId: nil,
// // Id: utils.Ptr("iid"),
// // Name: utils.Ptr("name"),
// // Replicas: sqlserverflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
// // Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
// // Storage: nil,
// // //Options: &map[string]string{
// // // "edition": "edition",
// // // "retentionDays": "1",
// // //},
// // Version: sqlserverflex.GetInstanceResponseGetVersionAttributeType(utils.Ptr("version")),
// // },
// // &flavorModel{
// // CPU: types.Int64Value(12),
// // RAM: types.Int64Value(34),
// // },
// // &storageModel{
// // Class: types.StringValue("class"),
// // Size: types.Int64Value(78),
// // },
// // &optionsModel{},
// // 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),
// // }),
// // Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
// // "edition": types.StringValue("edition"),
// // "retention_days": types.Int64Value(1),
// // }),
// // Version: types.StringValue("version"),
// // Region: types.StringValue(testRegion),
// // },
// // true,
// // },
// // {
// // "nil_response",
// // Model{
// // InstanceId: types.StringValue("iid"),
// // ProjectId: types.StringValue("pid"),
// // },
// // nil,
// // &flavorModel{},
// // &storageModel{},
// // &optionsModel{},
// // testRegion,
// // Model{},
// // false,
// // },
// // {
// // "no_resource_id",
// // Model{
// // InstanceId: types.StringValue("iid"),
// // ProjectId: types.StringValue("pid"),
// // },
// // &sqlserverflex.GetInstanceResponse{},
// // &flavorModel{},
// // &storageModel{},
// // &optionsModel{},
// // 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.state, tt.expected)
// if diff != "" {
// t.Fatalf("Data does not match: %s", diff)
// }
// }
// })
// }
//}
// func TestToCreatePayload(t *testing.T) {
// tests := []struct {
// description string
// input *Model
// inputAcl []string
// inputFlavor *flavorModel
// inputStorage *storageModel
// inputOptions *optionsModel
// expected *sqlserverflex.CreateInstanceRequestPayload
// isValid bool
// }{
// {
// "default_values",
// &Model{},
// []string{},
// &flavorModel{},
// &storageModel{},
// &optionsModel{},
// &sqlserverflex.CreateInstanceRequestPayload{
// Acl: &sqlserverflex.CreateInstanceRequestPayloadGetAclArgType{},
// Storage: &sqlserverflex.CreateInstanceRequestPayloadGetStorageArgType{},
// },
// 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),
// },
// &optionsModel{
// Edition: types.StringValue("edition"),
// RetentionDays: types.Int64Value(1),
// },
// &sqlserverflex.CreateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{
// "ip_1",
// "ip_2",
// },
// },
// BackupSchedule: utils.Ptr("schedule"),
// FlavorId: utils.Ptr("flavor_id"),
// Name: utils.Ptr("name"),
// Storage: &sqlserverflex.CreateInstancePayloadStorage{
// Class: utils.Ptr("class"),
// Size: utils.Ptr(int64(34)),
// },
// Options: &sqlserverflex.CreateInstancePayloadOptions{
// Edition: utils.Ptr("edition"),
// RetentionDays: utils.Ptr("1"),
// },
// 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(),
// },
// &optionsModel{
// Edition: types.StringNull(),
// RetentionDays: types.Int64Null(),
// },
// &sqlserverflex.CreateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{
// "",
// },
// },
// BackupSchedule: nil,
// FlavorId: nil,
// Name: nil,
// Storage: &sqlserverflex.CreateInstancePayloadStorage{
// Class: nil,
// Size: nil,
// },
// Options: &sqlserverflex.CreateInstancePayloadOptions{},
// Version: nil,
// },
// true,
// },
// {
// "nil_model",
// nil,
// []string{},
// &flavorModel{},
// &storageModel{},
// &optionsModel{},
// nil,
// false,
// },
// {
// "nil_acl",
// &Model{},
// nil,
// &flavorModel{},
// &storageModel{},
// &optionsModel{},
// &sqlserverflex.CreateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{},
// Storage: &sqlserverflex.CreateInstancePayloadStorage{},
// Options: &sqlserverflex.CreateInstancePayloadOptions{},
// },
// true,
// },
// {
// "nil_flavor",
// &Model{},
// []string{},
// nil,
// &storageModel{},
// &optionsModel{},
// nil,
// false,
// },
// {
// "nil_storage",
// &Model{},
// []string{},
// &flavorModel{},
// nil,
// &optionsModel{},
// &sqlserverflex.CreateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{},
// },
// Storage: &sqlserverflex.CreateInstancePayloadStorage{},
// Options: &sqlserverflex.CreateInstancePayloadOptions{},
// },
// true,
// },
// {
// "nil_options",
// &Model{},
// []string{},
// &flavorModel{},
// &storageModel{},
// nil,
// &sqlserverflex.CreateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{},
// },
// Storage: &sqlserverflex.CreateInstancePayloadStorage{},
// Options: &sqlserverflex.CreateInstancePayloadOptions{},
// },
// true,
// },
// }
// for _, tt := range tests {
// t.Run(tt.description, func(t *testing.T) {
// output, err := toCreatePayload(tt.input, tt.inputAcl, tt.inputFlavor, tt.inputStorage, tt.inputOptions)
// 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 TestToUpdatePayload(t *testing.T) {
// tests := []struct {
// description string
// input *Model
// inputAcl []string
// inputFlavor *flavorModel
// expected *sqlserverflex.PartialUpdateInstancePayload
// isValid bool
// }{
// {
// "default_values",
// &Model{},
// []string{},
// &flavorModel{},
// &sqlserverflex.PartialUpdateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{},
// },
// },
// 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"),
// },
// &sqlserverflex.PartialUpdateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{
// "ip_1",
// "ip_2",
// },
// },
// BackupSchedule: utils.Ptr("schedule"),
// FlavorId: utils.Ptr("flavor_id"),
// Name: utils.Ptr("name"),
// 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(),
// },
// &sqlserverflex.PartialUpdateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{
// Items: &[]string{
// "",
// },
// },
// BackupSchedule: nil,
// FlavorId: nil,
// Name: nil,
// Version: nil,
// },
// true,
// },
// {
// "nil_model",
// nil,
// []string{},
// &flavorModel{},
// nil,
// false,
// },
// {
// "nil_acl",
// &Model{},
// nil,
// &flavorModel{},
// &sqlserverflex.PartialUpdateInstancePayload{
// Acl: &sqlserverflex.CreateInstancePayloadAcl{},
// },
// true,
// },
// {
// "nil_flavor",
// &Model{},
// []string{},
// nil,
// nil,
// false,
// },
// }
// for _, tt := range tests {
// t.Run(tt.description, func(t *testing.T) {
// output, err := toUpdatePayload(tt.input, tt.inputAcl, tt.inputFlavor)
// 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 *sqlserverflex.ListFlavorsResponse
// expected *flavorModel
// getFlavorsFails bool
// isValid bool
// }{
// {
// "ok_flavor",
// &flavorModel{
// CPU: types.Int64Value(2),
// RAM: types.Int64Value(8),
// },
// &sqlserverflex.ListFlavorsResponse{
// Flavors: &[]sqlserverflex.InstanceFlavorEntry{
// {
// 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),
// },
// &sqlserverflex.ListFlavorsResponse{
// Flavors: &[]sqlserverflex.InstanceFlavorEntry{
// {
// 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),
// },
// &sqlserverflex.ListFlavorsResponse{
// Flavors: &[]sqlserverflex.InstanceFlavorEntry{
// {
// 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),
// },
// &sqlserverflex.ListFlavorsResponse{},
// &flavorModel{
// CPU: types.Int64Value(2),
// RAM: types.Int64Value(8),
// },
// false,
// false,
// },
// {
// "error_response",
// &flavorModel{
// CPU: types.Int64Value(2),
// RAM: types.Int64Value(8),
// },
// &sqlserverflex.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 := &sqlserverflexClientMocked{
// returnError: tt.getFlavorsFails,
// listFlavorsResp: 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)
// }
// }
// })
// }
// }

View file

@ -4,22 +4,68 @@ import (
"context" "context"
_ "embed" _ "embed"
"fmt" "fmt"
"log"
"os" "os"
"strconv" "strconv"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/stackitcloud/stackit-sdk-go/core/config"
sqlserverflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
sqlserverflexalphaPkgGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
// The fwresource import alias is so there is no collision // The fwresource import alias is so there is no collision
// with the more typical acceptance testing import: // with the more typical acceptance testing import:
// "github.com/hashicorp/terraform-plugin-testing/helper/resource" // "github.com/hashicorp/terraform-plugin-testing/helper/resource"
fwresource "github.com/hashicorp/terraform-plugin-framework/resource" fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
) )
const providerPrefix = "stackitprivatepreview_sqlserverflexalpha"
var testInstances []string
func init() {
sweeperName := fmt.Sprintf("%s_%s", providerPrefix, "sweeper")
resource.AddTestSweepers(sweeperName, &resource.Sweeper{
Name: sweeperName,
F: func(region string) error {
ctx := context.Background()
apiClientConfigOptions := []config.ConfigurationOption{}
apiClient, err := sqlserverflexalphaPkgGen.NewAPIClient(apiClientConfigOptions...)
if err != nil {
log.Fatalln(err)
}
instances, err := apiClient.ListInstancesRequest(ctx, testutils.ProjectId, region).
Size(100).
Execute()
if err != nil {
log.Fatalln(err)
}
for _, inst := range instances.GetInstances() {
if strings.HasPrefix(inst.GetName(), "tf-acc-") {
for _, item := range testInstances {
if inst.GetName() == item {
delErr := apiClient.DeleteInstanceRequestExecute(ctx, testutils.ProjectId, region, inst.GetId())
if delErr != nil {
// TODO: maybe just warn?
log.Fatalln(delErr)
}
}
}
}
}
return nil
},
})
}
func TestInstanceResourceSchema(t *testing.T) { func TestInstanceResourceSchema(t *testing.T) {
t.Parallel() t.Parallel()
@ -85,9 +131,15 @@ type User struct {
} }
type Database struct { type Database struct {
Name string Name string
ProjectId string ProjectId string
Owner string Owner string
Collation string
Compatibility string
}
func resName(res, name string) string {
return fmt.Sprintf("%s_%s.%s", providerPrefix, res, name)
} }
func getExample() resData { func getExample() resData {
@ -112,10 +164,6 @@ func getExample() resData {
func TestAccInstance(t *testing.T) { func TestAccInstance(t *testing.T) {
exData := getExample() exData := getExample()
resName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_instance.%s",
exData.TfName,
)
updNameData := exData updNameData := exData
updNameData.Name = "name-updated" updNameData.Name = "name-updated"
@ -123,8 +171,12 @@ func TestAccInstance(t *testing.T) {
updSizeData := exData updSizeData := exData
updSizeData.Size = 25 updSizeData.Size = 25
resource.Test(t, resource.TestCase{ resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() {
testAccPreCheck(t)
t.Logf(" ... working on instance %s", exData.TfName)
testInstances = append(testInstances, exData.TfName)
},
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
// Create and verify // Create and verify
@ -134,8 +186,9 @@ func TestAccInstance(t *testing.T) {
exData, exData,
), ),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", exData.Name), resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", exData.Name),
resource.TestCheckResourceAttrSet(resName, "id"), resource.TestCheckResourceAttrSet(resName("instance", exData.TfName), "id"),
// TODO: check all fields
), ),
}, },
// Update name and verify // Update name and verify
@ -145,7 +198,7 @@ func TestAccInstance(t *testing.T) {
updNameData, updNameData,
), ),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", updNameData.Name), resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", updNameData.Name),
), ),
}, },
// Update size and verify // Update size and verify
@ -156,15 +209,18 @@ func TestAccInstance(t *testing.T) {
), ),
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
resName, testutils.ResStr(providerPrefix, "instance", exData.TfName),
"storage.size", "storage.size",
strconv.Itoa(int(updSizeData.Size)), strconv.Itoa(int(updSizeData.Size)),
), ),
), ),
}, },
{
RefreshState: true,
},
//// Import test //// Import test
//{ //{
// ResourceName: "example_resource.test", // ResourceName: resName("instance", exData.TfName),
// ImportState: true, // ImportState: true,
// ImportStateVerify: true, // ImportStateVerify: true,
// }, // },
@ -172,29 +228,39 @@ func TestAccInstance(t *testing.T) {
}) })
} }
func TestAccInstanceWithUsers(t *testing.T) { func TestAccInstanceNoEncryption(t *testing.T) {
data := getExample() data := getExample()
dbName := "testDb"
userName := "testUser" userName := "testUser"
data.Users = []User{ data.Users = []User{
{ {
Name: userName, Name: userName,
ProjectId: os.Getenv("TF_ACC_PROJECT_ID"), ProjectId: os.Getenv("TF_ACC_PROJECT_ID"),
Roles: []string{"##STACKIT_LoginManager##", "##STACKIT_DatabaseManager##"}, Roles: []string{
"##STACKIT_DatabaseManager##",
"##STACKIT_LoginManager##",
"##STACKIT_ProcessManager##",
"##STACKIT_ServerManager##",
"##STACKIT_SQLAgentManager##",
"##STACKIT_SQLAgentUser##",
},
},
}
data.Databases = []Database{
{
Name: dbName,
ProjectId: os.Getenv("TF_ACC_PROJECT_ID"),
Owner: userName,
}, },
} }
resName := fmt.Sprintf( resource.ParallelTest(t, resource.TestCase{
"stackitprivatepreview_sqlserverflexalpha_instance.%s", PreCheck: func() {
data.TfName, testAccPreCheck(t)
) t.Logf(" ... working on instance %s", data.TfName)
testInstances = append(testInstances, data.TfName)
resUserName := fmt.Sprintf( },
"stackitprivatepreview_sqlserverflexalpha_user.%s",
userName,
)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
// Create and verify // Create and verify
@ -204,18 +270,71 @@ func TestAccInstanceWithUsers(t *testing.T) {
data, data,
), ),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", data.Name), // check instance values are set
resource.TestCheckResourceAttrSet(resName, "id"), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "id"),
resource.TestCheckResourceAttr(resUserName, "name", userName), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "backup_schedule"),
resource.TestCheckResourceAttrSet(resUserName, "id"), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "edition"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "flavor_id"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "instance_id"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "is_deletable"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "name"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "replicas"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "retention_days"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "status"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "version"),
resource.TestCheckNoResourceAttr(resName("instance", data.TfName), "encryption"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_id"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_version"),
//resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_ring_id"),
//resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.service_account"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.access_scope"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.acl"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.instance_address"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.router_address"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "storage.class"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "storage.size"),
// check instance values are correct
resource.TestCheckResourceAttr(resName("instance", data.TfName), "name", data.Name),
// check user values are set
resource.TestCheckResourceAttrSet(resName("user", userName), "id"),
resource.TestCheckResourceAttrSet(resName("user", userName), "username"),
// resource.TestCheckResourceAttrSet(resName("user", userName), "roles"),
// func(s *terraform.State) error {
// return nil
// },
// check user values are correct
resource.TestCheckResourceAttr(resName("user", userName), "username", userName),
resource.TestCheckResourceAttr(resName("user", userName), "roles.#", strconv.Itoa(len(data.Users[0].Roles))),
// check database values are set
resource.TestCheckResourceAttrSet(resName("database", dbName), "id"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "name"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "owner"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "compatibility"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "collation"),
// check database values are correct
resource.TestCheckResourceAttr(resName("database", dbName), "name", dbName),
resource.TestCheckResourceAttr(resName("database", dbName), "owner", userName),
), ),
}, },
}, },
}) })
} }
func TestAccInstanceWithDatabases(t *testing.T) { func TestAccInstanceEncryption(t *testing.T) {
data := getExample() data := getExample()
dbName := "testDb" dbName := "testDb"
userName := "testUser" userName := "testUser"
data.Users = []User{ data.Users = []User{
@ -233,23 +352,18 @@ func TestAccInstanceWithDatabases(t *testing.T) {
}, },
} }
resName := fmt.Sprintf( data.UseEncryption = true
"stackitprivatepreview_sqlserverflexalpha_instance.%s", data.KekKeyId = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
data.TfName, data.KekKeyRingId = "6a2d95ab-3c4c-4963-a2bb-08d17a320e27"
) data.KekKeyVersion = 1
data.KekServiceAccount = "henselinm-u2v3ex1@sa.stackit.cloud"
resUserName := fmt.Sprintf( resource.ParallelTest(t, resource.TestCase{
"stackitprivatepreview_sqlserverflexalpha_user.%s", PreCheck: func() {
userName, testAccPreCheck(t)
) t.Logf(" ... working on instance %s", data.TfName)
testInstances = append(testInstances, data.TfName)
resDbName := fmt.Sprintf( },
"stackitprivatepreview_sqlserverflexalpha_database.%s",
dbName,
)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories, ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{ Steps: []resource.TestStep{
// Create and verify // Create and verify
@ -259,13 +373,59 @@ func TestAccInstanceWithDatabases(t *testing.T) {
data, data,
), ),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", data.Name), // check instance values are set
resource.TestCheckResourceAttrSet(resName, "id"), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "id"),
resource.TestCheckResourceAttr(resUserName, "name", userName), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "backup_schedule"),
resource.TestCheckResourceAttrSet(resUserName, "id"), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "edition"),
resource.TestCheckResourceAttr(resDbName, "name", dbName), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "flavor_id"),
resource.TestCheckResourceAttr(resDbName, "owner", userName), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "instance_id"),
resource.TestCheckResourceAttrSet(resDbName, "id"), resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "is_deletable"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "name"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "replicas"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "retention_days"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "status"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "version"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_id"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_version"),
//resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.kek_key_ring_id"),
//resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "encryption.service_account"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.access_scope"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.acl"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.instance_address"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "network.router_address"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "storage.class"),
// resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "storage.size"),
// check instance values are correct
resource.TestCheckResourceAttr(resName("instance", data.TfName), "name", data.Name),
// check user values are set
resource.TestCheckResourceAttrSet(resName("user", userName), "id"),
resource.TestCheckResourceAttrSet(resName("user", userName), "username"),
// func(s *terraform.State) error {
// return nil
// },
// check user values are correct
resource.TestCheckResourceAttr(resName("user", userName), "username", userName),
resource.TestCheckResourceAttr(resName("user", userName), "roles.#", "2"),
// check database values are set
resource.TestCheckResourceAttrSet(resName("database", dbName), "id"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "name"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "owner"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "compatibility"),
resource.TestCheckResourceAttrSet(resName("database", dbName), "collation"),
// check database values are correct
resource.TestCheckResourceAttr(resName("database", dbName), "name", dbName),
resource.TestCheckResourceAttr(resName("database", dbName), "owner", userName),
), ),
}, },
}, },

View file

@ -1,483 +0,0 @@
// Copyright (c) STACKIT
package sqlserverflexalpha_test
import (
"context"
_ "embed"
"fmt"
"maps"
"strings"
"testing"
"github.com/hashicorp/terraform-plugin-testing/config"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
coreconfig "github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/wait"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
)
var (
//go:embed testdata/resource-max.tf
resourceMaxConfig string
//go:embed testdata/resource-min.tf
resourceMinConfig string
)
var testConfigVarsMin = config.Variables{
"project_id": config.StringVariable(testutils.ProjectId),
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
"flavor_cpu": config.IntegerVariable(4),
"flavor_ram": config.IntegerVariable(16),
"flavor_description": config.StringVariable("SQLServer-Flex-4.16-Standard-EU01"),
"replicas": config.IntegerVariable(1),
"flavor_id": config.StringVariable("4.16-Single"),
"username": config.StringVariable(fmt.Sprintf("tf-acc-user-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlpha))),
"role": config.StringVariable("##STACKIT_LoginManager##"),
}
var testConfigVarsMax = config.Variables{
"project_id": config.StringVariable(testutils.ProjectId),
"name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
"acl1": config.StringVariable("192.168.0.0/16"),
"flavor_cpu": config.IntegerVariable(4),
"flavor_ram": config.IntegerVariable(16),
"flavor_description": config.StringVariable("SQLServer-Flex-4.16-Standard-EU01"),
"storage_class": config.StringVariable("premium-perf2-stackit"),
"storage_size": config.IntegerVariable(40),
"server_version": config.StringVariable("2022"),
"replicas": config.IntegerVariable(1),
"options_retention_days": config.IntegerVariable(64),
"flavor_id": config.StringVariable("4.16-Single"),
"backup_schedule": config.StringVariable("00 6 * * *"),
"username": config.StringVariable(fmt.Sprintf("tf-acc-user-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlpha))),
"role": config.StringVariable("##STACKIT_LoginManager##"),
"region": config.StringVariable(testutils.Region),
}
func configVarsMinUpdated() config.Variables {
temp := maps.Clone(testConfigVarsMax)
temp["name"] = config.StringVariable(testutils.ConvertConfigVariable(temp["name"]) + "changed")
return temp
}
func configVarsMaxUpdated() config.Variables {
temp := maps.Clone(testConfigVarsMax)
temp["backup_schedule"] = config.StringVariable("00 12 * * *")
return temp
}
func TestAccSQLServerFlexMinResource(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
CheckDestroy: testAccChecksqlserverflexDestroy,
Steps: []resource.TestStep{
// Creation
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMin["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_description"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutils.ConvertConfigVariable(testConfigVarsMin["replicas"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_ram"])),
// User
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "password"),
),
},
// Update
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMin["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_description"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_ram"])),
// User
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "password"),
),
},
// data source
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMin["name"])),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_instance.instance", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_instance.instance", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_user.user", "instance_id",
),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.id", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_id"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_description"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_cpu"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMin["flavor_ram"])),
// User data
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "project_id", testutils.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "username", testutils.ConvertConfigVariable(testConfigVarsMin["username"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.#", "1"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.0", testutils.ConvertConfigVariable(testConfigVarsMax["role"])),
),
},
// Import
{
ConfigVariables: testConfigVarsMin,
ResourceName: "stackit_sqlserverflex_instance.instance",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_sqlserverflex_instance.instance"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_instance.instance")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
return fmt.Sprintf("%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"backup_schedule"},
ImportStateCheck: func(s []*terraform.InstanceState) error {
if len(s) != 1 {
return fmt.Errorf("expected 1 state, got %d", len(s))
}
return nil
},
},
{
ResourceName: "stackit_sqlserverflex_user.user",
ConfigVariables: testConfigVarsMin,
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_sqlserverflex_user.user"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_user.user")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
userId, ok := r.Primary.Attributes["user_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute user_id")
}
return fmt.Sprintf("%s,%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId, userId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password"},
},
// Update
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: configVarsMinUpdated(),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(configVarsMinUpdated()["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(configVarsMinUpdated()["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.description"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(configVarsMinUpdated()["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(configVarsMinUpdated()["flavor_ram"])),
),
},
// Deletion is done by the framework implicitly
},
})
}
func TestAccSQLServerFlexMaxResource(t *testing.T) {
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
CheckDestroy: testAccChecksqlserverflexDestroy,
Steps: []resource.TestStep{
// Creation
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMax["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMax["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", testutils.ConvertConfigVariable(testConfigVarsMax["acl1"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_description"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutils.ConvertConfigVariable(testConfigVarsMax["replicas"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_ram"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", testutils.ConvertConfigVariable(testConfigVarsMax["storage_class"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", testutils.ConvertConfigVariable(testConfigVarsMax["storage_size"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", testutils.ConvertConfigVariable(testConfigVarsMax["server_version"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", testutils.ConvertConfigVariable(testConfigVarsMax["options_retention_days"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", testutils.ConvertConfigVariable(testConfigVarsMax["backup_schedule"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "region", testutils.Region),
// User
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "password"),
),
},
// Update
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMax["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMax["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", testutils.ConvertConfigVariable(testConfigVarsMax["acl1"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_description"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutils.ConvertConfigVariable(testConfigVarsMax["replicas"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_ram"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", testutils.ConvertConfigVariable(testConfigVarsMax["storage_class"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", testutils.ConvertConfigVariable(testConfigVarsMax["storage_size"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", testutils.ConvertConfigVariable(testConfigVarsMax["server_version"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", testutils.ConvertConfigVariable(testConfigVarsMax["options_retention_days"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", testutils.ConvertConfigVariable(testConfigVarsMax["backup_schedule"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "region", testutils.Region),
// User
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_user.user", "password"),
),
},
// data source
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(testConfigVarsMax["project_id"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(testConfigVarsMax["name"])),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_instance.instance", "project_id",
"stackit_sqlserverflex_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_instance.instance", "instance_id",
"stackit_sqlserverflex_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrPair(
"data.stackit_sqlserverflex_user.user", "instance_id",
"stackit_sqlserverflex_user.user", "instance_id",
),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.0", testutils.ConvertConfigVariable(testConfigVarsMax["acl1"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.id", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_id"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.description", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_description"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_cpu"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(testConfigVarsMax["flavor_ram"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "replicas", testutils.ConvertConfigVariable(testConfigVarsMax["replicas"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", testutils.ConvertConfigVariable(testConfigVarsMax["options_retention_days"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "backup_schedule", testutils.ConvertConfigVariable(testConfigVarsMax["backup_schedule"])),
// User data
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "project_id", testutils.ConvertConfigVariable(testConfigVarsMax["project_id"])),
resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "user_id"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "username", testutils.ConvertConfigVariable(testConfigVarsMax["username"])),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.#", "1"),
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_user.user", "roles.0", testutils.ConvertConfigVariable(testConfigVarsMax["role"])),
resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "host"),
resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_user.user", "port"),
),
},
// Import
{
ConfigVariables: testConfigVarsMax,
ResourceName: "stackit_sqlserverflex_instance.instance",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_sqlserverflex_instance.instance"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_instance.instance")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
return fmt.Sprintf("%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"backup_schedule"},
ImportStateCheck: func(s []*terraform.InstanceState) error {
if len(s) != 1 {
return fmt.Errorf("expected 1 state, got %d", len(s))
}
if s[0].Attributes["backup_schedule"] != testutils.ConvertConfigVariable(testConfigVarsMax["backup_schedule"]) {
return fmt.Errorf("expected backup_schedule %s, got %s", testConfigVarsMax["backup_schedule"], s[0].Attributes["backup_schedule"])
}
return nil
},
},
{
ResourceName: "stackit_sqlserverflex_user.user",
ConfigVariables: testConfigVarsMax,
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_sqlserverflex_user.user"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_user.user")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
userId, ok := r.Primary.Attributes["user_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute user_id")
}
return fmt.Sprintf("%s,%s,%s,%s", testutils.ProjectId, testutils.Region, instanceId, userId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password"},
},
// Update
{
Config: testutils.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: configVarsMaxUpdated(),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", testutils.ConvertConfigVariable(configVarsMaxUpdated()["project_id"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", testutils.ConvertConfigVariable(configVarsMaxUpdated()["name"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", testutils.ConvertConfigVariable(configVarsMaxUpdated()["acl1"])),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.description"),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", testutils.ConvertConfigVariable(configVarsMaxUpdated()["flavor_cpu"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", testutils.ConvertConfigVariable(configVarsMaxUpdated()["flavor_ram"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", testutils.ConvertConfigVariable(configVarsMaxUpdated()["replicas"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", testutils.ConvertConfigVariable(configVarsMaxUpdated()["storage_class"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", testutils.ConvertConfigVariable(configVarsMaxUpdated()["storage_size"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", testutils.ConvertConfigVariable(configVarsMaxUpdated()["server_version"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", testutils.ConvertConfigVariable(configVarsMaxUpdated()["options_retention_days"])),
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", testutils.ConvertConfigVariable(configVarsMaxUpdated()["backup_schedule"])),
),
},
// Deletion is done by the framework implicitly
},
})
}
func testAccChecksqlserverflexDestroy(s *terraform.State) error {
ctx := context.Background()
var client *sqlserverflex.APIClient
var err error
if testutils.SQLServerFlexCustomEndpoint == "" {
client, err = sqlserverflex.NewAPIClient()
} else {
client, err = sqlserverflex.NewAPIClient(
coreconfig.WithEndpoint(testutils.SQLServerFlexCustomEndpoint),
)
}
if err != nil {
return fmt.Errorf("creating client: %w", err)
}
instancesToDestroy := []string{}
for _, rs := range s.RootModule().Resources {
if rs.Type != "stackit_sqlserverflex_instance" {
continue
}
// instance terraform ID: = "[project_id],[region],[instance_id]"
instanceId := strings.Split(rs.Primary.ID, core.Separator)[2]
instancesToDestroy = append(instancesToDestroy, instanceId)
}
instancesResp, err := client.ListInstances(ctx, testutils.ProjectId, testutils.Region).Execute()
if err != nil {
return fmt.Errorf("getting instancesResp: %w", err)
}
items := *instancesResp.Items
for i := range items {
if items[i].Id == nil {
continue
}
if utils.Contains(instancesToDestroy, *items[i].Id) {
err := client.DeleteInstanceExecute(ctx, testutils.ProjectId, *items[i].Id, testutils.Region)
if err != nil {
return fmt.Errorf("destroying instance %s during CheckDestroy: %w", *items[i].Id, err)
}
_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutils.ProjectId, *items[i].Id, testutils.Region).WaitWithContext(ctx)
if err != nil {
return fmt.Errorf("destroying instance %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err)
}
}
}
return nil
}

View file

@ -15,8 +15,8 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "{{ .TfName }}" {
} }
{{ if .UseEncryption }} {{ if .UseEncryption }}
encryption = { encryption = {
kek_key_id = {{ .KekKeyId }} kek_key_id = "{{ .KekKeyId }}"
kek_key_ring_id = {{ .KekKeyRingId }} kek_key_ring_id = "{{ .KekKeyRingId }}"
kek_key_version = {{ .KekKeyVersion }} kek_key_version = {{ .KekKeyVersion }}
service_account = "{{ .KekServiceAccount }}" service_account = "{{ .KekServiceAccount }}"
} }
@ -44,10 +44,17 @@ resource "stackitprivatepreview_sqlserverflexalpha_user" "{{ $user.Name }}" {
{{ $tfName := .TfName }} {{ $tfName := .TfName }}
{{ range $db := .Databases }} {{ range $db := .Databases }}
resource "stackitprivatepreview_sqlserverflexalpha_database" "{{ $db.Name }}" { resource "stackitprivatepreview_sqlserverflexalpha_database" "{{ $db.Name }}" {
project_id = "{{ $db.ProjectId }}" depends_on = [stackitprivatepreview_sqlserverflexalpha_user.{{ $db.Owner }}]
instance_id = stackitprivatepreview_sqlserverflexalpha_instance.{{ $tfName }}.instance_id project_id = "{{ $db.ProjectId }}"
name = "{{ $db.Name }}" instance_id = stackitprivatepreview_sqlserverflexalpha_instance.{{ $tfName }}.instance_id
owner = "{{ $db.Owner }}" name = "{{ $db.Name }}"
owner = "{{ $db.Owner }}"
{{ if $db.Collation }}
collation = "{{ $db.Collation }}"
{{ end }}
{{ if $db.Compatibility }}
compatibility = "{{ $db.Compatibility }}"
{{ end }}
} }
{{ end }} {{ end }}
{{ end }} {{ end }}

View file

@ -5,54 +5,47 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils" sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate"
sqlserverflexalphaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/datasources_gen"
) )
// Ensure the implementation satisfies the expected interfaces. var _ datasource.DataSource = (*userDataSource)(nil)
var (
_ datasource.DataSource = &userDataSource{} const errorPrefix = "[sqlserverflexalpha - User]"
)
// NewUserDataSource is a helper function to simplify the provider implementation.
func NewUserDataSource() datasource.DataSource { func NewUserDataSource() datasource.DataSource {
return &userDataSource{} return &userDataSource{}
} }
type dataSourceModel struct { type dataSourceModel struct {
//TODO: check generated data source for the correct types and pointers
Id types.String `tfsdk:"id"` // needed by TF
UserId types.Int64 `tfsdk:"user_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Username types.String `tfsdk:"username"`
Roles types.Set `tfsdk:"roles"`
Host types.String `tfsdk:"host"`
Port types.Int64 `tfsdk:"port"`
Region types.String `tfsdk:"region"`
Status types.String `tfsdk:"status"`
DefaultDatabase types.String `tfsdk:"default_database"` DefaultDatabase types.String `tfsdk:"default_database"`
Host types.String `tfsdk:"host"`
Id types.String `tfsdk:"id"`
InstanceId types.String `tfsdk:"instance_id"`
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"`
Username types.String `tfsdk:"username"`
} }
// userDataSource is the data source implementation.
type userDataSource struct { type userDataSource struct {
client *sqlserverflexalpha.APIClient client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
// Metadata returns the data source type name. func (d *userDataSource) Metadata(
func (r *userDataSource) Metadata(
_ context.Context, _ context.Context,
req datasource.MetadataRequest, req datasource.MetadataRequest,
resp *datasource.MetadataResponse, resp *datasource.MetadataResponse,
@ -60,108 +53,31 @@ func (r *userDataSource) Metadata(
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_user" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_user"
} }
func (d *userDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = sqlserverflexalphaGen.UserDataSourceSchema(ctx)
}
// Configure adds the provider configured client to the data source. // Configure adds the provider configured client to the data source.
func (r *userDataSource) Configure( func (d *userDataSource) Configure(
ctx context.Context, ctx context.Context,
req datasource.ConfigureRequest, req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse, resp *datasource.ConfigureResponse,
) { ) {
var ok bool var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok { if !ok {
return return
} }
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics) apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
r.client = apiClient d.client = apiClient
tflog.Info(ctx, "SQLServer Flex beta user client configured") tflog.Info(ctx, "SQL SERVER Flex alpha database client configured")
} }
// Schema defines the schema for the data source. func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
descriptions := map[string]string{
"main": "SQLServer Flex user 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`,`user_id`\".",
"user_id": "User ID.",
"instance_id": "ID of the SQLServer Flex instance.",
"project_id": "STACKIT project ID to which the instance is associated.",
"username": "Username of the SQLServer Flex instance.",
"roles": "Database access levels for the user.",
"password": "Password of the user account.",
"region": "The resource region. If not defined, the provider region is used.",
"status": "Status of the user.",
"default_database": "Default database of the user.",
}
resp.Schema = schema.Schema{
Description: descriptions["main"],
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: descriptions["id"],
Computed: true,
},
"user_id": schema.Int64Attribute{
Description: descriptions["user_id"],
Required: true,
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"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(),
},
},
"username": schema.StringAttribute{
Description: descriptions["username"],
Computed: true,
},
"roles": schema.SetAttribute{
Description: descriptions["roles"],
ElementType: types.StringType,
Computed: true,
},
"host": schema.StringAttribute{
Computed: true,
},
"port": schema.Int64Attribute{
Computed: true,
},
"region": schema.StringAttribute{
// the region cannot be found automatically, so it has to be passed
Optional: true,
Description: descriptions["region"],
},
"status": schema.StringAttribute{
Computed: true,
},
"default_database": schema.StringAttribute{
Computed: true,
},
},
}
}
// Read refreshes the Terraform state with the latest data.
func (r *userDataSource) Read(
ctx context.Context,
req datasource.ReadRequest,
resp *datasource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform
var model dataSourceModel var model dataSourceModel
diags := req.Config.Get(ctx, &model) diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
@ -174,13 +90,13 @@ func (r *userDataSource) Read(
projectId := model.ProjectId.ValueString() projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString() instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueInt64() userId := model.UserId.ValueInt64()
region := r.providerData.GetRegionWithOverride(model.Region) region := d.providerData.GetRegionWithOverride(model.Region)
ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId) ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId) ctx = tflog.SetField(ctx, "user_id", userId)
ctx = tflog.SetField(ctx, "region", region) ctx = tflog.SetField(ctx, "region", region)
recordSetResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute() recordSetResp, err := d.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
if err != nil { if err != nil {
utils.LogError( utils.LogError(
ctx, ctx,
@ -221,5 +137,5 @@ func (r *userDataSource) Read(
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
tflog.Info(ctx, "SQLServer Flex alpha instance read") tflog.Info(ctx, "SQLServer Flex beta instance read")
} }

View file

@ -42,7 +42,7 @@ func mapDataSourceFields(userResp *sqlserverflexalpha.GetUserResponse, model *da
// Map roles // Map roles
if user.Roles == nil { if user.Roles == nil {
model.Roles = types.SetNull(types.StringType) model.Roles = types.List(types.SetNull(types.StringType))
} else { } else {
var roles []attr.Value var roles []attr.Value
for _, role := range *user.Roles { for _, role := range *user.Roles {
@ -52,7 +52,7 @@ func mapDataSourceFields(userResp *sqlserverflexalpha.GetUserResponse, model *da
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags)) return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
} }
model.Roles = rolesSet model.Roles = types.List(rolesSet)
} }
// Set remaining attributes // Set remaining attributes
@ -141,7 +141,7 @@ func mapFieldsCreate(userResp *sqlserverflexalpha.CreateUserResponse, model *res
if user.Roles != nil { if user.Roles != nil {
var roles []attr.Value var roles []attr.Value
for _, role := range *user.Roles { for _, role := range *user.Roles {
roles = append(roles, types.StringValue(role)) roles = append(roles, types.StringValue(string(role)))
} }
rolesSet, diags := types.SetValue(types.StringType, roles) rolesSet, diags := types.SetValue(types.StringType, roles)
if diags.HasError() { if diags.HasError() {
@ -154,6 +154,9 @@ func mapFieldsCreate(userResp *sqlserverflexalpha.CreateUserResponse, model *res
model.Roles = types.List(types.SetNull(types.StringType)) model.Roles = types.List(types.SetNull(types.StringType))
} }
model.Password = types.StringPointerValue(user.Password)
model.Uri = types.StringPointerValue(user.Uri)
model.Host = types.StringPointerValue(user.Host) model.Host = types.StringPointerValue(user.Host)
model.Port = types.Int64PointerValue(user.Port) model.Port = types.Int64PointerValue(user.Port)
model.Region = types.StringValue(region) model.Region = types.StringValue(region)

View file

@ -30,7 +30,7 @@ func TestMapDataSourceFields(t *testing.T) {
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Username: types.StringNull(), Username: types.StringNull(),
Roles: types.SetNull(types.StringType), Roles: types.List(types.SetNull(types.StringType)),
Host: types.StringNull(), Host: types.StringNull(),
Port: types.Int64Null(), Port: types.Int64Null(),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
@ -60,12 +60,14 @@ func TestMapDataSourceFields(t *testing.T) {
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Username: types.StringValue("username"), Username: types.StringValue("username"),
Roles: types.SetValueMust( Roles: types.List(
types.StringType, []attr.Value{ types.SetValueMust(
types.StringValue("role_1"), types.StringType, []attr.Value{
types.StringValue("role_2"), types.StringValue("role_1"),
types.StringValue(""), types.StringValue("role_2"),
}, types.StringValue(""),
},
),
), ),
Host: types.StringValue("host"), Host: types.StringValue("host"),
Port: types.Int64Value(1234), Port: types.Int64Value(1234),
@ -91,7 +93,7 @@ func TestMapDataSourceFields(t *testing.T) {
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Username: types.StringNull(), Username: types.StringNull(),
Roles: types.SetValueMust(types.StringType, []attr.Value{}), Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
Host: types.StringNull(), Host: types.StringNull(),
Port: types.Int64Value(2123456789), Port: types.Int64Value(2123456789),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),

View file

@ -3,49 +3,40 @@ fields:
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- name: 'user_id'
modifiers:
- 'UseStateForUnknown'
- name: 'instance_id' - name: 'instance_id'
validators: validators:
- validate.NoSeparator - validate.NoSeparator
- validate.UUID - validate.UUID
modifiers: modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace' - 'RequiresReplace'
- name: 'project_id' - name: 'project_id'
validators: validators:
- validate.NoSeparator - validate.NoSeparator
- validate.UUID - validate.UUID
modifiers:
- 'RequiresReplace'
- name: 'username'
modifiers:
- 'RequiresReplace'
- name: 'roles'
modifiers:
- 'RequiresReplace'
- name: 'password'
modifiers:
- 'UseStateForUnknown'
- name: 'host'
modifiers:
- 'UseStateForUnknown'
- name: 'port'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'region' - name: 'region'
modifiers: modifiers:
- 'RequiresReplace' - 'RequiresReplace'
- name: 'status' - name: 'user_id'
modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'username'
modifiers:
- 'UseStateForUnknown'
- name: 'roles'
modifiers:
- 'UseStateForUnknown'
- name: 'password'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
@ -53,6 +44,6 @@ fields:
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'
- name: 'default_database' - name: 'status'
modifiers: modifiers:
- 'UseStateForUnknown' - 'UseStateForUnknown'

View file

@ -8,26 +8,27 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
sqlserverflexalphagen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/resources_gen"
sqlserverflexalphaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
sqlserverflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
"github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror" "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
sqlserverflexalphagen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/resources_gen"
sqlserverflexalphaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
sqlserverflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils" "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
sqlserverflexalphaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/resources_gen"
) )
// Ensure the implementation satisfies the expected interfaces.
var ( var (
_ resource.Resource = &userResource{} _ resource.Resource = &userResource{}
_ resource.ResourceWithConfigure = &userResource{} _ resource.ResourceWithConfigure = &userResource{}
@ -36,13 +37,12 @@ var (
_ resource.ResourceWithIdentity = &userResource{} _ resource.ResourceWithIdentity = &userResource{}
) )
// NewUserResource is a helper function to simplify the provider implementation.
func NewUserResource() resource.Resource { func NewUserResource() resource.Resource {
return &userResource{} return &userResource{}
} }
// resourceModel describes the resource data model. // resourceModel describes the resource data model.
type resourceModel = sqlserverflexalphagen.UserModel type resourceModel = sqlserverflexalphaResGen.UserModel
// UserResourceIdentityModel describes the resource's identity attributes. // UserResourceIdentityModel describes the resource's identity attributes.
type UserResourceIdentityModel struct { type UserResourceIdentityModel struct {
@ -52,14 +52,12 @@ type UserResourceIdentityModel struct {
UserID types.Int64 `tfsdk:"user_id"` UserID types.Int64 `tfsdk:"user_id"`
} }
// userResource is the resource implementation.
type userResource struct { type userResource struct {
client *sqlserverflexalpha.APIClient client *sqlserverflexalpha.APIClient
providerData core.ProviderData providerData core.ProviderData
} }
// Metadata returns the resource type name. func (r *userResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
func (r *userResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_user" resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_user"
} }
@ -76,7 +74,7 @@ func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequ
return return
} }
r.client = apiClient r.client = apiClient
tflog.Info(ctx, "SQLServer Alpha Flex user client configured") tflog.Info(ctx, "SQLServer Beta Flex user client configured")
} }
// ModifyPlan implements resource.ResourceWithModifyPlan. // ModifyPlan implements resource.ResourceWithModifyPlan.
@ -219,6 +217,7 @@ func (r *userResource) Create(
) )
return return
} }
userId := *userResp.Id userId := *userResp.Id
ctx = tflog.SetField(ctx, "user_id", userId) ctx = tflog.SetField(ctx, "user_id", userId)
@ -227,14 +226,13 @@ func (r *userResource) Create(
ProjectID: types.StringValue(projectId), ProjectID: types.StringValue(projectId),
Region: types.StringValue(region), Region: types.StringValue(region),
InstanceID: types.StringValue(instanceId), InstanceID: types.StringValue(instanceId),
UserID: types.Int64Value(userResp.GetId()), UserID: types.Int64Value(userId),
} }
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...) resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() { if resp.Diagnostics.HasError() {
return return
} }
// Map response body to schema
err = mapFieldsCreate(userResp, &model, region) err = mapFieldsCreate(userResp, &model, region)
if err != nil { if err != nil {
core.LogAndAddError( core.LogAndAddError(
@ -245,6 +243,51 @@ func (r *userResource) Create(
) )
return return
} }
waitResp, err := sqlserverflexalphaWait.CreateUserWaitHandler(
ctx,
r.client,
projectId,
instanceId,
region,
userId,
).SetSleepBeforeWait(
90 * time.Second,
).SetTimeout(
90 * time.Minute,
).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"create user",
fmt.Sprintf("Instance creation waiting: %v", err),
)
return
}
if waitResp.Id == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"create user",
"Instance creation waiting: returned id is nil",
)
return
}
// Map response body to schema
err = mapFields(waitResp, &model, region)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating user",
fmt.Sprintf("Processing API payload: %v", err),
)
return
}
// Set state to fully populated data // Set state to fully populated data
diags = resp.State.Set(ctx, model) diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
@ -296,18 +339,6 @@ func (r *userResource) Read(
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
// Set data returned by API in identity
identity := UserResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
InstanceID: types.StringValue(instanceId),
UserID: types.Int64Value(userId),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// Map response body to schema // Map response body to schema
err = mapFields(recordSetResp, &model, region) err = mapFields(recordSetResp, &model, region)
if err != nil { if err != nil {
@ -320,6 +351,18 @@ func (r *userResource) Read(
return return
} }
// Set data returned by API in identity
identity := UserResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
InstanceID: types.StringValue(instanceId),
UserID: types.Int64Value(userId),
}
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
if resp.Diagnostics.HasError() {
return
}
// Set refreshed state // Set refreshed state
diags = resp.State.Set(ctx, model) diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...) resp.Diagnostics.Append(diags...)
@ -366,9 +409,29 @@ func (r *userResource) Delete(
// Delete existing record set // Delete existing record set
// err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute() // err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
err := r.client.DeleteUserRequestExecute(ctx, projectId, region, instanceId, userId)
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
// TODO err handling
return
}
switch oapiErr.StatusCode {
case http.StatusNotFound:
resp.State.RemoveResource(ctx)
return
// case http.StatusInternalServerError:
// tflog.Warn(ctx, "[delete user] Wait handler got error 500")
// return false, nil, nil
default:
// TODO err handling
return
}
}
// Delete existing record set // Delete existing record set
_, err := sqlserverflexalphaWait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId). _, err = sqlserverflexalphaWait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId).
WaitWithContext(ctx) WaitWithContext(ctx)
// err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute() // err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute()
if err != nil { if err != nil {
@ -377,6 +440,7 @@ func (r *userResource) Delete(
} }
ctx = core.LogResponse(ctx) ctx = core.LogResponse(ctx)
resp.State.RemoveResource(ctx) resp.State.RemoveResource(ctx)
tflog.Info(ctx, "SQLServer Flex user deleted") tflog.Info(ctx, "SQLServer Flex user deleted")
@ -422,7 +486,7 @@ func (r *userResource) ImportState(
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), userId)...) resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), userId)...)
tflog.Info(ctx, "Postgres Flex user state imported") tflog.Info(ctx, "SQLServer Flex user state imported")
return return
} }

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package sqlserverflexalpha package sqlserverflexalpha
import ( import (
@ -28,145 +26,355 @@ const (
InstanceStateTerminating = "TERMINATING" InstanceStateTerminating = "TERMINATING"
) )
// APIClientInstanceInterface Interface needed for tests // APIClientInterface Interface needed for tests
type APIClientInstanceInterface interface { type APIClientInterface interface {
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error) GetInstanceRequestExecute(
ctx context.Context,
projectId, region, instanceId string,
) (*sqlserverflex.GetInstanceResponse, error)
GetDatabaseRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
databaseName string,
) (*sqlserverflex.GetDatabaseResponse, error)
GetUserRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
userId int64,
) (*sqlserverflex.GetUserResponse, error)
ListRolesRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
) (*sqlserverflex.ListRolesResponse, error)
ListUsersRequest(ctx context.Context, projectId string, region string, instanceId string) sqlserverflex.ApiListUsersRequestRequest
ListUsersRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
) (*sqlserverflex.ListUserResponse, error)
} }
// APIClientUserInterface Interface needed for tests // APIClientUserInterface Interface needed for tests
type APIClientUserInterface interface { type APIClientUserInterface interface {
DeleteUserRequestExecute(ctx context.Context, projectId string, region string, instanceId string, userId int64) error DeleteUserRequestExecute(
ctx context.Context,
projectId string,
region string,
instanceId string,
userId int64,
) error
} }
// CreateInstanceWaitHandler will wait for instance creation // CreateInstanceWaitHandler will wait for instance creation
func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] { func CreateInstanceWaitHandler(
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) { ctx context.Context,
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) a APIClientInterface,
if err != nil { projectId, instanceId, region string,
// TODO: catch 502, 503 and 504 ) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
return false, nil, err handler := wait.New(
} func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
return false, nil, nil if err != nil {
} return false, nil, err
switch strings.ToLower(string(*s.Status)) {
case strings.ToLower(InstanceStateSuccess):
if *s.Network.AccessScope == "SNA" {
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 if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): return false, nil, nil
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) }
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing): switch strings.ToLower(string(*s.Status)) {
tflog.Info(ctx, "request is being handled", map[string]interface{}{ case strings.ToLower(InstanceStateSuccess):
"status": *s.Status, if s.Network != nil && s.Network.AccessScope != nil && *s.Network.AccessScope == "SNA" {
}) if s.Network.InstanceAddress == nil {
return false, nil, nil tflog.Info(ctx, "Waiting for instance_address")
default: return false, nil, nil
tflog.Info(ctx, "Wait (create) received unknown status", map[string]interface{}{ }
"instanceId": instanceId, if s.Network.RouterAddress == nil {
"status": s.Status, tflog.Info(ctx, "Waiting for router_address")
}) return false, nil, nil
return false, s, nil }
} }
})
tflog.Info(ctx, "trying to get roles")
time.Sleep(10 * time.Second)
_, rolesErr := a.ListRolesRequestExecute(ctx, projectId, region, instanceId)
if rolesErr != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(rolesErr, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
if oapiErr.StatusCode != http.StatusInternalServerError {
tflog.Info(
ctx, "got error from api", map[string]interface{}{
"error": rolesErr.Error(),
},
)
return false, nil, rolesErr
}
tflog.Info(
ctx, "wait for get-roles to work hack", map[string]interface{}{},
)
time.Sleep(10 * time.Second)
return false, nil, nil
}
tflog.Info(ctx, "trying to get users")
time.Sleep(10 * time.Second)
_, usersErr := a.ListUsersRequestExecute(ctx, projectId, region, instanceId)
if usersErr != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(usersErr, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
if oapiErr.StatusCode != http.StatusInternalServerError {
tflog.Info(
ctx, "got error from api", map[string]interface{}{
"error": rolesErr.Error(),
},
)
return false, nil, usersErr
}
tflog.Info(
ctx, "wait for get-users to work hack", map[string]interface{}{},
)
time.Sleep(10 * time.Second)
return false, nil, nil
}
return true, s, nil
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return true, nil, fmt.Errorf("create failed for instance with id %s", instanceId)
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
tflog.Info(
ctx, "request is being handled", map[string]interface{}{
"status": *s.Status,
},
)
time.Sleep(10 * time.Second)
return false, nil, nil
default:
tflog.Info(
ctx, "Wait (create) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
},
)
return true, nil, errors.New("unknown status received")
}
},
)
return handler return handler
} }
// UpdateInstanceWaitHandler will wait for instance update // UpdateInstanceWaitHandler will wait for instance update
func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] { func UpdateInstanceWaitHandler(
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) { ctx context.Context,
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) a APIClientInterface,
if err != nil { projectId, instanceId, region string,
return false, nil, err ) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
} handler := wait.New(
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil { func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
return false, nil, nil s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
} if err != nil {
switch strings.ToLower(string(*s.Status)) { return false, nil, err
case strings.ToLower(InstanceStateSuccess): }
return true, s, nil if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed): return false, nil, nil
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) }
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing): switch strings.ToLower(string(*s.Status)) {
tflog.Info(ctx, "request is being handled", map[string]interface{}{ case strings.ToLower(InstanceStateSuccess):
"status": *s.Status, return true, s, nil
}) case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
return false, nil, nil return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
default: case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
tflog.Info(ctx, "Wait (update) received unknown status", map[string]interface{}{ tflog.Info(
"instanceId": instanceId, ctx, "request is being handled", map[string]interface{}{
"status": s.Status, "status": *s.Status,
}) },
return false, s, nil )
} return false, s, nil
}) default:
handler.SetSleepBeforeWait(15 * time.Second) tflog.Info(
handler.SetTimeout(45 * time.Minute) ctx, "Wait (update) received unknown status", map[string]interface{}{
"instanceId": instanceId,
"status": s.Status,
},
)
return false, s, nil
}
},
)
return handler return handler
} }
// PartialUpdateInstanceWaitHandler will wait for instance update // DeleteInstanceWaitHandler will wait for instance deletion
func PartialUpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] { func DeleteInstanceWaitHandler(
return UpdateInstanceWaitHandler(ctx, a, projectId, instanceId, region) ctx context.Context,
a APIClientInterface,
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, s, 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(30 * time.Minute)
return handler
} }
// DeleteInstanceWaitHandler will wait for instance deletion // CreateDatabaseWaitHandler will wait for instance creation
func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[struct{}] { func CreateDatabaseWaitHandler(
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { ctx context.Context,
_, err = a.GetInstanceRequestExecute(ctx, projectId, region, instanceId) a APIClientInterface,
if err == nil { projectId, instanceId, region, databaseName string,
) *wait.AsyncActionHandler[sqlserverflex.GetDatabaseResponse] {
handler := wait.New(
func() (waitFinished bool, response *sqlserverflex.GetDatabaseResponse, err error) {
s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("get database - could not convert error to oapierror.GenericOpenAPIError: %s", err.Error())
}
if oapiErr.StatusCode != http.StatusNotFound {
return false, nil, err
}
return false, nil, nil
}
if s == nil || s.Name == nil || *s.Name != databaseName {
return false, nil, errors.New("response did return different result")
}
return true, s, nil
},
)
return handler
}
// CreateUserWaitHandler will wait for instance creation
func CreateUserWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region string,
userId int64,
) *wait.AsyncActionHandler[sqlserverflex.GetUserResponse] {
handler := wait.New(
func() (waitFinished bool, response *sqlserverflex.GetUserResponse, err error) {
s, err := a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId)
if err != 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 false, nil, nil
}
return true, s, nil
},
)
return handler
}
// WaitForUserWaitHandler will wait for instance creation
func WaitForUserWaitHandler(
ctx context.Context,
a APIClientInterface,
projectId, instanceId, region, userName string,
) *wait.AsyncActionHandler[sqlserverflex.ListUserResponse] {
startTime := time.Now()
timeOut := 2 * time.Minute
handler := wait.New(
func() (waitFinished bool, response *sqlserverflex.ListUserResponse, err error) {
if time.Since(startTime) > timeOut {
return false, nil, errors.New("ran into timeout")
}
s, err := a.ListUsersRequest(ctx, projectId, region, instanceId).Size(100).Execute()
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if !ok {
return false, nil, fmt.Errorf("Wait (list users) could not convert error to oapierror.GenericOpenAPIError: %s", err.Error())
}
if oapiErr.StatusCode != http.StatusNotFound {
return false, nil, err
}
tflog.Info(
ctx, "Wait (list users) still waiting", map[string]interface{}{},
)
return false, nil, nil
}
users, ok := s.GetUsersOk()
if !ok {
return false, nil, errors.New("no users found")
}
for _, u := range users {
if u.GetUsername() == userName {
return true, s, nil
}
}
tflog.Info(
ctx, "Wait (list users) user still not present", map[string]interface{}{},
)
return false, nil, 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 return handler
} }
// DeleteUserWaitHandler will wait for instance deletion // DeleteUserWaitHandler will wait for instance deletion
func DeleteUserWaitHandler( func DeleteUserWaitHandler(
ctx context.Context, ctx context.Context,
a APIClientUserInterface, a APIClientInterface,
projectId, instanceId, region string, projectId, region, instanceId string,
userId int64, userId int64,
) *wait.AsyncActionHandler[struct{}] { ) *wait.AsyncActionHandler[struct{}] {
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { handler := wait.New(
err = a.DeleteUserRequestExecute(ctx, projectId, region, instanceId, userId) func() (waitFinished bool, response *struct{}, err error) {
if err == nil { _, err = a.GetUserRequestExecute(ctx, projectId, region, instanceId, userId)
return false, nil, nil if err == nil {
} return false, nil, nil
var oapiErr *oapierror.GenericOpenAPIError }
ok := errors.As(err, &oapiErr) var oapiErr *oapierror.GenericOpenAPIError
if !ok { ok := errors.As(err, &oapiErr)
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") if !ok {
} return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
}
switch oapiErr.StatusCode { switch oapiErr.StatusCode {
case http.StatusNotFound: case http.StatusNotFound:
return true, nil, nil return true, nil, nil
case http.StatusInternalServerError: default:
tflog.Warn(ctx, "Wait handler got error 500") return false, nil, err
return false, nil, nil }
default: },
return false, nil, err )
}
})
handler.SetTimeout(15 * time.Minute) handler.SetTimeout(15 * time.Minute)
handler.SetSleepBeforeWait(15 * time.Second) handler.SetSleepBeforeWait(15 * time.Second)
return handler return handler

View file

@ -1,9 +1,8 @@
// Copyright (c) STACKIT
package sqlserverflexalpha package sqlserverflexalpha
import ( import (
"context" "context"
"reflect"
"testing" "testing"
"time" "time"
@ -23,7 +22,81 @@ type apiClientInstanceMocked struct {
instanceGetFails bool instanceGetFails bool
} }
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(_ context.Context, _, _, _ string) (*sqlserverflex.GetInstanceResponse, error) { type ListUsersRequestRequest struct{}
func (l ListUsersRequestRequest) Page(_ int64) sqlserverflex.ApiListUsersRequestRequest {
return l
}
func (l ListUsersRequestRequest) Size(_ int64) sqlserverflex.ApiListUsersRequestRequest {
return l
}
func (l ListUsersRequestRequest) Sort(_ sqlserverflex.UserSort) sqlserverflex.ApiListUsersRequestRequest {
return l
}
func (l ListUsersRequestRequest) Execute() (*sqlserverflex.ListUserResponse, error) {
// TODO implement me
panic("implement me")
}
func (a *apiClientInstanceMocked) ListUsersRequest(
_ context.Context,
_ string,
_ string,
_ string,
) sqlserverflex.ApiListUsersRequestRequest {
return ListUsersRequestRequest{}
}
func (a *apiClientInstanceMocked) ListRolesRequestExecute(
_ context.Context,
_ string,
_ string,
_ string,
) (*sqlserverflex.ListRolesResponse, error) {
return &sqlserverflex.ListRolesResponse{
Roles: &[]string{},
}, nil
}
func (a *apiClientInstanceMocked) ListUsersRequestExecute(
_ context.Context,
_ string,
_ string,
_ string,
) (*sqlserverflex.ListUserResponse, error) {
return &sqlserverflex.ListUserResponse{
Pagination: nil,
Users: nil,
}, nil
}
func (a *apiClientInstanceMocked) GetDatabaseRequestExecute(
_ context.Context,
_ string,
_ string,
_ string,
_ string,
) (*sqlserverflex.GetDatabaseResponse, error) {
return nil, nil
}
func (a *apiClientInstanceMocked) GetUserRequestExecute(
_ context.Context,
_ string,
_ string,
_ string,
_ int64,
) (*sqlserverflex.GetUserResponse, error) {
return nil, nil
}
func (a *apiClientInstanceMocked) GetInstanceRequestExecute(
_ context.Context,
_, _, _ string,
) (*sqlserverflex.GetInstanceResponse, error) {
if a.instanceGetFails { if a.instanceGetFails {
return nil, &oapierror.GenericOpenAPIError{ return nil, &oapierror.GenericOpenAPIError{
StatusCode: 500, StatusCode: 500,
@ -43,9 +116,11 @@ func (a *apiClientInstanceMocked) GetInstanceRequestExecute(_ context.Context, _
}, nil }, nil
} }
func TestCreateInstanceWaitHandler(t *testing.T) { func TestCreateInstanceWaitHandler(t *testing.T) {
t.Skip("skipping - needs refactoring") //stateSuccess := utils.Ptr(InstanceStateSuccess)
instanceId := utils.Ptr("foo")
tests := []struct { tests := []struct {
desc string desc string
instanceId string
instanceGetFails bool instanceGetFails bool
instanceState string instanceState string
instanceNetwork sqlserverflex.InstanceNetwork instanceNetwork sqlserverflex.InstanceNetwork
@ -53,40 +128,42 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
wantErr bool wantErr bool
wantRes *sqlserverflex.GetInstanceResponse wantRes *sqlserverflex.GetInstanceResponse
}{ }{
{ //{
desc: "create_succeeded", // desc: "create_succeeded",
instanceGetFails: false, // instanceId: *instanceId,
instanceState: InstanceStateSuccess, // instanceGetFails: false,
instanceNetwork: sqlserverflex.InstanceNetwork{ // instanceState: *stateSuccess,
AccessScope: nil, // instanceNetwork: sqlserverflex.InstanceNetwork{
Acl: nil, // AccessScope: nil,
InstanceAddress: utils.Ptr("10.0.0.1"), // Acl: nil,
RouterAddress: utils.Ptr("10.0.0.2"), // InstanceAddress: utils.Ptr("10.0.0.1"),
}, // RouterAddress: utils.Ptr("10.0.0.2"),
wantErr: false, // },
wantRes: &sqlserverflex.GetInstanceResponse{ // wantErr: false,
BackupSchedule: nil, // wantRes: &sqlserverflex.GetInstanceResponse{
Edition: nil, // BackupSchedule: nil,
Encryption: nil, // Edition: nil,
FlavorId: nil, // Encryption: nil,
Id: nil, // FlavorId: nil,
IsDeletable: nil, // Id: instanceId,
Name: nil, // IsDeletable: nil,
Network: &sqlserverflex.InstanceNetwork{ // Name: nil,
AccessScope: nil, // Network: &sqlserverflex.InstanceNetwork{
Acl: nil, // AccessScope: nil,
InstanceAddress: utils.Ptr("10.0.0.1"), // Acl: nil,
RouterAddress: utils.Ptr("10.0.0.2"), // InstanceAddress: utils.Ptr("10.0.0.1"),
}, // RouterAddress: utils.Ptr("10.0.0.2"),
Replicas: nil, // },
RetentionDays: nil, // Replicas: nil,
Status: nil, // RetentionDays: nil,
Storage: nil, // Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(stateSuccess),
Version: nil, // Storage: nil,
}, // Version: nil,
}, // },
//},
{ {
desc: "create_failed", desc: "create_failed",
instanceId: *instanceId,
instanceGetFails: false, instanceGetFails: false,
instanceState: InstanceStateFailed, instanceState: InstanceStateFailed,
wantErr: true, wantErr: true,
@ -94,6 +171,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
}, },
{ {
desc: "create_failed_2", desc: "create_failed_2",
instanceId: *instanceId,
instanceGetFails: false, instanceGetFails: false,
instanceState: InstanceStateEmpty, instanceState: InstanceStateEmpty,
wantErr: true, wantErr: true,
@ -101,12 +179,14 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
}, },
{ {
desc: "instance_get_fails", desc: "instance_get_fails",
instanceId: *instanceId,
instanceGetFails: true, instanceGetFails: true,
wantErr: true, wantErr: true,
wantRes: nil, wantRes: nil,
}, },
{ {
desc: "timeout", desc: "timeout",
instanceId: *instanceId,
instanceGetFails: false, instanceGetFails: false,
instanceState: InstanceStateProcessing, instanceState: InstanceStateProcessing,
wantErr: true, wantErr: true,
@ -114,26 +194,26 @@ func TestCreateInstanceWaitHandler(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { t.Run(
instanceId := "foo-bar" tt.desc, func(t *testing.T) {
apiClient := &apiClientInstanceMocked{
instanceId: tt.instanceId,
instanceState: tt.instanceState,
instanceGetFails: tt.instanceGetFails,
}
apiClient := &apiClientInstanceMocked{ handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", tt.instanceId, "")
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)
}
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background()) if !reflect.DeepEqual(gotRes, tt.wantRes) {
if (err != nil) != tt.wantErr { t.Fatalf("handler gotRes = %v, want %v", gotRes, tt.wantRes)
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)
}
})
} }
} }
@ -182,34 +262,36 @@ func TestUpdateInstanceWaitHandler(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { t.Run(
instanceId := "foo-bar" tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{ apiClient := &apiClientInstanceMocked{
instanceId: instanceId, instanceId: instanceId,
instanceState: tt.instanceState, instanceState: tt.instanceState,
instanceGetFails: tt.instanceGetFails, 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, "") var wantRes *sqlserverflex.GetInstanceResponse
if tt.wantResp {
wantRes = &sqlserverflex.GetInstanceResponse{
Id: &instanceId,
Status: sqlserverflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr(tt.instanceState)),
}
}
gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background()) handler := UpdateInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
if (err != nil) != tt.wantErr { gotRes, err := handler.SetTimeout(10 * time.Millisecond).SetSleepBeforeWait(1 * time.Millisecond).WaitWithContext(context.Background())
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
} if (err != nil) != tt.wantErr {
if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) }
} if !cmp.Equal(gotRes, wantRes) {
}) t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes)
}
},
)
} }
} }
@ -239,23 +321,25 @@ func TestDeleteInstanceWaitHandler(t *testing.T) {
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { t.Run(
instanceId := "foo-bar" tt.desc, func(t *testing.T) {
instanceId := "foo-bar"
apiClient := &apiClientInstanceMocked{ apiClient := &apiClientInstanceMocked{
instanceGetFails: tt.instanceGetFails, instanceGetFails: tt.instanceGetFails,
instanceIsDeleted: tt.instanceState == InstanceStateSuccess, instanceIsDeleted: tt.instanceState == InstanceStateSuccess,
instanceId: instanceId, instanceId: instanceId,
instanceState: tt.instanceState, instanceState: tt.instanceState,
} }
handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "") handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId, "")
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
} }
}) },
)
} }
} }