fix: #106 postgresflex flavor datasource

feat: support V2 and V3 flavor handling in postgresflex
This commit is contained in:
Marcel S. Henselin 2026-05-07 06:15:59 +02:00
parent b51d6369f2
commit b56a41dc04
34 changed files with 2986 additions and 604 deletions

View file

@ -226,8 +226,8 @@ func (r *flavorDataSource) Read(ctx context.Context, req datasource.ReadRequest,
postgresflexalphaGen.StorageClassesValue{}.AttributeTypes(ctx),
map[string]attr.Value{
"class": types.StringValue(sc.Class),
"max_io_per_sec": types.Int32Value(sc.MaxIoPerSec),
"max_through_in_mb": types.Int32Value(sc.MaxThroughInMb),
"max_io_per_sec": types.Int64Value(int64(sc.GetMaxIoPerSec())),
"max_through_in_mb": types.Int64Value(int64(sc.GetMaxThroughInMb())),
},
),
)

View file

@ -1,4 +1,4 @@
package postgresflexalpha
package postgresflexalphaflavors
import (
"context"

View file

@ -0,0 +1,65 @@
package postgresflexalphaflavors
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
}

View file

@ -0,0 +1,135 @@
package postgresflexalphaflavors
/*
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))
}
}
*/

View file

@ -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"`

View file

@ -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
}

View file

@ -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))
}
}
*/

View file

@ -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)

View file

@ -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...)

View file

@ -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{}

View file

@ -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
}
}

View file

@ -0,0 +1,101 @@
provider "stackitprivatepreview" {
default_region = "{{ .Region }}"
service_account_key_path = "{{ .ServiceAccountFilePath }}"
}
data "stackitprivatepreview_postgresflexalpha_flavor" "flavor" {
project_id = "{{ .ProjectID }}"
region = "{{ .Region }}"
cpu = 4
ram = 16
node_type = "Single"
storage_class = "premium-perf2-stackit"
}
resource "stackitprivatepreview_postgresflexalpha_instance" "{{ .TfName }}" {
project_id = "{{ .ProjectID }}"
name = "{{ .Name }}"
backup_schedule = "{{ .BackupSchedule }}"
retention_days = {{ .RetentionDays }}
flavor_id = "{{ .FlavorID }}"
replicas = {{ .Replicas }}
storage = {
performance_class = "{{ .PerformanceClass }}"
size = {{ .Size }}
}
{{ if .UseEncryption }}
encryption = {
kek_key_id = "{{ .KekKeyID }}"
kek_key_ring_id = "{{ .KekKeyRingID }}"
kek_key_version = {{ .KekKeyVersion }}
service_account = "{{ .KekServiceAccount }}"
}
{{ end }}
network = {
acl = [{{ range $i, $v := .ACLStrings }}{{if $i}},{{end}}"{{$v}}"{{end}}]
access_scope = "{{ .AccessScope }}"
}
{{ if .Version }}
version = "{{ .Version }}"
{{ end }}
}
{{ if .Users }}
{{ $tfName := .TfName }}
{{ range $user := .Users }}
resource "stackitprivatepreview_postgresflexalpha_user" "{{ $user.Name }}" {
depends_on = [
stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}
]
project_id = "{{ $user.ProjectID }}"
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id
name = "{{ $user.Name }}"
roles = [{{ range $i, $v := $user.Roles }}{{if $i}},{{end}}"{{$v}}"{{end}}]
}
{{ end }}
{{ end }}
{{ if .Databases }}
{{ $tfName := .TfName }}
{{ range $db := .Databases }}
resource "stackitprivatepreview_postgresflexalpha_database" "{{ $db.Name }}" {
depends_on = [
stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }},
stackitprivatepreview_postgresflexalpha_user.{{ $db.Owner }}
]
project_id = "{{ $db.ProjectID }}"
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id
name = "{{ $db.Name }}"
owner = stackitprivatepreview_postgresflexalpha_user.{{ $db.Owner }}.name
}
{{ end }}
{{ end }}
{{ if .DataSourceTest }}
data "stackitprivatepreview_postgresflexalpha_instance" "{{ .TfName }}" {
project_id = stackitprivatepreview_postgresflexalpha_instance.{{ .TfName }}.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ .TfName }}.instance_id
}
{{ if .Users }}
{{ $tfName := .TfName }}
{{ range $user := .Users }}
data "stackitprivatepreview_postgresflexalpha_user" "{{ $user.Name }}" {
project_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id
user_id = stackitprivatepreview_postgresflexalpha_user.{{ $user.Name }}.user_id
}
{{ end }}
{{ end }}
{{ if .Databases }}
{{ $tfName := .TfName }}
{{ range $db := .Databases }}
data "stackitprivatepreview_postgresflexalpha_database" "{{ $db.Name }}" {
project_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.{{ $tfName }}.instance_id
database_id = stackitprivatepreview_postgresflexalpha_database.{{ $db.Name }}.database_id
}
{{ end }}
{{ end }}
{{ end }}

View file

@ -0,0 +1,451 @@
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package sqlserverflexalpha
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func CollationsDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"collations": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"collation_name": schema.StringAttribute{
Computed: true,
},
"description": schema.StringAttribute{
Computed: true,
},
},
CustomType: CollationsType{
ObjectType: types.ObjectType{
AttrTypes: CollationsValue{}.AttributeTypes(ctx),
},
},
},
Computed: true,
Description: "List of collations available for the instance.",
MarkdownDescription: "List of collations available for the instance.",
},
"instance_id": schema.StringAttribute{
Required: true,
Description: "The ID of the instance.",
MarkdownDescription: "The ID of the instance.",
},
"project_id": schema.StringAttribute{
Required: true,
Description: "The STACKIT project ID.",
MarkdownDescription: "The STACKIT project ID.",
},
"region": schema.StringAttribute{
Optional: true,
Description: "The region which should be addressed",
MarkdownDescription: "The region which should be addressed",
Validators: []validator.String{
stringvalidator.OneOf(
"eu01",
),
},
},
},
}
}
type CollationsModel struct {
Collations types.List `tfsdk:"collations"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
}
var _ basetypes.ObjectTypable = CollationsType{}
type CollationsType struct {
basetypes.ObjectType
}
func (t CollationsType) Equal(o attr.Type) bool {
other, ok := o.(CollationsType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t CollationsType) String() string {
return "CollationsType"
}
func (t CollationsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
collationNameAttribute, ok := attributes["collation_name"]
if !ok {
diags.AddError(
"Attribute Missing",
`collation_name is missing from object`)
return nil, diags
}
collationNameVal, ok := collationNameAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`collation_name expected to be basetypes.StringValue, was: %T`, collationNameAttribute))
}
descriptionAttribute, ok := attributes["description"]
if !ok {
diags.AddError(
"Attribute Missing",
`description is missing from object`)
return nil, diags
}
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
}
if diags.HasError() {
return nil, diags
}
return CollationsValue{
CollationName: collationNameVal,
Description: descriptionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewCollationsValueNull() CollationsValue {
return CollationsValue{
state: attr.ValueStateNull,
}
}
func NewCollationsValueUnknown() CollationsValue {
return CollationsValue{
state: attr.ValueStateUnknown,
}
}
func NewCollationsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CollationsValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing CollationsValue Attribute Value",
"While creating a CollationsValue value, a missing attribute value was detected. "+
"A CollationsValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("CollationsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid CollationsValue Attribute Type",
"While creating a CollationsValue value, an invalid attribute value was detected. "+
"A CollationsValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("CollationsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("CollationsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra CollationsValue Attribute Value",
"While creating a CollationsValue value, an extra attribute value was detected. "+
"A CollationsValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra CollationsValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewCollationsValueUnknown(), diags
}
collationNameAttribute, ok := attributes["collation_name"]
if !ok {
diags.AddError(
"Attribute Missing",
`collation_name is missing from object`)
return NewCollationsValueUnknown(), diags
}
collationNameVal, ok := collationNameAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`collation_name expected to be basetypes.StringValue, was: %T`, collationNameAttribute))
}
descriptionAttribute, ok := attributes["description"]
if !ok {
diags.AddError(
"Attribute Missing",
`description is missing from object`)
return NewCollationsValueUnknown(), diags
}
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
}
if diags.HasError() {
return NewCollationsValueUnknown(), diags
}
return CollationsValue{
CollationName: collationNameVal,
Description: descriptionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewCollationsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CollationsValue {
object, diags := NewCollationsValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewCollationsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t CollationsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewCollationsValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewCollationsValueUnknown(), nil
}
if in.IsNull() {
return NewCollationsValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewCollationsValueMust(CollationsValue{}.AttributeTypes(ctx), attributes), nil
}
func (t CollationsType) ValueType(ctx context.Context) attr.Value {
return CollationsValue{}
}
var _ basetypes.ObjectValuable = CollationsValue{}
type CollationsValue struct {
CollationName basetypes.StringValue `tfsdk:"collation_name"`
Description basetypes.StringValue `tfsdk:"description"`
state attr.ValueState
}
func (v CollationsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 2)
var val tftypes.Value
var err error
attrTypes["collation_name"] = basetypes.StringType{}.TerraformType(ctx)
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 2)
val, err = v.CollationName.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["collation_name"] = val
val, err = v.Description.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["description"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v CollationsValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v CollationsValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v CollationsValue) String() string {
return "CollationsValue"
}
func (v CollationsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"collation_name": basetypes.StringType{},
"description": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"collation_name": v.CollationName,
"description": v.Description,
})
return objVal, diags
}
func (v CollationsValue) Equal(o attr.Value) bool {
other, ok := o.(CollationsValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.CollationName.Equal(other.CollationName) {
return false
}
if !v.Description.Equal(other.Description) {
return false
}
return true
}
func (v CollationsValue) Type(ctx context.Context) attr.Type {
return CollationsType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v CollationsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"collation_name": basetypes.StringType{},
"description": basetypes.StringType{},
}
}

View file

@ -0,0 +1,569 @@
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package sqlserverflexalpha
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func VersionsDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"project_id": schema.StringAttribute{
Required: true,
Description: "The STACKIT project ID.",
MarkdownDescription: "The STACKIT project ID.",
},
"region": schema.StringAttribute{
Optional: true,
Description: "The region which should be addressed",
MarkdownDescription: "The region which should be addressed",
Validators: []validator.String{
stringvalidator.OneOf(
"eu01",
),
},
},
"versions": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"beta": schema.BoolAttribute{
Computed: true,
Description: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
MarkdownDescription: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
},
"deprecated": schema.StringAttribute{
Computed: true,
Description: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
MarkdownDescription: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
},
"recommend": schema.BoolAttribute{
Computed: true,
Description: "Flag if the version is recommend by the STACKIT Team.",
MarkdownDescription: "Flag if the version is recommend by the STACKIT Team.",
},
"version": schema.StringAttribute{
Computed: true,
Description: "The sqlserver version used for the instance.",
MarkdownDescription: "The sqlserver version used for the instance.",
},
},
CustomType: VersionsType{
ObjectType: types.ObjectType{
AttrTypes: VersionsValue{}.AttributeTypes(ctx),
},
},
},
Computed: true,
Description: "A list containing available sqlserver versions.",
MarkdownDescription: "A list containing available sqlserver versions.",
},
},
}
}
type VersionsModel struct {
ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
Versions types.List `tfsdk:"versions"`
}
var _ basetypes.ObjectTypable = VersionsType{}
type VersionsType struct {
basetypes.ObjectType
}
func (t VersionsType) Equal(o attr.Type) bool {
other, ok := o.(VersionsType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t VersionsType) String() string {
return "VersionsType"
}
func (t VersionsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
betaAttribute, ok := attributes["beta"]
if !ok {
diags.AddError(
"Attribute Missing",
`beta is missing from object`)
return nil, diags
}
betaVal, ok := betaAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
}
deprecatedAttribute, ok := attributes["deprecated"]
if !ok {
diags.AddError(
"Attribute Missing",
`deprecated is missing from object`)
return nil, diags
}
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
}
recommendAttribute, ok := attributes["recommend"]
if !ok {
diags.AddError(
"Attribute Missing",
`recommend is missing from object`)
return nil, diags
}
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
}
versionAttribute, ok := attributes["version"]
if !ok {
diags.AddError(
"Attribute Missing",
`version is missing from object`)
return nil, diags
}
versionVal, ok := versionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
}
if diags.HasError() {
return nil, diags
}
return VersionsValue{
Beta: betaVal,
Deprecated: deprecatedVal,
Recommend: recommendVal,
Version: versionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewVersionsValueNull() VersionsValue {
return VersionsValue{
state: attr.ValueStateNull,
}
}
func NewVersionsValueUnknown() VersionsValue {
return VersionsValue{
state: attr.ValueStateUnknown,
}
}
func NewVersionsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (VersionsValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing VersionsValue Attribute Value",
"While creating a VersionsValue value, a missing attribute value was detected. "+
"A VersionsValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid VersionsValue Attribute Type",
"While creating a VersionsValue value, an invalid attribute value was detected. "+
"A VersionsValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("VersionsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra VersionsValue Attribute Value",
"While creating a VersionsValue value, an extra attribute value was detected. "+
"A VersionsValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra VersionsValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewVersionsValueUnknown(), diags
}
betaAttribute, ok := attributes["beta"]
if !ok {
diags.AddError(
"Attribute Missing",
`beta is missing from object`)
return NewVersionsValueUnknown(), diags
}
betaVal, ok := betaAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
}
deprecatedAttribute, ok := attributes["deprecated"]
if !ok {
diags.AddError(
"Attribute Missing",
`deprecated is missing from object`)
return NewVersionsValueUnknown(), diags
}
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
}
recommendAttribute, ok := attributes["recommend"]
if !ok {
diags.AddError(
"Attribute Missing",
`recommend is missing from object`)
return NewVersionsValueUnknown(), diags
}
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
}
versionAttribute, ok := attributes["version"]
if !ok {
diags.AddError(
"Attribute Missing",
`version is missing from object`)
return NewVersionsValueUnknown(), diags
}
versionVal, ok := versionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
}
if diags.HasError() {
return NewVersionsValueUnknown(), diags
}
return VersionsValue{
Beta: betaVal,
Deprecated: deprecatedVal,
Recommend: recommendVal,
Version: versionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewVersionsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) VersionsValue {
object, diags := NewVersionsValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewVersionsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t VersionsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewVersionsValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewVersionsValueUnknown(), nil
}
if in.IsNull() {
return NewVersionsValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewVersionsValueMust(VersionsValue{}.AttributeTypes(ctx), attributes), nil
}
func (t VersionsType) ValueType(ctx context.Context) attr.Value {
return VersionsValue{}
}
var _ basetypes.ObjectValuable = VersionsValue{}
type VersionsValue struct {
Beta basetypes.BoolValue `tfsdk:"beta"`
Deprecated basetypes.StringValue `tfsdk:"deprecated"`
Recommend basetypes.BoolValue `tfsdk:"recommend"`
Version basetypes.StringValue `tfsdk:"version"`
state attr.ValueState
}
func (v VersionsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 4)
var val tftypes.Value
var err error
attrTypes["beta"] = basetypes.BoolType{}.TerraformType(ctx)
attrTypes["deprecated"] = basetypes.StringType{}.TerraformType(ctx)
attrTypes["recommend"] = basetypes.BoolType{}.TerraformType(ctx)
attrTypes["version"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 4)
val, err = v.Beta.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["beta"] = val
val, err = v.Deprecated.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["deprecated"] = val
val, err = v.Recommend.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["recommend"] = val
val, err = v.Version.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["version"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v VersionsValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v VersionsValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v VersionsValue) String() string {
return "VersionsValue"
}
func (v VersionsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"beta": basetypes.BoolType{},
"deprecated": basetypes.StringType{},
"recommend": basetypes.BoolType{},
"version": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"beta": v.Beta,
"deprecated": v.Deprecated,
"recommend": v.Recommend,
"version": v.Version,
})
return objVal, diags
}
func (v VersionsValue) Equal(o attr.Value) bool {
other, ok := o.(VersionsValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.Beta.Equal(other.Beta) {
return false
}
if !v.Deprecated.Equal(other.Deprecated) {
return false
}
if !v.Recommend.Equal(other.Recommend) {
return false
}
if !v.Version.Equal(other.Version) {
return false
}
return true
}
func (v VersionsValue) Type(ctx context.Context) attr.Type {
return VersionsType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v VersionsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"beta": basetypes.BoolType{},
"deprecated": basetypes.StringType{},
"recommend": basetypes.BoolType{},
"version": basetypes.StringType{},
}
}

View file

@ -0,0 +1,452 @@
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package sqlserverflexbeta
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func CollationsDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"collations": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"collation_name": schema.StringAttribute{
Computed: true,
},
"description": schema.StringAttribute{
Computed: true,
},
},
CustomType: CollationsType{
ObjectType: types.ObjectType{
AttrTypes: CollationsValue{}.AttributeTypes(ctx),
},
},
},
Computed: true,
Description: "List of collations available for the instance.",
MarkdownDescription: "List of collations available for the instance.",
},
"instance_id": schema.StringAttribute{
Required: true,
Description: "The ID of the instance.",
MarkdownDescription: "The ID of the instance.",
},
"project_id": schema.StringAttribute{
Required: true,
Description: "The STACKIT project ID.",
MarkdownDescription: "The STACKIT project ID.",
},
"region": schema.StringAttribute{
Optional: true,
Description: "The region which should be addressed",
MarkdownDescription: "The region which should be addressed",
Validators: []validator.String{
stringvalidator.OneOf(
"eu01",
"eu02",
),
},
},
},
}
}
type CollationsModel struct {
Collations types.List `tfsdk:"collations"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
}
var _ basetypes.ObjectTypable = CollationsType{}
type CollationsType struct {
basetypes.ObjectType
}
func (t CollationsType) Equal(o attr.Type) bool {
other, ok := o.(CollationsType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t CollationsType) String() string {
return "CollationsType"
}
func (t CollationsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
collationNameAttribute, ok := attributes["collation_name"]
if !ok {
diags.AddError(
"Attribute Missing",
`collation_name is missing from object`)
return nil, diags
}
collationNameVal, ok := collationNameAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`collation_name expected to be basetypes.StringValue, was: %T`, collationNameAttribute))
}
descriptionAttribute, ok := attributes["description"]
if !ok {
diags.AddError(
"Attribute Missing",
`description is missing from object`)
return nil, diags
}
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
}
if diags.HasError() {
return nil, diags
}
return CollationsValue{
CollationName: collationNameVal,
Description: descriptionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewCollationsValueNull() CollationsValue {
return CollationsValue{
state: attr.ValueStateNull,
}
}
func NewCollationsValueUnknown() CollationsValue {
return CollationsValue{
state: attr.ValueStateUnknown,
}
}
func NewCollationsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CollationsValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing CollationsValue Attribute Value",
"While creating a CollationsValue value, a missing attribute value was detected. "+
"A CollationsValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("CollationsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid CollationsValue Attribute Type",
"While creating a CollationsValue value, an invalid attribute value was detected. "+
"A CollationsValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("CollationsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("CollationsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra CollationsValue Attribute Value",
"While creating a CollationsValue value, an extra attribute value was detected. "+
"A CollationsValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra CollationsValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewCollationsValueUnknown(), diags
}
collationNameAttribute, ok := attributes["collation_name"]
if !ok {
diags.AddError(
"Attribute Missing",
`collation_name is missing from object`)
return NewCollationsValueUnknown(), diags
}
collationNameVal, ok := collationNameAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`collation_name expected to be basetypes.StringValue, was: %T`, collationNameAttribute))
}
descriptionAttribute, ok := attributes["description"]
if !ok {
diags.AddError(
"Attribute Missing",
`description is missing from object`)
return NewCollationsValueUnknown(), diags
}
descriptionVal, ok := descriptionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`description expected to be basetypes.StringValue, was: %T`, descriptionAttribute))
}
if diags.HasError() {
return NewCollationsValueUnknown(), diags
}
return CollationsValue{
CollationName: collationNameVal,
Description: descriptionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewCollationsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CollationsValue {
object, diags := NewCollationsValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewCollationsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t CollationsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewCollationsValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewCollationsValueUnknown(), nil
}
if in.IsNull() {
return NewCollationsValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewCollationsValueMust(CollationsValue{}.AttributeTypes(ctx), attributes), nil
}
func (t CollationsType) ValueType(ctx context.Context) attr.Value {
return CollationsValue{}
}
var _ basetypes.ObjectValuable = CollationsValue{}
type CollationsValue struct {
CollationName basetypes.StringValue `tfsdk:"collation_name"`
Description basetypes.StringValue `tfsdk:"description"`
state attr.ValueState
}
func (v CollationsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 2)
var val tftypes.Value
var err error
attrTypes["collation_name"] = basetypes.StringType{}.TerraformType(ctx)
attrTypes["description"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 2)
val, err = v.CollationName.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["collation_name"] = val
val, err = v.Description.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["description"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v CollationsValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v CollationsValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v CollationsValue) String() string {
return "CollationsValue"
}
func (v CollationsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"collation_name": basetypes.StringType{},
"description": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"collation_name": v.CollationName,
"description": v.Description,
})
return objVal, diags
}
func (v CollationsValue) Equal(o attr.Value) bool {
other, ok := o.(CollationsValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.CollationName.Equal(other.CollationName) {
return false
}
if !v.Description.Equal(other.Description) {
return false
}
return true
}
func (v CollationsValue) Type(ctx context.Context) attr.Type {
return CollationsType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v CollationsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"collation_name": basetypes.StringType{},
"description": basetypes.StringType{},
}
}

View file

@ -0,0 +1,570 @@
// Code generated by terraform-plugin-framework-generator DO NOT EDIT.
package sqlserverflexbeta
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
)
func VersionsDataSourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
"project_id": schema.StringAttribute{
Required: true,
Description: "The STACKIT project ID.",
MarkdownDescription: "The STACKIT project ID.",
},
"region": schema.StringAttribute{
Optional: true,
Description: "The region which should be addressed",
MarkdownDescription: "The region which should be addressed",
Validators: []validator.String{
stringvalidator.OneOf(
"eu01",
"eu02",
),
},
},
"versions": schema.ListNestedAttribute{
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"beta": schema.BoolAttribute{
Computed: true,
Description: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
MarkdownDescription: "Flag if the version is a beta version. If set the version may contain bugs and is not fully tested.",
},
"deprecated": schema.StringAttribute{
Computed: true,
Description: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
MarkdownDescription: "Timestamp in RFC3339 format which says when the version will no longer be supported by STACKIT.",
},
"recommend": schema.BoolAttribute{
Computed: true,
Description: "Flag if the version is recommend by the STACKIT Team.",
MarkdownDescription: "Flag if the version is recommend by the STACKIT Team.",
},
"version": schema.StringAttribute{
Computed: true,
Description: "The sqlserver version used for the instance.",
MarkdownDescription: "The sqlserver version used for the instance.",
},
},
CustomType: VersionsType{
ObjectType: types.ObjectType{
AttrTypes: VersionsValue{}.AttributeTypes(ctx),
},
},
},
Computed: true,
Description: "A list containing available sqlserver versions.",
MarkdownDescription: "A list containing available sqlserver versions.",
},
},
}
}
type VersionsModel struct {
ProjectId types.String `tfsdk:"project_id"`
Region types.String `tfsdk:"region"`
Versions types.List `tfsdk:"versions"`
}
var _ basetypes.ObjectTypable = VersionsType{}
type VersionsType struct {
basetypes.ObjectType
}
func (t VersionsType) Equal(o attr.Type) bool {
other, ok := o.(VersionsType)
if !ok {
return false
}
return t.ObjectType.Equal(other.ObjectType)
}
func (t VersionsType) String() string {
return "VersionsType"
}
func (t VersionsType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) {
var diags diag.Diagnostics
attributes := in.Attributes()
betaAttribute, ok := attributes["beta"]
if !ok {
diags.AddError(
"Attribute Missing",
`beta is missing from object`)
return nil, diags
}
betaVal, ok := betaAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
}
deprecatedAttribute, ok := attributes["deprecated"]
if !ok {
diags.AddError(
"Attribute Missing",
`deprecated is missing from object`)
return nil, diags
}
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
}
recommendAttribute, ok := attributes["recommend"]
if !ok {
diags.AddError(
"Attribute Missing",
`recommend is missing from object`)
return nil, diags
}
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
}
versionAttribute, ok := attributes["version"]
if !ok {
diags.AddError(
"Attribute Missing",
`version is missing from object`)
return nil, diags
}
versionVal, ok := versionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
}
if diags.HasError() {
return nil, diags
}
return VersionsValue{
Beta: betaVal,
Deprecated: deprecatedVal,
Recommend: recommendVal,
Version: versionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewVersionsValueNull() VersionsValue {
return VersionsValue{
state: attr.ValueStateNull,
}
}
func NewVersionsValueUnknown() VersionsValue {
return VersionsValue{
state: attr.ValueStateUnknown,
}
}
func NewVersionsValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (VersionsValue, diag.Diagnostics) {
var diags diag.Diagnostics
// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521
ctx := context.Background()
for name, attributeType := range attributeTypes {
attribute, ok := attributes[name]
if !ok {
diags.AddError(
"Missing VersionsValue Attribute Value",
"While creating a VersionsValue value, a missing attribute value was detected. "+
"A VersionsValue must contain values for all attributes, even if null or unknown. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()),
)
continue
}
if !attributeType.Equal(attribute.Type(ctx)) {
diags.AddError(
"Invalid VersionsValue Attribute Type",
"While creating a VersionsValue value, an invalid attribute value was detected. "+
"A VersionsValue must use a matching attribute type for the value. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("VersionsValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+
fmt.Sprintf("VersionsValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)),
)
}
}
for name := range attributes {
_, ok := attributeTypes[name]
if !ok {
diags.AddError(
"Extra VersionsValue Attribute Value",
"While creating a VersionsValue value, an extra attribute value was detected. "+
"A VersionsValue must not contain values beyond the expected attribute types. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Extra VersionsValue Attribute Name: %s", name),
)
}
}
if diags.HasError() {
return NewVersionsValueUnknown(), diags
}
betaAttribute, ok := attributes["beta"]
if !ok {
diags.AddError(
"Attribute Missing",
`beta is missing from object`)
return NewVersionsValueUnknown(), diags
}
betaVal, ok := betaAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`beta expected to be basetypes.BoolValue, was: %T`, betaAttribute))
}
deprecatedAttribute, ok := attributes["deprecated"]
if !ok {
diags.AddError(
"Attribute Missing",
`deprecated is missing from object`)
return NewVersionsValueUnknown(), diags
}
deprecatedVal, ok := deprecatedAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`deprecated expected to be basetypes.StringValue, was: %T`, deprecatedAttribute))
}
recommendAttribute, ok := attributes["recommend"]
if !ok {
diags.AddError(
"Attribute Missing",
`recommend is missing from object`)
return NewVersionsValueUnknown(), diags
}
recommendVal, ok := recommendAttribute.(basetypes.BoolValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`recommend expected to be basetypes.BoolValue, was: %T`, recommendAttribute))
}
versionAttribute, ok := attributes["version"]
if !ok {
diags.AddError(
"Attribute Missing",
`version is missing from object`)
return NewVersionsValueUnknown(), diags
}
versionVal, ok := versionAttribute.(basetypes.StringValue)
if !ok {
diags.AddError(
"Attribute Wrong Type",
fmt.Sprintf(`version expected to be basetypes.StringValue, was: %T`, versionAttribute))
}
if diags.HasError() {
return NewVersionsValueUnknown(), diags
}
return VersionsValue{
Beta: betaVal,
Deprecated: deprecatedVal,
Recommend: recommendVal,
Version: versionVal,
state: attr.ValueStateKnown,
}, diags
}
func NewVersionsValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) VersionsValue {
object, diags := NewVersionsValue(attributeTypes, attributes)
if diags.HasError() {
// This could potentially be added to the diag package.
diagsStrings := make([]string, 0, len(diags))
for _, diagnostic := range diags {
diagsStrings = append(diagsStrings, fmt.Sprintf(
"%s | %s | %s",
diagnostic.Severity(),
diagnostic.Summary(),
diagnostic.Detail()))
}
panic("NewVersionsValueMust received error(s): " + strings.Join(diagsStrings, "\n"))
}
return object
}
func (t VersionsType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) {
if in.Type() == nil {
return NewVersionsValueNull(), nil
}
if !in.Type().Equal(t.TerraformType(ctx)) {
return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type())
}
if !in.IsKnown() {
return NewVersionsValueUnknown(), nil
}
if in.IsNull() {
return NewVersionsValueNull(), nil
}
attributes := map[string]attr.Value{}
val := map[string]tftypes.Value{}
err := in.As(&val)
if err != nil {
return nil, err
}
for k, v := range val {
a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v)
if err != nil {
return nil, err
}
attributes[k] = a
}
return NewVersionsValueMust(VersionsValue{}.AttributeTypes(ctx), attributes), nil
}
func (t VersionsType) ValueType(ctx context.Context) attr.Value {
return VersionsValue{}
}
var _ basetypes.ObjectValuable = VersionsValue{}
type VersionsValue struct {
Beta basetypes.BoolValue `tfsdk:"beta"`
Deprecated basetypes.StringValue `tfsdk:"deprecated"`
Recommend basetypes.BoolValue `tfsdk:"recommend"`
Version basetypes.StringValue `tfsdk:"version"`
state attr.ValueState
}
func (v VersionsValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := make(map[string]tftypes.Type, 4)
var val tftypes.Value
var err error
attrTypes["beta"] = basetypes.BoolType{}.TerraformType(ctx)
attrTypes["deprecated"] = basetypes.StringType{}.TerraformType(ctx)
attrTypes["recommend"] = basetypes.BoolType{}.TerraformType(ctx)
attrTypes["version"] = basetypes.StringType{}.TerraformType(ctx)
objectType := tftypes.Object{AttributeTypes: attrTypes}
switch v.state {
case attr.ValueStateKnown:
vals := make(map[string]tftypes.Value, 4)
val, err = v.Beta.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["beta"] = val
val, err = v.Deprecated.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["deprecated"] = val
val, err = v.Recommend.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["recommend"] = val
val, err = v.Version.ToTerraformValue(ctx)
if err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
vals["version"] = val
if err := tftypes.ValidateValue(objectType, vals); err != nil {
return tftypes.NewValue(objectType, tftypes.UnknownValue), err
}
return tftypes.NewValue(objectType, vals), nil
case attr.ValueStateNull:
return tftypes.NewValue(objectType, nil), nil
case attr.ValueStateUnknown:
return tftypes.NewValue(objectType, tftypes.UnknownValue), nil
default:
panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state))
}
}
func (v VersionsValue) IsNull() bool {
return v.state == attr.ValueStateNull
}
func (v VersionsValue) IsUnknown() bool {
return v.state == attr.ValueStateUnknown
}
func (v VersionsValue) String() string {
return "VersionsValue"
}
func (v VersionsValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) {
var diags diag.Diagnostics
attributeTypes := map[string]attr.Type{
"beta": basetypes.BoolType{},
"deprecated": basetypes.StringType{},
"recommend": basetypes.BoolType{},
"version": basetypes.StringType{},
}
if v.IsNull() {
return types.ObjectNull(attributeTypes), diags
}
if v.IsUnknown() {
return types.ObjectUnknown(attributeTypes), diags
}
objVal, diags := types.ObjectValue(
attributeTypes,
map[string]attr.Value{
"beta": v.Beta,
"deprecated": v.Deprecated,
"recommend": v.Recommend,
"version": v.Version,
})
return objVal, diags
}
func (v VersionsValue) Equal(o attr.Value) bool {
other, ok := o.(VersionsValue)
if !ok {
return false
}
if v.state != other.state {
return false
}
if v.state != attr.ValueStateKnown {
return true
}
if !v.Beta.Equal(other.Beta) {
return false
}
if !v.Deprecated.Equal(other.Deprecated) {
return false
}
if !v.Recommend.Equal(other.Recommend) {
return false
}
if !v.Version.Equal(other.Version) {
return false
}
return true
}
func (v VersionsValue) Type(ctx context.Context) attr.Type {
return VersionsType{
basetypes.ObjectType{
AttrTypes: v.AttributeTypes(ctx),
},
}
}
func (v VersionsValue) AttributeTypes(ctx context.Context) map[string]attr.Type {
return map[string]attr.Type{
"beta": basetypes.BoolType{},
"deprecated": basetypes.StringType{},
"recommend": basetypes.BoolType{},
"version": basetypes.StringType{},
}
}

View file

@ -20,10 +20,9 @@ import (
sdkauth "github.com/stackitcloud/stackit-sdk-go/core/auth"
"github.com/stackitcloud/stackit-sdk-go/core/config"
sqlserverflexalphaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database"
sqlserverflexalphaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
sqlserverflexalphaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user"
sqlserverflexbetaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/user"
//sqlserverflexalphaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database"
//sqlserverflexalphaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/instance"
//sqlserverflexalphaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user"
"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/features"
@ -36,7 +35,7 @@ import (
sqlserverFlexBetaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/database"
sqlserverflexBetaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/instance"
// sqlserverFlexBetaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbetaUser/user"
sqlserverflexbetaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/user"
)
// Ensure the implementation satisfies the expected interfaces
@ -536,9 +535,9 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
postgresflexalphaFlavors.NewFlavorsDataSource,
// sqlserverFlexAlphaFlavor.NewFlavorDataSource,
sqlserverflexalphaInstance.NewInstanceDataSource,
sqlserverflexalphaUser.NewUserDataSource,
sqlserverflexalphaDatabase.NewDatabaseDataSource,
//sqlserverflexalphaInstance.NewInstanceDataSource,
//sqlserverflexalphaUser.NewUserDataSource,
//sqlserverflexalphaDatabase.NewDatabaseDataSource,
sqlserverFlexBetaDatabase.NewDatabaseDataSource,
sqlserverflexBetaInstance.NewInstanceDataSource,
@ -554,9 +553,9 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
postgresFlexAlphaUser.NewUserResource,
postgresFlexAlphaDatabase.NewDatabaseResource,
sqlserverflexalphaInstance.NewInstanceResource,
sqlserverflexalphaUser.NewUserResource,
sqlserverflexalphaDatabase.NewDatabaseResource,
//sqlserverflexalphaInstance.NewInstanceResource,
//sqlserverflexalphaUser.NewUserResource,
//sqlserverflexalphaDatabase.NewDatabaseResource,
sqlserverflexBetaInstance.NewInstanceResource,
sqlserverflexbetaUser.NewUserResource,