fix: #106 postgresflex flavor datasource
feat: support V2 and V3 flavor handling in postgresflex
This commit is contained in:
parent
b51d6369f2
commit
b56a41dc04
34 changed files with 2986 additions and 604 deletions
|
|
@ -115,6 +115,12 @@ func InstanceDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
Description: "Whether the instance can be deleted or not.",
|
||||
MarkdownDescription: "Whether the instance can be deleted or not.",
|
||||
},
|
||||
"labels": schema.MapAttribute{
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
Description: "Key-value pairs, 63 characters max, begin and end with an alphanumerical character,\nmay contain dashes (-), underscores (_), dots (.), and alphanumerics between. Key MUST be at least 1 character.\nMax 64 labels\nRegex for keys: ^(?=.{1,63}$)([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$\nRegex for values: ^(?=.{0,63}$)(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])*$\nThe stackit- prefix is reserved and cannot be used for Keys.\n",
|
||||
MarkdownDescription: "Key-value pairs, 63 characters max, begin and end with an alphanumerical character,\nmay contain dashes (-), underscores (_), dots (.), and alphanumerics between. Key MUST be at least 1 character.\nMax 64 labels\nRegex for keys: ^(?=.{1,63}$)([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$\nRegex for values: ^(?=.{0,63}$)(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])*$\nThe stackit- prefix is reserved and cannot be used for Keys.\n",
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "The name of the instance.",
|
||||
|
|
@ -171,8 +177,8 @@ func InstanceDataSourceSchema(ctx context.Context) schema.Schema {
|
|||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
Description: "How long backups are retained. The value can only be between 32 and 365 days.",
|
||||
MarkdownDescription: "How long backups are retained. The value can only be between 32 and 365 days.",
|
||||
Description: "How long backups are retained. The value can only be between 32 and 90 days.",
|
||||
MarkdownDescription: "How long backups are retained. The value can only be between 32 and 90 days.",
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
|
|
@ -219,6 +225,7 @@ type InstanceModel struct {
|
|||
Id types.String `tfsdk:"tf_original_api_id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
IsDeletable types.Bool `tfsdk:"is_deletable"`
|
||||
Labels types.Map `tfsdk:"labels"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Network NetworkValue `tfsdk:"network"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
|
||||
)
|
||||
|
||||
type flavorsClientReader interface {
|
||||
GetFlavorsRequest(
|
||||
ctx context.Context,
|
||||
projectId, region string,
|
||||
) v3alpha1api.ApiGetFlavorsRequestRequest
|
||||
}
|
||||
|
||||
func getAllFlavors(ctx context.Context, client flavorsClientReader, projectId, region string) (
|
||||
[]v3alpha1api.ListFlavors,
|
||||
error,
|
||||
) {
|
||||
getAllFilter := func(_ v3alpha1api.ListFlavors) bool { return true }
|
||||
flavorList, err := getFlavorsByFilter(ctx, client, projectId, region, getAllFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return flavorList, nil
|
||||
}
|
||||
|
||||
// getFlavorsByFilter is a helper function to retrieve flavors using a filtern function.
|
||||
// Hint: The API does not have a GetFlavors endpoint, only ListFlavors
|
||||
func getFlavorsByFilter(
|
||||
ctx context.Context,
|
||||
client flavorsClientReader,
|
||||
projectId, region string,
|
||||
filter func(db v3alpha1api.ListFlavors) bool,
|
||||
) ([]v3alpha1api.ListFlavors, error) {
|
||||
if projectId == "" || region == "" {
|
||||
return nil, fmt.Errorf("listing postgresflex flavors: projectId and region are required")
|
||||
}
|
||||
|
||||
const pageSize = 25
|
||||
|
||||
var result = make([]v3alpha1api.ListFlavors, 0)
|
||||
|
||||
for page := int32(1); ; page++ {
|
||||
res, err := client.GetFlavorsRequest(ctx, projectId, region).
|
||||
Page(page).Size(pageSize).Sort(v3alpha1api.FLAVORSORT_ID_ASC).Execute()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("requesting flavors list (page %d): %w", page, err)
|
||||
}
|
||||
|
||||
// If the API returns no flavors, we have reached the end of the list.
|
||||
if len(res.Flavors) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for _, flavor := range res.Flavors {
|
||||
if filter(flavor) {
|
||||
result = append(result, flavor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package postgresflexalpha
|
||||
|
||||
/*
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
postgresflex "github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
|
||||
)
|
||||
|
||||
type mockRequest struct {
|
||||
executeFunc func() (*postgresflex.GetFlavorsResponse, error)
|
||||
}
|
||||
|
||||
func (m *mockRequest) Page(_ int32) postgresflex.ApiGetFlavorsRequestRequest { return m }
|
||||
func (m *mockRequest) Size(_ int32) postgresflex.ApiGetFlavorsRequestRequest { return m }
|
||||
func (m *mockRequest) Sort(_ postgresflex.FlavorSort) postgresflex.ApiGetFlavorsRequestRequest {
|
||||
return m
|
||||
}
|
||||
func (m *mockRequest) Execute() (*postgresflex.GetFlavorsResponse, error) {
|
||||
return m.executeFunc()
|
||||
}
|
||||
|
||||
type mockFlavorsClient struct {
|
||||
executeRequest func() postgresflex.ApiGetFlavorsRequestRequest
|
||||
}
|
||||
|
||||
func (m *mockFlavorsClient) GetFlavorsRequest(_ context.Context, _, _ string) postgresflex.ApiGetFlavorsRequestRequest {
|
||||
return m.executeRequest()
|
||||
}
|
||||
|
||||
var mockResp = func(page int32) (*postgresflex.GetFlavorsResponse, error) {
|
||||
if page == 1 {
|
||||
return &postgresflex.GetFlavorsResponse{
|
||||
Flavors: []postgresflex.ListFlavors{
|
||||
{Id: "flavor-1", Description: "first"},
|
||||
{Id: "flavor-2", Description: "second"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
if page == 2 {
|
||||
return &postgresflex.GetFlavorsResponse{
|
||||
Flavors: []postgresflex.ListFlavors{
|
||||
{Id: "flavor-3", Description: "three"},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &postgresflex.GetFlavorsResponse{
|
||||
Flavors: []postgresflex.ListFlavors{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestGetFlavorsByFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
projectId string
|
||||
region string
|
||||
mockErr error
|
||||
filter func(postgresflex.ListFlavors) bool
|
||||
wantCount int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
description: "Success - Get all flavors (2 pages)",
|
||||
projectId: "pid", region: "reg",
|
||||
filter: func(_ postgresflex.ListFlavors) bool { return true },
|
||||
wantCount: 3,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
description: "Success - Filter flavors by description",
|
||||
projectId: "pid", region: "reg",
|
||||
filter: func(f postgresflex.ListFlavors) bool { return f.Description == "first" },
|
||||
wantCount: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
description: "Error - Missing parameters",
|
||||
projectId: "", region: "reg",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(
|
||||
tt.description, func(t *testing.T) {
|
||||
var currentPage int32
|
||||
client := &mockFlavorsClient{
|
||||
executeRequest: func() postgresflex.ApiGetFlavorsRequestRequest {
|
||||
return mockRequest{
|
||||
executeFunc: func() (*postgresflex.GetFlavorsResponse, error) {
|
||||
currentPage++
|
||||
return mockResp(currentPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
actual, err := getFlavorsByFilter(context.Background(), client, tt.projectId, tt.region, tt.filter)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getFlavorsByFilter() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !tt.wantErr && len(actual) != tt.wantCount {
|
||||
t.Errorf("getFlavorsByFilter() got %d flavors, want %d", len(actual), tt.wantCount)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllFlavors(t *testing.T) {
|
||||
var currentPage int32
|
||||
client := &mockFlavorsClient{
|
||||
executeRequest: func() postgresflex.ApiGetFlavorsRequestRequest {
|
||||
return mockRequest{
|
||||
executeFunc: func() (*postgresflex.GetFlavorsResponse, error) {
|
||||
currentPage++
|
||||
return mockResp(currentPage)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
res, err := getAllFlavors(context.Background(), client, "pid", "reg")
|
||||
if err != nil {
|
||||
t.Errorf("getAllFlavors() unexpected error: %v", err)
|
||||
}
|
||||
if len(res) != 3 {
|
||||
t.Errorf("getAllFlavors() expected 3 flavor, got %d", len(res))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
func mapGetInstanceResponseToModel(
|
||||
ctx context.Context,
|
||||
m *postgresflexalpharesource.InstanceModel,
|
||||
m *LocalInstanceModel,
|
||||
resp *postgresflex.GetInstanceResponse,
|
||||
) error {
|
||||
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
|
||||
|
|
@ -71,6 +71,8 @@ func mapGetInstanceResponseToModel(
|
|||
|
||||
m.Acl = netACL
|
||||
|
||||
// m.Labels = resp.GetLabels()
|
||||
|
||||
netInstAdd := types.StringValue("")
|
||||
if instAdd, ok := resp.Network.GetInstanceAddressOk(); ok {
|
||||
netInstAdd = types.StringValue(*instAdd)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@ import (
|
|||
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
coreUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
|
||||
|
||||
"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"
|
||||
postgresflexalpha "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance/resources_gen"
|
||||
|
|
@ -44,12 +47,33 @@ type instanceResource struct {
|
|||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
type LocalInstanceModel struct {
|
||||
postgresflexalpha.InstanceModel
|
||||
Flavor types.Object `tfsdk:"flavor"`
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Flavor
|
||||
type flavorModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Description types.String `tfsdk:"description"`
|
||||
CPU types.Int64 `tfsdk:"cpu"`
|
||||
RAM types.Int64 `tfsdk:"ram"`
|
||||
}
|
||||
|
||||
//// Types corresponding to flavorModel
|
||||
//var flavorTypes = map[string]attr.Type{
|
||||
// "id": basetypes.StringType{},
|
||||
// "description": basetypes.StringType{},
|
||||
// "cpu": basetypes.Int64Type{},
|
||||
// "ram": basetypes.Int64Type{},
|
||||
//}
|
||||
|
||||
func (r *instanceResource) ValidateConfig(
|
||||
ctx context.Context,
|
||||
req resource.ValidateConfigRequest,
|
||||
resp *resource.ValidateConfigResponse,
|
||||
) {
|
||||
var data postgresflexalpha.InstanceModel
|
||||
var data LocalInstanceModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -64,6 +88,23 @@ func (r *instanceResource) ValidateConfig(
|
|||
"The resource may return unexpected results.",
|
||||
)
|
||||
}
|
||||
|
||||
if data.FlavorId.IsNull() {
|
||||
if data.Flavor.IsUnknown() || data.Flavor.IsNull() {
|
||||
resp.Diagnostics.AddAttributeError(
|
||||
path.Root("flavor"),
|
||||
"Missing Attribute Configuration",
|
||||
"Expected flavor to be configured. "+
|
||||
"The resource may return unexpected results.",
|
||||
)
|
||||
}
|
||||
resp.Diagnostics.AddAttributeWarning(
|
||||
path.Root("flavor"),
|
||||
"Attribute Configuration Deprecation",
|
||||
"Using flavor is deprecated, "+
|
||||
"please use flavor_id instead.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
|
|
@ -73,7 +114,7 @@ func (r *instanceResource) ModifyPlan(
|
|||
req resource.ModifyPlanRequest,
|
||||
resp *resource.ModifyPlanResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel postgresflexalpha.InstanceModel
|
||||
var configModel LocalInstanceModel
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
|
|
@ -83,7 +124,7 @@ func (r *instanceResource) ModifyPlan(
|
|||
return
|
||||
}
|
||||
|
||||
var planModel postgresflexalpha.InstanceModel
|
||||
var planModel LocalInstanceModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
|
|
@ -133,20 +174,53 @@ func (r *instanceResource) Configure(
|
|||
var modifiersFileByte []byte
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
schema := postgresflexalpha.InstanceResourceSchema(ctx)
|
||||
func (r *instanceResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
schemaVar := postgresflexalpha.InstanceResourceSchema(ctx)
|
||||
schemaVar.Attributes["flavor"] = schema.SingleNestedAttribute{
|
||||
Optional: true,
|
||||
DeprecationMessage: "Please use flavor_id instead.",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
UseStateForUnknownIfFlavorUnchanged(req),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
UseStateForUnknownIfFlavorUnchanged(req),
|
||||
},
|
||||
},
|
||||
"cpu": schema.Int64Attribute{
|
||||
DeprecationMessage: "Please use flavor_id instead.",
|
||||
Optional: true,
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
DeprecationMessage: "Please use flavor_id instead.",
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
schemaVar.Attributes["flavor_id"] = schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "The id of the instance flavor.",
|
||||
MarkdownDescription: "The id of the instance flavor.",
|
||||
}
|
||||
|
||||
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = utils.AddPlanModifiersToResourceSchema(fields, &schema)
|
||||
err = utils.AddPlanModifiersToResourceSchema(fields, &schemaVar)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
||||
return
|
||||
}
|
||||
resp.Schema = schema
|
||||
resp.Schema = schemaVar
|
||||
}
|
||||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
|
|
@ -155,7 +229,7 @@ func (r *instanceResource) Create(
|
|||
req resource.CreateRequest,
|
||||
resp *resource.CreateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha.InstanceModel
|
||||
var model LocalInstanceModel
|
||||
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
|
@ -177,6 +251,73 @@ func (r *instanceResource) Create(
|
|||
return
|
||||
}
|
||||
|
||||
// determine flavor ID
|
||||
var flModel = &flavorModel{}
|
||||
if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) {
|
||||
diags = model.Flavor.As(ctx, flModel, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
flavors, err := getAllFlavors(ctx, r.client.DefaultAPI, projectID, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading flavors", fmt.Sprintf("getAllFlavors: %v", err))
|
||||
return
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("loaded flavors: %d", len(flavors)))
|
||||
|
||||
var foundFlavors []v3alpha1api.ListFlavors
|
||||
for _, flavor := range flavors {
|
||||
if flModel.CPU.ValueInt64() != int64(flavor.Cpu) {
|
||||
// tflog.Debug(ctx, fmt.Sprintf("flavor - cpu did not match (%d - %d)", flModel.CPU.ValueInt64(), flavor.Cpu))
|
||||
continue
|
||||
}
|
||||
if flModel.RAM.ValueInt64() != int64(flavor.Memory) {
|
||||
// tflog.Debug(ctx, fmt.Sprintf("flavor - ram did not match (%d - %d)", flModel.RAM.ValueInt64(), flavor.Memory))
|
||||
continue
|
||||
}
|
||||
tmpNodeType := "Single"
|
||||
if model.Replicas.ValueInt64() > 1 {
|
||||
tmpNodeType = "Replica"
|
||||
}
|
||||
if strings.ToLower(tmpNodeType) != strings.ToLower(flavor.NodeType) {
|
||||
//tflog.Debug(
|
||||
// ctx,
|
||||
// fmt.Sprintf(
|
||||
// "flavor - nodeType did not match ('%s' - '%s')",
|
||||
// strings.ToLower(tmpNodeType),
|
||||
// strings.ToLower(flavor.NodeType),
|
||||
// ),
|
||||
//)
|
||||
continue
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("found flavor %s, checking storage classes", flavor.Id))
|
||||
for _, sc := range flavor.StorageClasses {
|
||||
if model.Storage.PerformanceClass.ValueString() != sc.Class {
|
||||
continue
|
||||
}
|
||||
tflog.Debug(ctx, fmt.Sprintf("found storage class '%s' for flavor '%s', checking storage classes", sc.Class, flavor.Id))
|
||||
foundFlavors = append(foundFlavors, flavor)
|
||||
}
|
||||
}
|
||||
if len(foundFlavors) == 0 {
|
||||
resp.Diagnostics.AddError("get flavor", "could not find requested flavor")
|
||||
return
|
||||
}
|
||||
if len(foundFlavors) > 1 {
|
||||
resp.Diagnostics.AddError("get flavor", "found too many matching flavors")
|
||||
return
|
||||
}
|
||||
|
||||
f := foundFlavors[0]
|
||||
flModel.Description = types.StringValue(f.Description)
|
||||
flModel.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, f.Id)
|
||||
model.FlavorId = types.StringValue(f.Id)
|
||||
//flModel. .MaxGb = types.Int32Value(f.MaxGB)
|
||||
//flModel.MinGb = types.Int32Value(f.MinGB)
|
||||
}
|
||||
|
||||
replVal := model.Replicas.ValueInt64() // nolint:gosec // check is performed above
|
||||
payload := modelToCreateInstancePayload(netACL, model, replVal)
|
||||
|
||||
|
|
@ -243,7 +384,7 @@ func (r *instanceResource) Create(
|
|||
|
||||
func modelToCreateInstancePayload(
|
||||
netACL []string,
|
||||
model postgresflexalpha.InstanceModel,
|
||||
model LocalInstanceModel,
|
||||
replVal int64,
|
||||
) v3alpha1api.CreateInstanceRequestPayload {
|
||||
var enc *v3alpha1api.InstanceEncryption
|
||||
|
|
@ -283,7 +424,7 @@ func (r *instanceResource) Read(
|
|||
) { // nolint:gocritic // function signature required by Terraform
|
||||
functionErrorSummary := "read instance failed"
|
||||
|
||||
var model postgresflexalpha.InstanceModel
|
||||
var model LocalInstanceModel
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
|
|
@ -371,7 +512,7 @@ func (r *instanceResource) Update(
|
|||
req resource.UpdateRequest,
|
||||
resp *resource.UpdateResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha.InstanceModel
|
||||
var model LocalInstanceModel
|
||||
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
|
@ -484,7 +625,7 @@ func (r *instanceResource) Delete(
|
|||
req resource.DeleteRequest,
|
||||
resp *resource.DeleteResponse,
|
||||
) { // nolint:gocritic // function signature required by Terraform
|
||||
var model postgresflexalpha.InstanceModel
|
||||
var model LocalInstanceModel
|
||||
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,13 @@ func InstanceResourceSchema(ctx context.Context) schema.Schema {
|
|||
Description: "Whether the instance can be deleted or not.",
|
||||
MarkdownDescription: "Whether the instance can be deleted or not.",
|
||||
},
|
||||
//"labels": schema.MapAttribute{
|
||||
// ElementType: types.StringType,
|
||||
// Optional: true,
|
||||
// Computed: true,
|
||||
// Description: "Key-value pairs, 63 characters max, begin and end with an alphanumerical character,\nmay contain dashes (-), underscores (_), dots (.), and alphanumerics between. Key MUST be at least 1 character.\nMax 64 labels\nRegex for keys: ^(?=.{1,63}$)([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$\nRegex for values: ^(?=.{0,63}$)(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])*$\nThe stackit- prefix is reserved and cannot be used for Keys.\n",
|
||||
// MarkdownDescription: "Key-value pairs, 63 characters max, begin and end with an alphanumerical character,\nmay contain dashes (-), underscores (_), dots (.), and alphanumerics between. Key MUST be at least 1 character.\nMax 64 labels\nRegex for keys: ^(?=.{1,63}$)([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$\nRegex for values: ^(?=.{0,63}$)(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])*$\nThe stackit- prefix is reserved and cannot be used for Keys.\n",
|
||||
//},
|
||||
"name": schema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "The name of the instance.",
|
||||
|
|
@ -191,8 +198,8 @@ func InstanceResourceSchema(ctx context.Context) schema.Schema {
|
|||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Required: true,
|
||||
Description: "How long backups are retained. The value can only be between 32 and 365 days.",
|
||||
MarkdownDescription: "How long backups are retained. The value can only be between 32 and 365 days.",
|
||||
Description: "How long backups are retained. The value can only be between 32 and 90 days.",
|
||||
MarkdownDescription: "How long backups are retained. The value can only be between 32 and 90 days.",
|
||||
},
|
||||
"status": schema.StringAttribute{
|
||||
Computed: true,
|
||||
|
|
@ -239,15 +246,16 @@ type InstanceModel struct {
|
|||
Id types.String `tfsdk:"id"`
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
IsDeletable types.Bool `tfsdk:"is_deletable"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Network NetworkValue `tfsdk:"network"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Replicas types.Int64 `tfsdk:"replicas"`
|
||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
Storage StorageValue `tfsdk:"storage"`
|
||||
Version types.String `tfsdk:"version"`
|
||||
//Labels types.Map `tfsdk:"labels"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Network NetworkValue `tfsdk:"network"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
Replicas types.Int64 `tfsdk:"replicas"`
|
||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||
Status types.String `tfsdk:"status"`
|
||||
Storage StorageValue `tfsdk:"storage"`
|
||||
Version types.String `tfsdk:"version"`
|
||||
}
|
||||
|
||||
var _ basetypes.ObjectTypable = ConnectionInfoType{}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package postgresflexalpha
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
)
|
||||
|
||||
type useStateForUnknownIfFlavorUnchangedModifier struct {
|
||||
Req resource.SchemaRequest
|
||||
}
|
||||
|
||||
// UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown
|
||||
// if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing.
|
||||
func UseStateForUnknownIfFlavorUnchanged(req resource.SchemaRequest) planmodifier.String {
|
||||
return useStateForUnknownIfFlavorUnchangedModifier{
|
||||
Req: req,
|
||||
}
|
||||
}
|
||||
|
||||
func (m useStateForUnknownIfFlavorUnchangedModifier) Description(context.Context) string {
|
||||
return "UseStateForUnknownIfFlavorUnchanged returns a plan modifier similar to UseStateForUnknown if the RAM and CPU values are not changed in the plan. Otherwise, the plan modifier does nothing."
|
||||
}
|
||||
|
||||
func (m useStateForUnknownIfFlavorUnchangedModifier) MarkdownDescription(ctx context.Context) string {
|
||||
return m.Description(ctx)
|
||||
}
|
||||
|
||||
func (m useStateForUnknownIfFlavorUnchangedModifier) PlanModifyString(ctx context.Context, req planmodifier.StringRequest, resp *planmodifier.StringResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Do nothing if there is no state value.
|
||||
if req.StateValue.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
// Do nothing if there is a known planned value.
|
||||
if !req.PlanValue.IsUnknown() {
|
||||
return
|
||||
}
|
||||
|
||||
// Do nothing if there is an unknown configuration value, otherwise interpolation gets messed up.
|
||||
if req.ConfigValue.IsUnknown() {
|
||||
return
|
||||
}
|
||||
|
||||
// The above checks are taken from the UseStateForUnknown plan modifier implementation
|
||||
// (https://github.com/hashicorp/terraform-plugin-framework/blob/main/resource/schema/stringplanmodifier/use_state_for_unknown.go#L38)
|
||||
|
||||
var stateModel LocalInstanceModel
|
||||
diags := req.State.Get(ctx, &stateModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var stateFlavor = &flavorModel{}
|
||||
if !(stateModel.Flavor.IsNull() || stateModel.Flavor.IsUnknown()) {
|
||||
diags = stateModel.Flavor.As(ctx, stateFlavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var planModel LocalInstanceModel
|
||||
diags = req.Plan.Get(ctx, &planModel)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var planFlavor = &flavorModel{}
|
||||
if !(planModel.Flavor.IsNull() || planModel.Flavor.IsUnknown()) {
|
||||
diags = planModel.Flavor.As(ctx, planFlavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if planFlavor.CPU == stateFlavor.CPU && planFlavor.RAM == stateFlavor.RAM {
|
||||
resp.PlanValue = req.StateValue
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue