fix: sqlserver_beta (#33)
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Reviewed-on: #33
Reviewed-by: Andre_Harms <andre.harms@stackit.cloud>
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Co-committed-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
This commit is contained in:
parent
581e45eb9c
commit
c22e758b2c
14 changed files with 456 additions and 102 deletions
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||
sqlserverflexalphaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||
sqlserverflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
|
|
@ -405,9 +406,14 @@ func (r *userResource) Delete(
|
|||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Delete existing record set
|
||||
err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
||||
// err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
||||
|
||||
// Delete existing record set
|
||||
_, err := sqlserverflexalphaWait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId).
|
||||
WaitWithContext(ctx)
|
||||
//err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "User Delete Error", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"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"
|
||||
|
|
@ -30,12 +31,22 @@ type databaseDataSource struct {
|
|||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
type dsModel struct {
|
||||
sqlserverflexbetaGen.DatabaseModel
|
||||
TfId types.String `tfsdk:"id"`
|
||||
}
|
||||
|
||||
func (d *databaseDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexbeta_database"
|
||||
}
|
||||
|
||||
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = sqlserverflexbetaGen.DatabaseDataSourceSchema(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.
|
||||
|
|
@ -81,7 +92,7 @@ func (d *databaseDataSource) Configure(
|
|||
}
|
||||
|
||||
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
var data sqlserverflexbetaGen.DatabaseModel
|
||||
var data dsModel
|
||||
readErr := "Read DB error"
|
||||
|
||||
// Read Terraform configuration data into the model
|
||||
|
|
@ -131,19 +142,21 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
|
|||
)
|
||||
return
|
||||
}
|
||||
data.Id = types.Int64Value(dbId)
|
||||
|
||||
owner, ok := databaseResp.GetOwnerOk()
|
||||
if !ok {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
readErr,
|
||||
"Database creation waiting: returned owner is nil",
|
||||
)
|
||||
return
|
||||
}
|
||||
data.Owner = types.StringValue(owner)
|
||||
data.Id = types.Int64Value(dbId)
|
||||
data.TfId = utils.BuildInternalTerraformId(projectId, region, instanceId, databaseName)
|
||||
data.Owner = types.StringValue(databaseResp.GetOwner())
|
||||
|
||||
// TODO: fill remaining fields
|
||||
// data.CollationName = types.Sometype(apiResponse.GetCollationName())
|
||||
// data.CompatibilityLevel = types.Sometype(apiResponse.GetCompatibilityLevel())
|
||||
// data.DatabaseName = types.Sometype(apiResponse.GetDatabaseName())
|
||||
// data.Id = types.Sometype(apiResponse.GetId())
|
||||
// data.InstanceId = types.Sometype(apiResponse.GetInstanceId())
|
||||
// data.Name = types.Sometype(apiResponse.GetName())
|
||||
// data.Owner = types.Sometype(apiResponse.GetOwner())
|
||||
// data.ProjectId = types.Sometype(apiResponse.GetProjectId())
|
||||
// data.Region = types.Sometype(apiResponse.GetRegion())
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func DatabaseDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
Description: "The name of the database.",
|
||||
MarkdownDescription: "The name of the database.",
|
||||
},
|
||||
"id": schema.Int64Attribute{
|
||||
"tf_original_api_id": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The id of the database.",
|
||||
MarkdownDescription: "The id of the database.",
|
||||
|
|
@ -55,8 +55,7 @@ func DatabaseDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
MarkdownDescription: "The STACKIT project ID.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Required: true,
|
||||
Description: "The region which should be addressed",
|
||||
MarkdownDescription: "The region which should be addressed",
|
||||
Validators: []validator.String{
|
||||
|
|
@ -73,7 +72,7 @@ type DatabaseModel struct {
|
|||
CollationName types.String `tfsdk:"collation_name"`
|
||||
CompatibilityLevel types.Int64 `tfsdk:"compatibility_level"`
|
||||
DatabaseName types.String `tfsdk:"database_name"`
|
||||
Id types.Int64 `tfsdk:"id"`
|
||||
Id types.Int64 `tfsdk:"tf_original_api_id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Owner types.String `tfsdk:"owner"`
|
||||
|
|
@ -244,6 +244,7 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
|
|||
|
||||
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var data sqlserverflexbetaResGen.DatabaseModel
|
||||
readErr := "[Database Read]"
|
||||
|
||||
// Read Terraform prior state data into the model
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
||||
|
|
@ -265,22 +266,34 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
|
|||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
|
||||
// Todo: Read API call logic
|
||||
instanceId := identityData.InstanceID.ValueString()
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
dbName := identityData.DatabaseName.ValueString()
|
||||
ctx = tflog.SetField(ctx, "database_name", dbName)
|
||||
|
||||
// TODO: Set data returned by API in identity
|
||||
identity := DatabaseResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
// InstanceID: types.StringValue(instanceId),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
getResp, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, dbName).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
readErr,
|
||||
fmt.Sprintf("Calling API: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
data.Id = types.Int64Value(getResp.GetId())
|
||||
data.Owner = types.StringValue(getResp.GetOwner())
|
||||
data.Name = types.StringValue(getResp.GetName())
|
||||
data.DatabaseName = types.StringValue(getResp.GetName())
|
||||
data.CollationName = types.StringValue(getResp.GetCollationName())
|
||||
data.CompatibilityLevel = types.Int64Value(getResp.GetCompatibilityLevel())
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
tflog.Info(ctx, "sqlserverflexbeta.Database read")
|
||||
}
|
||||
|
||||
|
|
@ -350,11 +363,13 @@ func (r *databaseResource) ModifyPlan(
|
|||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel sqlserverflexbetaResGen.DatabaseModel
|
||||
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
var configModel sqlserverflexbetaResGen.DatabaseModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -374,10 +389,14 @@ func (r *databaseResource) ModifyPlan(
|
|||
var identityModel DatabaseResourceIdentityModel
|
||||
identityModel.ProjectID = planModel.ProjectId
|
||||
identityModel.Region = planModel.Region
|
||||
// TODO: complete
|
||||
//if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
|
||||
// identityModel.InstanceID = planModel.InstanceId
|
||||
//}
|
||||
|
||||
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() {
|
||||
|
|
@ -397,30 +416,54 @@ func (r *databaseResource) ImportState(
|
|||
req resource.ImportStateRequest,
|
||||
resp *resource.ImportStateResponse,
|
||||
) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
// Todo: Import logic
|
||||
if len(idParts) < 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(
|
||||
ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf(
|
||||
"Expected import identifier with format [project_id],[region],..., got %q",
|
||||
req.ID,
|
||||
),
|
||||
)
|
||||
if req.ID != "" {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing database",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[region],[instance_id],[database_name] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), idParts[3])...)
|
||||
|
||||
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, "Sqlserverflexbeta database state imported")
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), idParts[1])...)
|
||||
// ... more ...
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), identityData.ProjectID.ValueString())...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("region"), identityData.Region.ValueString())...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), identityData.InstanceID.ValueString())...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("database_name"), identityData.DatabaseName.ValueString())...)
|
||||
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
core.LogAndAddWarning(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Sqlserverflexbeta 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, "Sqlserverflexbeta database state imported")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,17 +92,47 @@ func (r *flavorDataSource) Configure(ctx context.Context, req datasource.Configu
|
|||
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) {
|
||||
resp.Schema = schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"project_id": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The project ID of the flavor.",
|
||||
MarkdownDescription: "The project ID of the flavor.",
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The region of the flavor.",
|
||||
MarkdownDescription: "The region of the flavor.",
|
||||
},
|
||||
"cpu": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
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.",
|
||||
},
|
||||
"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.",
|
||||
|
|
@ -118,21 +148,11 @@ func (r *flavorDataSource) Schema(ctx context.Context, _ datasource.SchemaReques
|
|||
Description: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||
MarkdownDescription: "maximum storage which can be ordered for the flavor in Gigabyte.",
|
||||
},
|
||||
"memory": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "The memory of the instance in Gibibyte.",
|
||||
MarkdownDescription: "The memory of the instance in Gibibyte.",
|
||||
},
|
||||
"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{
|
||||
Computed: true,
|
||||
Description: "defines the nodeType it can be either single or HA",
|
||||
MarkdownDescription: "defines the nodeType it can be either single or HA",
|
||||
},
|
||||
"storage_classes": schema.ListNestedAttribute{
|
||||
NestedObject: schema.NestedAttributeObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
|
|
@ -331,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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ type APIClientInstanceInterface interface {
|
|||
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
|
||||
}
|
||||
|
||||
// APIClientUserInterface Interface needed for tests
|
||||
type APIClientUserInterface interface {
|
||||
DeleteUserRequestExecute(ctx context.Context, projectId string, region string, instanceId string, userId int64) error
|
||||
}
|
||||
|
||||
// CreateInstanceWaitHandler will wait for instance creation
|
||||
func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||
|
|
@ -131,3 +136,36 @@ func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface
|
|||
handler.SetTimeout(15 * time.Minute)
|
||||
return handler
|
||||
}
|
||||
|
||||
// DeleteUserWaitHandler will wait for instance deletion
|
||||
func DeleteUserWaitHandler(
|
||||
ctx context.Context,
|
||||
a APIClientUserInterface,
|
||||
projectId, instanceId, region string,
|
||||
userId int64,
|
||||
) *wait.AsyncActionHandler[struct{}] {
|
||||
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
|
||||
err = a.DeleteUserRequestExecute(ctx, projectId, region, instanceId, userId)
|
||||
if err == nil {
|
||||
return false, nil, nil
|
||||
}
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
ok := errors.As(err, &oapiErr)
|
||||
if !ok {
|
||||
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
||||
}
|
||||
|
||||
switch oapiErr.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return true, nil, nil
|
||||
case http.StatusInternalServerError:
|
||||
tflog.Warn(ctx, "Wait handler got error 500")
|
||||
return false, nil, nil
|
||||
default:
|
||||
return false, nil, err
|
||||
}
|
||||
})
|
||||
handler.SetTimeout(15 * time.Minute)
|
||||
handler.SetSleepBeforeWait(15 * time.Second)
|
||||
return handler
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue