feat: implement database resource management and error handling for SQL Server Flex
This commit is contained in:
parent
e21fe64326
commit
4383300103
4 changed files with 619 additions and 26 deletions
|
|
@ -3,7 +3,9 @@ package sqlserverflexalpha
|
|||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -13,6 +15,7 @@ import (
|
|||
"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"
|
||||
|
||||
|
|
@ -28,6 +31,13 @@ var (
|
|||
_ resource.ResourceWithImportState = &databaseResource{}
|
||||
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
||||
_ resource.ResourceWithIdentity = &databaseResource{}
|
||||
|
||||
// Define errors
|
||||
errDatabaseNotFound = errors.New("database not found")
|
||||
|
||||
// Error message constants
|
||||
extractErrorSummary = "extracting failed"
|
||||
extractErrorMessage = "Extracting identity data: %v"
|
||||
)
|
||||
|
||||
func NewDatabaseResource() resource.Resource {
|
||||
|
|
@ -137,40 +147,181 @@ func (r *databaseResource) Configure(
|
|||
}
|
||||
|
||||
func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var data sqlserverflexalphaGen.DatabaseModel
|
||||
|
||||
// Read Terraform plan data into the model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
||||
var model resourceModel
|
||||
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Create API call logic
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Example data value setting
|
||||
// data.DatabaseId = types.StringValue("id-from-response")
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
projectId := identityData.ProjectID.ValueString()
|
||||
region := identityData.ProjectID.ValueString()
|
||||
instanceId := identityData.InstanceID.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 := toCreatePayload(&model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating database",
|
||||
fmt.Sprintf("Creating API payload: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
// Create new database
|
||||
databaseResp, 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))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
if databaseResp == nil || databaseResp.Id == nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating database",
|
||||
"API didn't return database Id. A database might have been created",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
databaseId := *databaseResp.Id
|
||||
databaseName := model.DatabaseName.String()
|
||||
|
||||
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating database",
|
||||
fmt.Sprintf("Getting database details after creation: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapResourceFields(database, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error creating database",
|
||||
fmt.Sprintf("Processing API payload: %v", err),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Set data returned by API in identity
|
||||
identity := DatabaseResourceIdentityModel{
|
||||
ProjectID: types.StringValue(projectId),
|
||||
Region: types.StringValue(region),
|
||||
InstanceID: types.StringValue(instanceId),
|
||||
DatabaseName: types.StringValue(databaseName),
|
||||
}
|
||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Set state to fully populated data
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "sqlserverflexalpha.Database created")
|
||||
}
|
||||
|
||||
func (r *databaseResource) 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)...)
|
||||
|
||||
var model resourceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Todo: Read API call logic
|
||||
// Read identity data
|
||||
var identityData DatabaseResourceIdentityModel
|
||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Save updated data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
ctx = core.InitProviderContext(ctx)
|
||||
|
||||
projectId, instanceId, region, databaseName, errExt := r.extractIdentityData(model, identityData)
|
||||
if errExt != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
extractErrorSummary,
|
||||
fmt.Sprintf(extractErrorMessage, errExt),
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
databaseResp, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).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) || errors.Is(err, errDatabaseNotFound) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx = core.LogResponse(ctx)
|
||||
|
||||
// Map response body to schema
|
||||
err = mapResourceFields(databaseResp, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
"Error reading database",
|
||||
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, "sqlserverflexalpha.Database read")
|
||||
}
|
||||
|
|
@ -312,3 +463,46 @@ func (r *databaseResource) ImportState(
|
|||
|
||||
tflog.Info(ctx, "Sqlserverflexalpha database state imported")
|
||||
}
|
||||
|
||||
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
|
||||
func (r *databaseResource) extractIdentityData(
|
||||
model resourceModel,
|
||||
identity DatabaseResourceIdentityModel,
|
||||
) (projectId, region, instanceId, databaseName string, err error) {
|
||||
if !model.DatabaseName.IsNull() && !model.DatabaseName.IsUnknown() {
|
||||
databaseName = model.DatabaseName.ValueString()
|
||||
} else {
|
||||
if identity.DatabaseName.IsNull() || identity.DatabaseName.IsUnknown() {
|
||||
return "", "", "", "", fmt.Errorf("database_name not found in config")
|
||||
}
|
||||
databaseName = identity.DatabaseName.ValueString()
|
||||
}
|
||||
|
||||
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
||||
projectId = model.ProjectId.ValueString()
|
||||
} else {
|
||||
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
|
||||
return "", "", "", "", fmt.Errorf("project_id not found in config")
|
||||
}
|
||||
projectId = identity.ProjectID.ValueString()
|
||||
}
|
||||
|
||||
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
||||
region = r.providerData.GetRegionWithOverride(model.Region)
|
||||
} else {
|
||||
if identity.Region.IsNull() || identity.Region.IsUnknown() {
|
||||
return "", "", "", "", fmt.Errorf("region not found in config")
|
||||
}
|
||||
region = r.providerData.GetRegionWithOverride(identity.Region)
|
||||
}
|
||||
|
||||
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
|
||||
instanceId = model.InstanceId.ValueString()
|
||||
} else {
|
||||
if identity.InstanceID.IsNull() || identity.InstanceID.IsUnknown() {
|
||||
return "", "", "", "", fmt.Errorf("instance_id not found in config")
|
||||
}
|
||||
instanceId = identity.InstanceID.ValueString()
|
||||
}
|
||||
return projectId, region, instanceId, databaseName, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue