fix: fix sqlserverflexalpha #61

Merged
marcel.henselin merged 1 commit from fix/sqlserverflexalpha into alpha 2026-02-13 16:23:43 +00:00
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/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/sqlserverflex v1.4.1
github.com/teambition/rrule-go v1.8.2
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/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/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/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
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/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/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/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"
)
// dataSourceModel maps the data source schema data.
type dataSourceModel struct {
sqlserverflexalphaGen.DatabaseModel
TerraformID types.String `tfsdk:"id"`
}
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/database/datasources_gen"
)
var _ datasource.DataSource = (*databaseDataSource)(nil)
// NewDatabaseDataSource creates a new database data source.
const errorPrefix = "[sqlserverflexalpha - Database]"
func NewDatabaseDataSource() datasource.DataSource {
return &databaseDataSource{}
}
type dataSourceModel struct {
sqlserverflexalphaGen.DatabaseModel
TerraformId types.String `tfsdk:"id"`
}
type databaseDataSource struct {
client *sqlserverflexalpha.APIClient
client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData
}
// Metadata returns the data source type name.
func (d *databaseDataSource) Metadata(
_ context.Context,
req datasource.MetadataRequest,
@ -46,16 +46,13 @@ func (d *databaseDataSource) Metadata(
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
}
// Schema defines the data source schema.
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
s := sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
s.Attributes["id"] = schema.StringAttribute{
Description: "Terraform's internal resource ID. It is structured as \\\"`project_id`,`region`,`instance_id`," +
"`database_id`\\\".\",",
Computed: true,
resp.Schema = sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
resp.Schema.Attributes["id"] = schema.StringAttribute{
Computed: true,
Description: "The terraform internal identifier.",
MarkdownDescription: "The terraform internal identifier.",
}
resp.Schema = s
}
// Configure adds the provider configured client to the data source.
@ -70,19 +67,41 @@ func (d *databaseDataSource) Configure(
return
}
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
apiClientConfigOptions := []config.ConfigurationOption{
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
}
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) {
var model dataSourceModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
var data dataSourceModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
@ -90,22 +109,17 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
ctx = core.InitProviderContext(ctx)
// Extract identifiers from the plan
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
region := d.providerData.GetRegionWithOverride(model.Region)
databaseName := model.DatabaseName.ValueString()
projectId := data.ProjectId.ValueString()
region := d.providerData.GetRegionWithOverride(data.Region)
instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
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()
if resp.Diagnostics.HasError() {
return
}
if err != nil {
handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId)
resp.State.RemoveResource(ctx)
@ -113,9 +127,8 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
}
ctx = core.LogResponse(ctx)
// Map response body to schema and populate Computed attribute values
err = mapFields(databaseResp, &model, region)
err = mapFields(databaseResp, &data, region)
if err != nil {
core.LogAndAddError(
ctx,
@ -126,14 +139,11 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
return
}
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
tflog.Info(ctx, "SQL Server Flex beta database read")
tflog.Info(ctx, "SQL Server Flex alpha database read")
}
// 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.CollationName = types.StringValue(source.GetCollationName())
model.TerraformID = utils.BuildInternalTerraformId(
model.TerraformId = utils.BuildInternalTerraformId(
model.ProjectId.ValueString(),
region,
model.InstanceId.ValueString(),
@ -80,6 +80,12 @@ func mapResourceFields(source *sqlserverflexalpha.GetDatabaseResponse, model *re
model.ProjectId = types.StringValue(model.ProjectId.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
}

View file

@ -35,7 +35,7 @@ func TestMapFields(t *testing.T) {
Name: utils.Ptr("my-db"),
CollationName: utils.Ptr("collation"),
CompatibilityLevel: utils.Ptr(int64(150)),
Owner: utils.Ptr("\"my-owner\""),
Owner: utils.Ptr("my-owner"),
},
model: &dataSourceModel{
DatabaseModel: datasource.DatabaseModel{
@ -58,7 +58,7 @@ func TestMapFields(t *testing.T) {
CompatibilityLevel: types.Int64Value(150),
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{
Id: utils.Ptr(int64(1)),
Name: utils.Ptr("my-db"),
Owner: utils.Ptr("\"my-owner\""),
Owner: utils.Ptr("my-owner"),
},
model: &resourceModel{
ProjectId: types.StringValue("my-project"),
@ -137,13 +137,17 @@ func TestMapResourceFields(t *testing.T) {
},
expected: expected{
model: &resourceModel{
Id: types.Int64Value(1),
Name: types.StringValue("my-db"),
DatabaseName: types.StringValue("my-db"),
InstanceId: types.StringValue("my-instance"),
ProjectId: types.StringValue("my-project"),
Region: types.StringValue("eu01"),
Owner: types.StringValue("my-owner"),
Id: types.Int64Value(1),
Name: types.StringValue("my-db"),
Compatibility: types.Int64Value(0),
CompatibilityLevel: types.Int64Value(0),
Collation: types.StringValue(""),
CollationName: types.StringValue(""),
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'
modifiers:
- 'RequiresReplace'
- 'UseStateForUnknown'
- name: 'database_name'
modifiers:
@ -43,6 +43,7 @@ fields:
- name: 'compatibility'
modifiers:
- 'UseStateForUnknown'
- 'RequiresReplace'
- name: 'compatibility_level'

View file

@ -6,8 +6,8 @@ import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/path"
"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/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/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 (
@ -46,9 +47,13 @@ func NewDatabaseResource() resource.Resource {
}
// 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 {
ProjectID types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
@ -56,12 +61,11 @@ type DatabaseResourceIdentityModel struct {
DatabaseName types.String `tfsdk:"database_name"`
}
type databaseResource struct {
client *sqlserverflexalpha.APIClient
providerData core.ProviderData
}
func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
func (r *databaseResource) Metadata(
_ context.Context,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
}
@ -69,7 +73,7 @@ func (r *databaseResource) Metadata(_ context.Context, req resource.MetadataRequ
var modifiersFileByte []byte
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)
if err != nil {
@ -124,10 +128,10 @@ func (r *databaseResource) Configure(
config.WithCustomAuth(r.providerData.RoundTripper),
utils.UserAgentConfigOption(r.providerData.Version),
}
if r.providerData.PostgresFlexCustomEndpoint != "" {
if r.providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(
apiClientConfigOptions,
config.WithEndpoint(r.providerData.PostgresFlexCustomEndpoint),
config.WithEndpoint(r.providerData.SQLServerFlexCustomEndpoint),
)
} else {
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) {
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() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
region := model.Region.ValueString()
instanceId := model.InstanceId.ValueString()
projectId := data.ProjectId.ValueString()
region := data.Region.ValueString()
instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "region", region)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
// Generate API request body from model
payload, err := toCreatePayload(&model)
databaseName := data.Name.ValueString()
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 {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error creating database",
fmt.Sprintf("Creating API payload: %v", err),
createErr,
fmt.Sprintf("Calling API: %v", err),
)
return
}
// Create new database
databaseResp, err := r.client.CreateDatabaseRequest(
ctx,
projectId,
region,
instanceId,
).CreateDatabaseRequestPayload(*payload).Execute()
createResp, err := r.client.CreateDatabaseRequest(ctx, projectId, region, instanceId).
CreateDatabaseRequestPayload(payLoad).
Execute()
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
}
ctx = core.LogResponse(ctx)
if databaseResp == nil || databaseResp.Id == nil {
if createResp == nil || createResp.Id == nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
@ -201,11 +229,9 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
return
}
databaseId := *databaseResp.Id
databaseName := model.DatabaseName.String()
databaseId := *createResp.Id
ctx = tflog.SetField(ctx, "database_id", databaseId)
ctx = tflog.SetField(ctx, "database_name", databaseName)
ctx = core.LogResponse(ctx)
@ -221,6 +247,69 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
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()
if err != nil {
core.LogAndAddError(
@ -233,7 +322,7 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
}
// Map response body to schema
err = mapResourceFields(database, &model, region)
err = mapResourceFields(database, &data, region)
if err != nil {
core.LogAndAddError(
ctx,
@ -245,11 +334,13 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
}
// 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() {
return
}
// Save data into Terraform state
tflog.Info(ctx, "sqlserverflexalpha.Database created")
}
@ -310,7 +401,7 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}
// Set data returned by API in identity
// Save identity into Terraform state
identity := DatabaseResourceIdentityModel{
ProjectID: types.StringValue(projectId),
Region: types.StringValue(region),
@ -372,7 +463,13 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques
// Delete existing record set
err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
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)
@ -388,11 +485,13 @@ func (r *databaseResource) ModifyPlan(
req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform
var configModel resourceModel
// skip initial empty configuration to avoid follow-up errors
if req.Config.Raw.IsNull() {
return
}
var configModel resourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
if resp.Diagnostics.HasError() {
return
@ -409,6 +508,23 @@ func (r *databaseResource) ModifyPlan(
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)...)
if resp.Diagnostics.HasError() {
return
@ -416,7 +532,7 @@ func (r *databaseResource) ModifyPlan(
}
// 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(
ctx context.Context,
req resource.ImportStateRequest,
@ -432,36 +548,31 @@ func (r *databaseResource) ImportState(
ctx, &resp.Diagnostics,
"Error importing database",
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,
),
)
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("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("database_name"), databaseId)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), idParts[3])...)
core.LogAndAddWarning(
ctx,
&resp.Diagnostics,
"Sqlserverflexalpha database imported with empty password",
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
)
tflog.Info(ctx, "Sqlserverflexalpha database state imported")
var identityData DatabaseResourceIdentityModel
identityData.ProjectID = types.StringValue(idParts[0])
identityData.Region = types.StringValue(idParts[1])
identityData.InstanceID = types.StringValue(idParts[2])
identityData.DatabaseName = types.StringValue(idParts[3])
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
@ -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("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.

View file

@ -1,4 +1,4 @@
package sqlserverFlexAlphaFlavor
package sqlserverflexalphaFlavor
import (
"context"
@ -10,14 +10,14 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"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/core"
"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"
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
)
// Ensure the implementation satisfies the expected interfaces.
@ -26,11 +26,6 @@ var (
_ datasource.DataSourceWithConfigure = &flavorDataSource{}
)
// NewFlavorDataSource is a helper function to simplify the provider implementation.
func NewFlavorDataSource() datasource.DataSource {
return &flavorDataSource{}
}
type FlavorModel struct {
ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
@ -46,39 +41,58 @@ type FlavorModel struct {
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.
type flavorDataSource struct {
client *sqlserverflexalpha.APIClient
client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData
}
// Metadata returns the data source type name.
func (r *flavorDataSource) Metadata(
_ context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
) {
func (r *flavorDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_flavor"
}
// Configure adds the provider configured client to the data source.
func (r *flavorDataSource) Configure(
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
func (r *flavorDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
return
}
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
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 := 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
}
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) {
@ -86,13 +100,13 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
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.",
Description: "The project ID of the flavor.",
MarkdownDescription: "The project ID of the flavor.",
},
"region": schema.StringAttribute{
Required: true,
Description: "The flavor description.",
MarkdownDescription: "The flavor description.",
Description: "The region of the flavor.",
MarkdownDescription: "The region of the flavor.",
},
"cpu": schema.Int64Attribute{
Required: true,
@ -109,6 +123,16 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
Description: "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{
Computed: true,
Description: "The flavor description.",
@ -116,13 +140,8 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
},
"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.",
Description: "The id of the instance flavor.",
MarkdownDescription: "The id of the instance flavor.",
},
"max_gb": schema.Int64Attribute{
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.",
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{
@ -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
}
var foundFlavors []sqlserverflexalpha.ListFlavors
var foundFlavors []sqlserverflexalphaPkg.ListFlavors
for _, flavor := range flavors {
if model.Cpu.ValueInt64() != *flavor.Cpu {
continue
@ -220,13 +314,11 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
model.MinGb = types.Int64Value(*f.MinGB)
if f.StorageClasses == nil {
model.StorageClasses = types.ListNull(
sqlserverflexalphaGen.StorageClassesType{
ObjectType: basetypes.ObjectType{
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
},
model.StorageClasses = types.ListNull(sqlserverflexalphaGen.StorageClassesType{
ObjectType: basetypes.ObjectType{
AttrTypes: sqlserverflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
},
)
})
} else {
var scList []attr.Value
for _, sc := range *f.StorageClasses {
@ -259,5 +351,5 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
if resp.Diagnostics.HasError() {
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 (
"context"

View file

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

View file

@ -2,30 +2,39 @@ package sqlserverflexalpha
import (
"context"
"fmt"
"net/http"
"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/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/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"
)
// dataSourceModel maps the data source schema data.
type dataSourceModel = sqlserverflexalphaGen.FlavorsModel
var _ datasource.DataSource = (*flavorsDataSource)(nil)
// TODO: Use NewFlavorsDataSource when datasource is implemented
const errorPrefix = "[sqlserverflexalpha - Flavors]"
func NewFlavorsDataSource() datasource.DataSource {
return &flavorsDataSource{}
}
type dataSourceModel struct {
sqlserverflexalphaGen.FlavorsModel
TerraformId types.String `tfsdk:"id"`
}
type flavorsDataSource struct {
client *sqlserverflexalpha.APIClient
client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData
}
@ -39,6 +48,11 @@ func (d *flavorsDataSource) Metadata(
func (d *flavorsDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
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.
@ -53,12 +67,34 @@ func (d *flavorsDataSource) Configure(
return
}
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
apiClientConfigOptions := []config.ConfigurationOption{
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
}
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) {
@ -71,11 +107,50 @@ func (d *flavorsDataSource) Read(ctx context.Context, req datasource.ReadRequest
return
}
// Todo: Read API call logic
ctx = core.InitProviderContext(ctx)
// Example data value setting
// data.Id = types.StringValue("example-id")
projectId := data.ProjectId.ValueString()
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)...)
tflog.Info(ctx, fmt.Sprintf("%s read successful", errorPrefix))
}

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package sqlserverflexalpha
import (
@ -7,47 +5,40 @@ import (
"fmt"
"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-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"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/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.
type dataSourceModel struct {
sqlserverflexalpha2.InstanceModel
TerraformID types.String `tfsdk:"id"`
}
var _ datasource.DataSource = (*instanceDataSource)(nil)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &instanceDataSource{}
)
const errorPrefix = "[sqlserverflexalpha - Instance]"
// NewInstanceDataSource is a helper function to simplify the provider implementation.
func NewInstanceDataSource() datasource.DataSource {
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 {
client *sqlserverflex.APIClient
client *sqlserverflexalphaPkg.APIClient
providerData core.ProviderData
}
// Metadata returns the data source type name.
func (r *instanceDataSource) Metadata(
func (d *instanceDataSource) Metadata(
_ context.Context,
req datasource.MetadataRequest,
resp *datasource.MetadataResponse,
@ -55,66 +46,80 @@ func (r *instanceDataSource) Metadata(
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.
func (r *instanceDataSource) Configure(
func (d *instanceDataSource) Configure(
ctx context.Context,
req datasource.ConfigureRequest,
resp *datasource.ConfigureResponse,
) {
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 {
return
}
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
if resp.Diagnostics.HasError() {
apiClientConfigOptions := []config.ConfigurationOption{
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
}
r.client = apiClient
tflog.Info(ctx, "SQLServer Flex instance client configured")
d.client = apiClient
tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
}
// Schema defines the schema for the data source.
func (r *instanceDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
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,
}
func (d *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data dataSourceModel
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() {
return
}
ctx = core.InitProviderContext(ctx)
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
region := r.providerData.GetRegionWithOverride(model.Region)
projectId := data.ProjectId.ValueString()
region := d.providerData.GetRegionWithOverride(data.Region)
instanceId := data.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
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 {
utils.LogError(
ctx,
&resp.Diagnostics,
err,
"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{
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)
// var storage = &storageModel{}
// if !model.Storage.IsNull() && !model.Storage.IsUnknown() {
// diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
// resp.Diagnostics.Append(diags...)
// if resp.Diagnostics.HasError() {
// return
// }
//}
//
// var encryption = &encryptionModel{}
//if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
// diags = model.Encryption.As(ctx, encryption, basetypes.ObjectAsOptions{})
// resp.Diagnostics.Append(diags...)
// if resp.Diagnostics.HasError() {
// return
// }
//}
//
//var network = &networkModel{}
//if !model.Network.IsNull() && !model.Network.IsUnknown() {
// diags = model.Network.As(ctx, network, basetypes.ObjectAsOptions{})
// resp.Diagnostics.Append(diags...)
// if resp.Diagnostics.HasError() {
// return
// }
//}
err = mapFields(ctx, instanceResp, &model, resp.Diagnostics)
// err = mapFields(ctx, instanceResp, &model, storage, encryption, network, region)
err = mapDataResponseToModel(ctx, instanceResp, &data, resp.Diagnostics)
if err != nil {
core.LogAndAddError(
ctx,
&resp.Diagnostics,
"Error reading instance",
fmt.Sprintf("%s Read", errorPrefix),
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
}
tflog.Info(ctx, "SQLServer Flex instance read")
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

View file

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

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package sqlserverflexalpha
import (
@ -10,26 +8,25 @@ import (
"strings"
"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/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
"github.com/hashicorp/terraform-plugin-framework/types"
"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/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"
"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 (
_ resource.Resource = &instanceResource{}
_ resource.ResourceWithConfigure = &instanceResource{}
@ -38,19 +35,17 @@ var (
_ resource.ResourceWithIdentity = &instanceResource{}
)
// NewInstanceResource is a helper function to simplify the provider implementation.
func NewInstanceResource() resource.Resource {
return &instanceResource{}
}
//nolint:unused // TODO: remove if not needed later
var validNodeTypes []string = []string{
"Single",
"Replica",
type instanceResource struct {
client *sqlserverflexalpha.APIClient
providerData core.ProviderData
}
// resourceModel describes the resource data model.
type resourceModel = sqlserverflexalpha2.InstanceModel
type resourceModel = sqlserverflexalphaResGen.InstanceModel
type InstanceResourceIdentityModel struct {
ProjectID types.String `tfsdk:"project_id"`
@ -58,93 +53,19 @@ type InstanceResourceIdentityModel struct {
InstanceID types.String `tfsdk:"instance_id"`
}
// instanceResource is the resource implementation.
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(
func (r *instanceResource) Metadata(
ctx context.Context,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
req resource.MetadataRequest,
resp *resource.MetadataResponse,
) {
var ok bool
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
}
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_instance"
}
//go:embed planModifiers.yaml
var modifiersFileByte []byte
// Schema defines the schema for the resource.
func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
schema := sqlserverflexalpha2.InstanceResourceSchema(ctx)
func (r *instanceResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
s := sqlserverflexalphaResGen.InstanceResourceSchema(ctx)
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
if err != nil {
@ -152,12 +73,12 @@ func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest,
return
}
err = utils.AddPlanModifiersToResourceSchema(fields, &schema)
err = utils.AddPlanModifiersToResourceSchema(fields, &s)
if err != nil {
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
return
}
resp.Schema = schema
resp.Schema = s
}
func (r *instanceResource) IdentitySchema(
@ -180,15 +101,355 @@ func (r *instanceResource) IdentitySchema(
}
}
// Create creates the resource and sets the initial Terraform state.
func (r *instanceResource) Create(
// Configure adds the provider configured client to the resource.
func (r *instanceResource) Configure(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) {
var ok bool
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
if !ok {
return
}
apiClientConfigOptions := []config.ConfigurationOption{
config.WithCustomAuth(r.providerData.RoundTripper),
utils.UserAgentConfigOption(r.providerData.Version),
}
if r.providerData.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
var model resourceModel
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
// skip initial empty configuration to avoid follow-up errors
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() {
return
}
@ -207,284 +468,8 @@ func (r *instanceResource) Create(
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
// Generate API request body from model
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)
instanceId := identityData.InstanceID.ValueString()
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
err := r.client.DeleteInstanceRequest(ctx, projectId, region, instanceId).Execute()
@ -495,7 +480,7 @@ func (r *instanceResource) Delete(
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 {
core.LogAndAddError(
ctx,
@ -505,16 +490,31 @@ func (r *instanceResource) Delete(
)
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.
// 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(
ctx context.Context,
req resource.ImportStateRequest,
resp *resource.ImportStateResponse,
) {
ctx = core.InitProviderContext(ctx)
if req.ID != "" {
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("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"
_ "embed"
"fmt"
"log"
"os"
"strconv"
"strings"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
sqlserverflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"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
// with the more typical acceptance testing import:
// "github.com/hashicorp/terraform-plugin-testing/helper/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) {
t.Parallel()
@ -85,9 +131,15 @@ type User struct {
}
type Database struct {
Name string
ProjectId string
Owner string
Name string
ProjectId 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 {
@ -112,10 +164,6 @@ func getExample() resData {
func TestAccInstance(t *testing.T) {
exData := getExample()
resName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_instance.%s",
exData.TfName,
)
updNameData := exData
updNameData.Name = "name-updated"
@ -123,8 +171,12 @@ func TestAccInstance(t *testing.T) {
updSizeData := exData
updSizeData.Size = 25
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
t.Logf(" ... working on instance %s", exData.TfName)
testInstances = append(testInstances, exData.TfName)
},
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and verify
@ -134,8 +186,9 @@ func TestAccInstance(t *testing.T) {
exData,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", exData.Name),
resource.TestCheckResourceAttrSet(resName, "id"),
resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", exData.Name),
resource.TestCheckResourceAttrSet(resName("instance", exData.TfName), "id"),
// TODO: check all fields
),
},
// Update name and verify
@ -145,7 +198,7 @@ func TestAccInstance(t *testing.T) {
updNameData,
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", updNameData.Name),
resource.TestCheckResourceAttr(resName("instance", exData.TfName), "name", updNameData.Name),
),
},
// Update size and verify
@ -156,15 +209,18 @@ func TestAccInstance(t *testing.T) {
),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
resName,
testutils.ResStr(providerPrefix, "instance", exData.TfName),
"storage.size",
strconv.Itoa(int(updSizeData.Size)),
),
),
},
{
RefreshState: true,
},
//// Import test
//{
// ResourceName: "example_resource.test",
// ResourceName: resName("instance", exData.TfName),
// ImportState: true,
// ImportStateVerify: true,
// },
@ -172,29 +228,39 @@ func TestAccInstance(t *testing.T) {
})
}
func TestAccInstanceWithUsers(t *testing.T) {
func TestAccInstanceNoEncryption(t *testing.T) {
data := getExample()
dbName := "testDb"
userName := "testUser"
data.Users = []User{
{
Name: userName,
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(
"stackitprivatepreview_sqlserverflexalpha_instance.%s",
data.TfName,
)
resUserName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_user.%s",
userName,
)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
t.Logf(" ... working on instance %s", data.TfName)
testInstances = append(testInstances, data.TfName)
},
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and verify
@ -204,18 +270,71 @@ func TestAccInstanceWithUsers(t *testing.T) {
data,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", data.Name),
resource.TestCheckResourceAttrSet(resName, "id"),
resource.TestCheckResourceAttr(resUserName, "name", userName),
resource.TestCheckResourceAttrSet(resUserName, "id"),
// check instance values are set
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "id"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "backup_schedule"),
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()
dbName := "testDb"
userName := "testUser"
data.Users = []User{
@ -233,23 +352,18 @@ func TestAccInstanceWithDatabases(t *testing.T) {
},
}
resName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_instance.%s",
data.TfName,
)
data.UseEncryption = true
data.KekKeyId = "fe039bcf-8d7b-431a-801d-9e81371a6b7b"
data.KekKeyRingId = "6a2d95ab-3c4c-4963-a2bb-08d17a320e27"
data.KekKeyVersion = 1
data.KekServiceAccount = "henselinm-u2v3ex1@sa.stackit.cloud"
resUserName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_user.%s",
userName,
)
resDbName := fmt.Sprintf(
"stackitprivatepreview_sqlserverflexalpha_database.%s",
dbName,
)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
t.Logf(" ... working on instance %s", data.TfName)
testInstances = append(testInstances, data.TfName)
},
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and verify
@ -259,13 +373,59 @@ func TestAccInstanceWithDatabases(t *testing.T) {
data,
),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resName, "name", data.Name),
resource.TestCheckResourceAttrSet(resName, "id"),
resource.TestCheckResourceAttr(resUserName, "name", userName),
resource.TestCheckResourceAttrSet(resUserName, "id"),
resource.TestCheckResourceAttr(resDbName, "name", dbName),
resource.TestCheckResourceAttr(resDbName, "owner", userName),
resource.TestCheckResourceAttrSet(resDbName, "id"),
// check instance values are set
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "id"),
resource.TestCheckResourceAttrSet(resName("instance", data.TfName), "backup_schedule"),
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.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 }}
encryption = {
</