fix: fixed some tests

This commit is contained in:
Marcel S. Henselin 2025-12-29 11:10:42 +01:00
parent 5b6576da1c
commit ff9f47edc3
12 changed files with 941 additions and 418 deletions

View file

@ -44,13 +44,13 @@ import {
- `backup_schedule` (String) - `backup_schedule` (String)
- `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption)) - `encryption` (Attributes) The encryption block. (see [below for nested schema](#nestedatt--encryption))
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor)) - `flavor` (Attributes) The block that defines the flavor data. (see [below for nested schema](#nestedatt--flavor))
- `name` (String) Instance name. - `name` (String) Instance name.
- `network` (Attributes) (see [below for nested schema](#nestedatt--network)) - `network` (Attributes) The network block configuration. (see [below for nested schema](#nestedatt--network))
- `project_id` (String) STACKIT project ID to which the instance is associated. - `project_id` (String) STACKIT project ID to which the instance is associated.
- `replicas` (Number) - `replicas` (Number)
- `storage` (Attributes) (see [below for nested schema](#nestedatt--storage)) - `storage` (Attributes) (see [below for nested schema](#nestedatt--storage))
- `version` (String) - `version` (String) The database version used.
### Optional ### Optional
@ -67,9 +67,9 @@ import {
Required: Required:
- `key_id` (String) Key ID of the encryption key. - `key_id` (String) Key ID of the encryption key.
- `key_version` (String) - `key_version` (String) Key version of the encryption key.
- `keyring_id` (String) - `keyring_id` (String) KeyRing ID of the encryption key.
- `service_account` (String) - `service_account` (String) The service account ID of the service account.
<a id="nestedatt--flavor"></a> <a id="nestedatt--flavor"></a>
@ -77,16 +77,17 @@ Required:
Required: Required:
- `cpu` (Number) - `cpu` (Number) The CPU count of the flavor.
- `ram` (Number) - `ram` (Number) The RAM count of the flavor.
Optional: Optional:
- `id` (String) - `id` (String) The ID of the flavor.
- `node_type` (String) The node type of the flavor. (Single or Replicas)
Read-Only: Read-Only:
- `description` (String) - `description` (String) The flavor detailed flavor name.
<a id="nestedatt--network"></a> <a id="nestedatt--network"></a>
@ -98,9 +99,9 @@ Required:
Optional: Optional:
- `access_scope` (String) - `access_scope` (String) The access scope. (Either SNA or PUBLIC)
- `instance_address` (String) - `instance_address` (String) The returned instance address.
- `router_address` (String) - `router_address` (String) The returned router address.
<a id="nestedatt--storage"></a> <a id="nestedatt--storage"></a>
@ -108,5 +109,5 @@ Optional:
Required: Required:
- `class` (String) - `class` (String) The storage class used.
- `size` (Number) - `size` (Number) The disk size of the storage.

View file

@ -1,11 +1 @@
# see other files # see other files
data "stackitprivatepreview_sqlserverflexalpha_instance" "existing" {
project_id = var.project_id
instance_id = "b31575e9-9dbd-4ff6-b341-82d89c34f14f"
region = "eu01"
}
output "myinstance" {
value = data.stackitprivatepreview_sqlserverflexalpha_instance.existing
}

View file

@ -6,7 +6,7 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "ptlsdbsrv" {
cpu = 2 cpu = 2
ram = 4 ram = 4
} }
replicas = 3 replicas = 1
storage = { storage = {
class = "premium-perf2-stackit" class = "premium-perf2-stackit"
size = 5 size = 5
@ -26,12 +26,12 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "ptlsdbsrv" {
version = 14 version = 14
} }
data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" { # data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" {
project_id = var.project_id # project_id = var.project_id
instance_id = "fdb6573e-2dea-4e1d-a638-9157cf90c3ba" # instance_id = "fdb6573e-2dea-4e1d-a638-9157cf90c3ba"
region = "eu01" # region = "eu01"
} # }
#
output "sample_psqlinstance" { # output "sample_psqlinstance" {
value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql # value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql
} # }

View file

@ -88,3 +88,13 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "ptlsdbsqlsrv" {
# instance_id = var.instance_id # instance_id = var.instance_id
# region = "eu01" # region = "eu01"
# } # }
# data "stackitprivatepreview_sqlserverflexalpha_instance" "existing" {
# project_id = var.project_id
# instance_id = "b31575e9-9dbd-4ff6-b341-82d89c34f14f"
# region = "eu01"
# }
#
# output "myinstance" {
# value = data.stackitprivatepreview_sqlserverflexalpha_instance.existing
# }

View file

@ -1,12 +1,12 @@
data "stackitprivatepreview_postgresflexalpha_user" "example" { data "stackitprivatepreview_postgresflexalpha_user" "example" {
project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.id instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.instance_id
user_id = 1 user_id = 1
} }
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" { resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id project_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.id instance_id = stackitprivatepreview_postgresflexalpha_instance.ptlsdbsrv.instance_id
username = var.db_username username = var.db_username
# roles = ["createdb", "login", "createrole"] # roles = ["createdb", "login", "createrole"]
roles = ["createdb", "login"] roles = ["createdb", "login"]
@ -14,7 +14,7 @@ resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" { resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" {
project_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.project_id project_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.project_id
instance_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.id instance_id = stackitprivatepreview_sqlserverflexalpha_instance.ptlsdbsqlsrv.instance_id
username = var.db_username username = var.db_username
roles = ["login"] roles = ["login"]
} }

View file

@ -127,7 +127,6 @@ func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaReques
// Read refreshes the Terraform state with the latest data. // Read refreshes the Terraform state with the latest data.
func (r *databaseDataSource) Read( func (r *databaseDataSource) Read(
ctx context.Context, ctx context.Context,
// TODO - make it pointer
req datasource.ReadRequest, req datasource.ReadRequest,
resp *datasource.ReadResponse, resp *datasource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform ) { // nolint:gocritic // function signature required by Terraform

View file

@ -58,7 +58,6 @@ type databaseResource struct {
// Use the modifier to set the effective region in the current plan. // Use the modifier to set the effective region in the current plan.
func (r *databaseResource) ModifyPlan( func (r *databaseResource) ModifyPlan(
ctx context.Context, ctx context.Context,
// TODO - make it pointer
req resource.ModifyPlanRequest, req resource.ModifyPlanRequest,
resp *resource.ModifyPlanResponse, resp *resource.ModifyPlanResponse,
) { // nolint:gocritic // function signature required by Terraform ) { // nolint:gocritic // function signature required by Terraform
@ -201,7 +200,6 @@ func (r *databaseResource) Schema(_ context.Context, _ resource.SchemaRequest, r
// Create creates the resource and sets the initial Terraform state. // Create creates the resource and sets the initial Terraform state.
func (r *databaseResource) Create( func (r *databaseResource) Create(
ctx context.Context, ctx context.Context,
// TODO - make it pointer
req resource.CreateRequest, req resource.CreateRequest,
resp *resource.CreateResponse, resp *resource.CreateResponse,
) { // nolint:gocritic // function signature required by Terraform ) { // nolint:gocritic // function signature required by Terraform
@ -292,7 +290,6 @@ func (r *databaseResource) Create(
// Read refreshes the Terraform state with the latest data. // Read refreshes the Terraform state with the latest data.
func (r *databaseResource) Read( func (r *databaseResource) Read(
ctx context.Context, ctx context.Context,
// TODO - make it pointer
req resource.ReadRequest, req resource.ReadRequest,
resp *resource.ReadResponse, resp *resource.ReadResponse,
) { // nolint:gocritic // function signature required by Terraform ) { // nolint:gocritic // function signature required by Terraform

View file

@ -0,0 +1,514 @@
package postgresflex
import (
"context"
"reflect"
"testing"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)
func TestFlavorType_Equal(t1 *testing.T) {
type fields struct {
ObjectType basetypes.ObjectType
}
type args struct {
o attr.Type
}
tests := []struct {
name string
fields fields
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := FlavorType{
ObjectType: tt.fields.ObjectType,
}
if got := t.Equal(tt.args.o); got != tt.want {
t1.Errorf("Equal() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorType_String(t1 *testing.T) {
type fields struct {
ObjectType basetypes.ObjectType
}
tests := []struct {
name string
fields fields
want string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := FlavorType{
ObjectType: tt.fields.ObjectType,
}
if got := t.String(); got != tt.want {
t1.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorType_ValueFromObject(t1 *testing.T) {
type fields struct {
ObjectType basetypes.ObjectType
}
type args struct {
in0 context.Context
in basetypes.ObjectValue
}
tests := []struct {
name string
fields fields
args args
want basetypes.ObjectValuable
want1 diag.Diagnostics
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := FlavorType{
ObjectType: tt.fields.ObjectType,
}
got, got1 := t.ValueFromObject(tt.args.in0, tt.args.in)
if !reflect.DeepEqual(got, tt.want) {
t1.Errorf("ValueFromObject() got = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got1, tt.want1) {
t1.Errorf("ValueFromObject() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestFlavorType_ValueFromTerraform(t1 *testing.T) {
type fields struct {
ObjectType basetypes.ObjectType
}
type args struct {
ctx context.Context
in tftypes.Value
}
tests := []struct {
name string
fields fields
args args
want attr.Value
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := FlavorType{
ObjectType: tt.fields.ObjectType,
}
got, err := t.ValueFromTerraform(tt.args.ctx, tt.args.in)
if (err != nil) != tt.wantErr {
t1.Errorf("ValueFromTerraform() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t1.Errorf("ValueFromTerraform() got = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorType_ValueType(t1 *testing.T) {
type fields struct {
ObjectType basetypes.ObjectType
}
type args struct {
in0 context.Context
}
tests := []struct {
name string
fields fields
args args
want attr.Value
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t1.Run(tt.name, func(t1 *testing.T) {
t := FlavorType{
ObjectType: tt.fields.ObjectType,
}
if got := t.ValueType(tt.args.in0); !reflect.DeepEqual(got, tt.want) {
t1.Errorf("ValueType() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_AttributeTypes(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
type args struct {
in0 context.Context
}
tests := []struct {
name string
fields fields
args args
want map[string]attr.Type
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.AttributeTypes(tt.args.in0); !reflect.DeepEqual(got, tt.want) {
t.Errorf("AttributeTypes() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_Equal(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
type args struct {
o attr.Value
}
tests := []struct {
name string
fields fields
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.Equal(tt.args.o); got != tt.want {
t.Errorf("Equal() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_IsNull(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
tests := []struct {
name string
fields fields
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.IsNull(); got != tt.want {
t.Errorf("IsNull() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_IsUnknown(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
tests := []struct {
name string
fields fields
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.IsUnknown(); got != tt.want {
t.Errorf("IsUnknown() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_String(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
tests := []struct {
name string
fields fields
want string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.String(); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_ToObjectValue(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
type args struct {
in0 context.Context
}
tests := []struct {
name string
fields fields
args args
want basetypes.ObjectValue
want1 diag.Diagnostics
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
got, got1 := v.ToObjectValue(tt.args.in0)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToObjectValue() got = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got1, tt.want1) {
t.Errorf("ToObjectValue() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestFlavorValue_ToTerraformValue(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
type args struct {
ctx context.Context
}
tests := []struct {
name string
fields fields
args args
want tftypes.Value
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
got, err := v.ToTerraformValue(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("ToTerraformValue() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToTerraformValue() got = %v, want %v", got, tt.want)
}
})
}
}
func TestFlavorValue_Type(t *testing.T) {
type fields struct {
Cpu basetypes.Int64Value
Description basetypes.StringValue
Id basetypes.StringValue
Ram basetypes.Int64Value
state attr.ValueState
}
type args struct {
ctx context.Context
}
tests := []struct {
name string
fields fields
args args
want attr.Type
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := FlavorValue{
Cpu: tt.fields.Cpu,
Description: tt.fields.Description,
Id: tt.fields.Id,
Ram: tt.fields.Ram,
state: tt.fields.state,
}
if got := v.Type(tt.args.ctx); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Type() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewFlavorValue(t *testing.T) {
type args struct {
attributeTypes map[string]attr.Type
attributes map[string]attr.Value
}
tests := []struct {
name string
args args
want FlavorValue
want1 diag.Diagnostics
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := NewFlavorValue(tt.args.attributeTypes, tt.args.attributes)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewFlavorValue() got = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got1, tt.want1) {
t.Errorf("NewFlavorValue() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestNewFlavorValueMust(t *testing.T) {
type args struct {
attributeTypes map[string]attr.Type
attributes map[string]attr.Value
}
tests := []struct {
name string
args args
want FlavorValue
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewFlavorValueMust(tt.args.attributeTypes, tt.args.attributes); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewFlavorValueMust() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewFlavorValueNull(t *testing.T) {
tests := []struct {
name string
want FlavorValue
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewFlavorValueNull(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewFlavorValueNull() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewFlavorValueUnknown(t *testing.T) {
tests := []struct {
name string
want FlavorValue
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewFlavorValueUnknown(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewFlavorValueUnknown() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -101,7 +101,7 @@ func mapFields(
} }
var flavorValues map[string]attr.Value var flavorValues map[string]attr.Value
if instance.FlavorId == nil { if instance.FlavorId == nil || *instance.FlavorId == "" {
return fmt.Errorf("instance has no flavor id") return fmt.Errorf("instance has no flavor id")
} }
if !flavor.Id.IsUnknown() && !flavor.Id.IsNull() { if !flavor.Id.IsUnknown() && !flavor.Id.IsNull() {
@ -110,12 +110,28 @@ func mapFields(
} }
} }
if model.Flavor.IsNull() || model.Flavor.IsUnknown() { if model.Flavor.IsNull() || model.Flavor.IsUnknown() {
var nodeType string
if flavor.NodeType.IsUnknown() || flavor.NodeType.IsNull() {
if instance.Replicas == nil {
return fmt.Errorf("instance has no replicas setting")
}
switch *instance.Replicas {
case 1:
nodeType = "Single"
case 3:
nodeType = "Replicas"
default:
return fmt.Errorf("could not determine replicas settings")
}
} else {
nodeType = flavor.NodeType.ValueString()
}
flavorValues = map[string]attr.Value{ flavorValues = map[string]attr.Value{
"id": flavor.Id, "id": flavor.Id,
"description": flavor.Description, "description": flavor.Description,
"cpu": flavor.CPU, "cpu": flavor.CPU,
"ram": flavor.RAM, "ram": flavor.RAM,
"node_type": flavor.NodeType, "node_type": types.StringValue(nodeType),
} }
} else { } else {
flavorValues = model.Flavor.Attributes() flavorValues = model.Flavor.Attributes()
@ -143,6 +159,11 @@ func mapFields(
return fmt.Errorf("creating storage: %w", core.DiagsToError(diags)) return fmt.Errorf("creating storage: %w", core.DiagsToError(diags))
} }
if instance.Replicas == nil {
diags.AddError("error mapping fields", "replicas is nil")
return fmt.Errorf("replicas is nil")
}
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId) model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, instanceId)
model.InstanceId = types.StringValue(instanceId) model.InstanceId = types.StringValue(instanceId)
model.Name = types.StringPointerValue(instance.Name) model.Name = types.StringPointerValue(instance.Name)
@ -159,7 +180,13 @@ func mapFields(
return nil return nil
} }
func toCreatePayload(model *Model, flavor *flavorModel, storage *storageModel, enc *encryptionModel, net *networkModel) (*postgresflex.CreateInstanceRequestPayload, error) { func toCreatePayload(
model *Model,
flavor *flavorModel,
storage *storageModel,
enc *encryptionModel,
net *networkModel,
) (*postgresflex.CreateInstanceRequestPayload, error) {
if model == nil { if model == nil {
return nil, fmt.Errorf("nil model") return nil, fmt.Errorf("nil model")
} }
@ -170,10 +197,13 @@ func toCreatePayload(model *Model, flavor *flavorModel, storage *storageModel, e
return nil, fmt.Errorf("nil storage") return nil, fmt.Errorf("nil storage")
} }
if model.Replicas.ValueInt64() > math.MaxInt32 { var replVal int32
return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64()) if !model.Replicas.IsNull() && !model.Replicas.IsUnknown() {
if model.Replicas.ValueInt64() > math.MaxInt32 {
return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64())
}
replVal = int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
} }
replVal := int32(model.Replicas.ValueInt64()) // nolint:gosec // check is performed above
storagePayload := &postgresflex.CreateInstanceRequestPayloadGetStorageArgType{ storagePayload := &postgresflex.CreateInstanceRequestPayloadGetStorageArgType{
PerformanceClass: conversion.StringValueToPointer(storage.Class), PerformanceClass: conversion.StringValueToPointer(storage.Class),
@ -197,6 +227,10 @@ func toCreatePayload(model *Model, flavor *flavorModel, storage *storageModel, e
} }
} }
if len(aclElements) < 1 {
return nil, fmt.Errorf("no acl elements found")
}
networkPayload := &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{} networkPayload := &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{}
if net != nil { if net != nil {
networkPayload = &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{ networkPayload = &postgresflex.CreateInstanceRequestPayloadGetNetworkArgType{
@ -205,6 +239,19 @@ func toCreatePayload(model *Model, flavor *flavorModel, storage *storageModel, e
} }
} }
if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
if !flavor.NodeType.IsNull() && !flavor.NodeType.IsUnknown() {
switch strings.ToLower(flavor.NodeType.ValueString()) {
case "single":
replVal = int32(1)
case "replica":
replVal = int32(3)
default:
return nil, fmt.Errorf("flavor has invalid replica attribute")
}
}
}
return &postgresflex.CreateInstanceRequestPayload{ return &postgresflex.CreateInstanceRequestPayload{
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule), BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
FlavorId: conversion.StringValueToPointer(flavor.Id), FlavorId: conversion.StringValueToPointer(flavor.Id),
@ -259,10 +306,22 @@ func loadFlavorId(ctx context.Context, client postgresflexClient, model *Model,
if ram == nil { if ram == nil {
return fmt.Errorf("nil RAM") return fmt.Errorf("nil RAM")
} }
nodeType := conversion.StringValueToPointer(flavor.NodeType) nodeType := conversion.StringValueToPointer(flavor.NodeType)
if nodeType == nil { if nodeType == nil {
return fmt.Errorf("nil NodeType") if model.Replicas.IsNull() || model.Replicas.IsUnknown() {
return fmt.Errorf("nil NodeType")
}
switch model.Replicas.ValueInt64() {
case 1:
nodeType = conversion.StringValueToPointer(types.StringValue("Single"))
case 3:
nodeType = conversion.StringValueToPointer(types.StringValue("Replicas"))
default:
return fmt.Errorf("unknown Replicas value: %d", model.Replicas.ValueInt64())
}
} }
storageClass := conversion.StringValueToPointer(storage.Class) storageClass := conversion.StringValueToPointer(storage.Class)
if storageClass == nil { if storageClass == nil {
return fmt.Errorf("nil StorageClass") return fmt.Errorf("nil StorageClass")

View file

@ -0,0 +1,80 @@
package postgresflexalpha
import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
BackupSchedule types.String `tfsdk:"backup_schedule"`
Flavor types.Object `tfsdk:"flavor"`
Replicas types.Int64 `tfsdk:"replicas"`
Storage types.Object `tfsdk:"storage"`
Version types.String `tfsdk:"version"`
Region types.String `tfsdk:"region"`
Encryption types.Object `tfsdk:"encryption"`
Network types.Object `tfsdk:"network"`
}
type encryptionModel struct {
KeyRingId types.String `tfsdk:"keyring_id"`
KeyId types.String `tfsdk:"key_id"`
KeyVersion types.String `tfsdk:"key_version"`
ServiceAccount types.String `tfsdk:"service_account"`
}
var encryptionTypes = map[string]attr.Type{
"keyring_id": basetypes.StringType{},
"key_id": basetypes.StringType{},
"key_version": basetypes.StringType{},
"service_account": basetypes.StringType{},
}
type networkModel struct {
ACL types.List `tfsdk:"acl"`
AccessScope types.String `tfsdk:"access_scope"`
InstanceAddress types.String `tfsdk:"instance_address"`
RouterAddress types.String `tfsdk:"router_address"`
}
var networkTypes = map[string]attr.Type{
"acl": basetypes.ListType{ElemType: types.StringType},
"access_scope": basetypes.StringType{},
"instance_address": basetypes.StringType{},
"router_address": basetypes.StringType{},
}
// 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"`
NodeType types.String `tfsdk:"node_type"`
}
// Types corresponding to flavorModel
var flavorTypes = map[string]attr.Type{
"id": basetypes.StringType{},
"description": basetypes.StringType{},
"cpu": basetypes.Int64Type{},
"ram": basetypes.Int64Type{},
"node_type": basetypes.StringType{},
}
// Struct corresponding to Model.Storage
type storageModel struct {
Class types.String `tfsdk:"class"`
Size types.Int64 `tfsdk:"size"`
}
// Types corresponding to storageModel
var storageTypes = map[string]attr.Type{
"class": basetypes.StringType{},
"size": basetypes.Int64Type{},
}

View file

@ -8,6 +8,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait" "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait"
postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils" postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils"
@ -35,85 +36,13 @@ import (
// Ensure the implementation satisfies the expected interfaces. // Ensure the implementation satisfies the expected interfaces.
var ( var (
_ resource.Resource = &instanceResource{} _ resource.Resource = &instanceResource{}
_ resource.ResourceWithConfigure = &instanceResource{} _ resource.ResourceWithConfigure = &instanceResource{}
_ resource.ResourceWithImportState = &instanceResource{} _ resource.ResourceWithImportState = &instanceResource{}
_ resource.ResourceWithModifyPlan = &instanceResource{} _ resource.ResourceWithModifyPlan = &instanceResource{}
_ resource.ResourceWithValidateConfig = &instanceResource{}
) )
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
BackupSchedule types.String `tfsdk:"backup_schedule"`
Flavor types.Object `tfsdk:"flavor"`
Replicas types.Int64 `tfsdk:"replicas"`
Storage types.Object `tfsdk:"storage"`
Version types.String `tfsdk:"version"`
Region types.String `tfsdk:"region"`
Encryption types.Object `tfsdk:"encryption"`
Network types.Object `tfsdk:"network"`
}
type encryptionModel struct {
KeyRingId types.String `tfsdk:"keyring_id"`
KeyId types.String `tfsdk:"key_id"`
KeyVersion types.String `tfsdk:"key_version"`
ServiceAccount types.String `tfsdk:"service_account"`
}
var encryptionTypes = map[string]attr.Type{
"keyring_id": basetypes.StringType{},
"key_id": basetypes.StringType{},
"key_version": basetypes.StringType{},
"service_account": basetypes.StringType{},
}
type networkModel struct {
ACL types.List `tfsdk:"acl"`
AccessScope types.String `tfsdk:"access_scope"`
InstanceAddress types.String `tfsdk:"instance_address"`
RouterAddress types.String `tfsdk:"router_address"`
}
var networkTypes = map[string]attr.Type{
"acl": basetypes.ListType{ElemType: types.StringType},
"access_scope": basetypes.StringType{},
"instance_address": basetypes.StringType{},
"router_address": basetypes.StringType{},
}
// 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"`
NodeType types.String `tfsdk:"node_type"`
}
// Types corresponding to flavorModel
var flavorTypes = map[string]attr.Type{
"id": basetypes.StringType{},
"description": basetypes.StringType{},
"cpu": basetypes.Int64Type{},
"ram": basetypes.Int64Type{},
"node_type": basetypes.StringType{},
}
// Struct corresponding to Model.Storage
type storageModel struct {
Class types.String `tfsdk:"class"`
Size types.Int64 `tfsdk:"size"`
}
// Types corresponding to storageModel
var storageTypes = map[string]attr.Type{
"class": basetypes.StringType{},
"size": basetypes.Int64Type{},
}
// NewInstanceResource is a helper function to simplify the provider implementation. // NewInstanceResource is a helper function to simplify the provider implementation.
func NewInstanceResource() resource.Resource { func NewInstanceResource() resource.Resource {
return &instanceResource{} return &instanceResource{}
@ -125,6 +54,24 @@ type instanceResource struct {
providerData core.ProviderData providerData core.ProviderData
} }
func (r *instanceResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var data Model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
if data.Replicas.IsNull() || data.Replicas.IsUnknown() {
resp.Diagnostics.AddAttributeWarning(
path.Root("replicas"),
"Missing Attribute Configuration",
"Expected replicas to be configured. "+
"The resource may return unexpected results.",
)
}
}
// ModifyPlan implements resource.ResourceWithModifyPlan. // ModifyPlan implements resource.ResourceWithModifyPlan.
// Use the modifier to set the effective region in the current plan. // Use the modifier to set the effective region in the current plan.
func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform func (r *instanceResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
@ -179,18 +126,35 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure
// Schema defines the schema for the resource. // Schema defines the schema for the resource.
func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
descriptions := map[string]string{ descriptions := map[string]string{
"backup_schedule": "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.", "main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.",
"main": "Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.", "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".",
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".", "instance_id": "ID of the PostgresFlex instance.",
"instance_id": "ID of the PostgresFlex instance.", "project_id": "STACKIT project ID to which the instance is associated.",
"project_id": "STACKIT project ID to which the instance is associated.", "name": "Instance name.",
"name": "Instance name.", "backup_schedule": "The schedule for on what time and how often the database backup will be created. The schedule is written as a cron schedule.",
"acl": "The Access Control List (ACL) for the PostgresFlex instance.", "flavor": "The block that defines the flavor data.",
"region": "The resource region. If not defined, the provider region is used.", "flavor_id": "The ID of the flavor.",
"encryption": "The encryption block.", "flavor_description": "The flavor detailed flavor name.",
"key_id": "Key ID of the encryption key.", "flavor_cpu": "The CPU count of the flavor.",
"flavor_ram": "The RAM count of the flavor.",
"flavor_node_type": "The node type of the flavor. (Single or Replicas)",
"replicas": "The number of replicas.",
"storage": "The block of the storage configuration.",
"storage_class": "The storage class used.",
"storage_size": "The disk size of the storage.",
"region": "The resource region. If not defined, the provider region is used.",
"version": "The database version used.",
"encryption": "The encryption block.",
"keyring_id": "KeyRing ID of the encryption key.",
"key_id": "Key ID of the encryption key.",
"key_version": "Key version of the encryption key.",
"service_account": "The service account ID of the service account.",
"network": "The network block configuration.",
"access_scope": "The access scope. (Either SNA or PUBLIC)",
"acl": "The Access Control List (ACL) for the PostgresFlex instance.",
"instance_address": "The returned instance address.",
"router_address": "The returned router address.",
} }
// TODO @mhenselin - do the rest
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Description: descriptions["main"], Description: descriptions["main"],
@ -240,44 +204,64 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
Required: true, Required: true,
}, },
"flavor": schema.SingleNestedAttribute{ "flavor": schema.SingleNestedAttribute{
Required: true, Required: true,
Description: descriptions["flavor"],
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{ "id": schema.StringAttribute{
Computed: true, Description: descriptions["flavor_id"],
Optional: true, Computed: true,
Optional: true,
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
UseStateForUnknownIfFlavorUnchanged(req), UseStateForUnknownIfFlavorUnchanged(req),
stringplanmodifier.RequiresReplace(), stringplanmodifier.RequiresReplace(),
}, },
}, },
"description": schema.StringAttribute{ "description": schema.StringAttribute{
Computed: true, Computed: true,
Description: descriptions["flavor_description"],
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
UseStateForUnknownIfFlavorUnchanged(req), UseStateForUnknownIfFlavorUnchanged(req),
}, },
}, },
"cpu": schema.Int64Attribute{ "cpu": schema.Int64Attribute{
Required: true, Description: descriptions["flavor_cpu"],
Required: true,
}, },
"ram": schema.Int64Attribute{ "ram": schema.Int64Attribute{
Required: true, Description: descriptions["flavor_ram"],
Required: true,
},
"node_type": schema.StringAttribute{
Description: descriptions["flavor_node_type"],
Computed: true,
Optional: true,
PlanModifiers: []planmodifier.String{
// TODO @mhenselin anschauen
UseStateForUnknownIfFlavorUnchanged(req),
stringplanmodifier.RequiresReplace(),
},
}, },
}, },
}, },
"replicas": schema.Int64Attribute{ "replicas": schema.Int64Attribute{
Required: true, Required: true,
PlanModifiers: []planmodifier.Int64{
int64planmodifier.RequiresReplace(),
},
}, },
"storage": schema.SingleNestedAttribute{ "storage": schema.SingleNestedAttribute{
Required: true, Required: true,
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"class": schema.StringAttribute{ "class": schema.StringAttribute{
Required: true, Required: true,
Description: descriptions["storage_class"],
PlanModifiers: []planmodifier.String{ PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(), stringplanmodifier.RequiresReplace(),
}, },
}, },
"size": schema.Int64Attribute{ "size": schema.Int64Attribute{
Required: true, Description: descriptions["storage_size"],
Required: true,
// PlanModifiers: []planmodifier.Int64{ // PlanModifiers: []planmodifier.Int64{
// TODO - req replace if new size smaller than state size // TODO - req replace if new size smaller than state size
// int64planmodifier.RequiresReplaceIf(), // int64planmodifier.RequiresReplaceIf(),
@ -286,7 +270,8 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
}, },
}, },
"version": schema.StringAttribute{ "version": schema.StringAttribute{
Required: true, Description: descriptions["version"],
Required: true,
}, },
"region": schema.StringAttribute{ "region": schema.StringAttribute{
Optional: true, Optional: true,
@ -399,7 +384,11 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest,
} }
// Create creates the resource and sets the initial Terraform state. // Create creates the resource and sets the initial Terraform state.
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform func (r *instanceResource) Create(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from plan // Retrieve values from plan
var model Model var model Model
diags := req.Plan.Get(ctx, &model) diags := req.Plan.Get(ctx, &model)

View file

@ -2,10 +2,12 @@ package postgresflexalpha
import ( import (
"context" "context"
"reflect"
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types"
postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha"
"github.com/stackitcloud/stackit-sdk-go/core/utils" "github.com/stackitcloud/stackit-sdk-go/core/utils"
@ -25,7 +27,6 @@ import (
// } // }
func TestMapFields(t *testing.T) { func TestMapFields(t *testing.T) {
t.Skip("Skipping - needs refactoring")
const testRegion = "region" const testRegion = "region"
tests := []struct { tests := []struct {
description string description string
@ -40,16 +41,26 @@ func TestMapFields(t *testing.T) {
isValid bool isValid bool
}{ }{
{ {
"default_values", "default_values does exactly mean what?",
Model{ Model{
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Replicas: types.Int64Value(1),
},
&postgresflex.GetInstanceResponse{
FlavorId: utils.Ptr("flavor_id"),
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(1))),
},
&flavorModel{
NodeType: types.StringValue("Single"),
}, },
&postgresflex.GetInstanceResponse{},
&flavorModel{},
&storageModel{}, &storageModel{},
&encryptionModel{}, &encryptionModel{},
&networkModel{}, &networkModel{
ACL: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
},
testRegion, testRegion,
Model{ Model{
Id: types.StringValue("pid,region,iid"), Id: types.StringValue("pid,region,iid"),
@ -63,202 +74,96 @@ func TestMapFields(t *testing.T) {
"description": types.StringNull(), "description": types.StringNull(),
"cpu": types.Int64Null(), "cpu": types.Int64Null(),
"ram": types.Int64Null(), "ram": types.Int64Null(),
"node_type": types.StringNull(), "node_type": types.StringValue("Single"),
}),
Replicas: types.Int64Value(1),
Encryption: types.ObjectValueMust(encryptionTypes, map[string]attr.Value{
"keyring_id": types.StringNull(),
"key_id": types.StringNull(),
"key_version": types.StringNull(),
"service_account": types.StringNull(),
}), }),
Replicas: types.Int64Null(),
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{ Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
"class": types.StringNull(), "class": types.StringNull(),
"size": types.Int64Null(), "size": types.Int64Null(),
}), }),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"acl": types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
"access_scope": types.StringNull(),
"instance_address": types.StringNull(),
"router_address": types.StringNull(),
}),
Version: types.StringNull(), Version: types.StringNull(),
Region: types.StringValue(testRegion), Region: types.StringValue(testRegion),
}, },
true, true,
}, },
{ // {
"simple_values", // "acl_unordered",
Model{ // Model{
InstanceId: types.StringValue("iid"), // InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), // ProjectId: types.StringValue("pid"),
}, // // ACL: types.ListValueMust(types.StringType, []attr.Value{
&postgresflex.GetInstanceResponse{ // // types.StringValue("ip2"),
// Acl: &[]string{ // // types.StringValue(""),
// "ip1", // // types.StringValue("ip1"),
// "ip2", // // }),
// "", // },
// }, // &postgresflex.GetInstanceResponse{
BackupSchedule: utils.Ptr("schedule"), // // Acl: &[]string{
// Flavor: &postgresflex.Flavor{ // // "",
// Cpu: utils.Ptr(int64(12)), // // "ip1",
// Description: utils.Ptr("description"), // // "ip2",
// Id: utils.Ptr("flavor_id"), // // },
// Ram: utils.Ptr(int64(34)), // BackupSchedule: utils.Ptr("schedule"),
// }, // FlavorId: nil,
Id: utils.Ptr("iid"), // Id: utils.Ptr("iid"),
Name: utils.Ptr("name"), // Name: utils.Ptr("name"),
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))), // Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")), // Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
Storage: &postgresflex.Storage{ // Storage: nil,
PerformanceClass: utils.Ptr("class"), // Version: utils.Ptr("version"),
Size: utils.Ptr(int64(78)), // },
}, // &flavorModel{
Version: utils.Ptr("version"), // CPU: types.Int64Value(12),
}, // RAM: types.Int64Value(34),
&flavorModel{}, // },
&storageModel{}, // &storageModel{
&encryptionModel{}, // Class: types.StringValue("class"),
&networkModel{}, // Size: types.Int64Value(78),
testRegion, // },
Model{ // &encryptionModel{},
Id: types.StringValue("pid,region,iid"), // &networkModel{},
InstanceId: types.StringValue("iid"), // testRegion,
ProjectId: types.StringValue("pid"), // Model{
Name: types.StringValue("name"), // Id: types.StringValue("pid,region,iid"),
// ACL: types.ListValueMust(types.StringType, []attr.Value{ // InstanceId: types.StringValue("iid"),
// types.StringValue("ip1"), // ProjectId: types.StringValue("pid"),
// types.StringValue("ip2"), // Name: types.StringValue("name"),
// types.StringValue(""), // // ACL: types.ListValueMust(types.StringType, []attr.Value{
// }), // // types.StringValue("ip2"),
BackupSchedule: types.StringValue("schedule"), // // types.StringValue(""),
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{ // // types.StringValue("ip1"),
"id": types.StringValue("flavor_id"), // // }),
"description": types.StringValue("description"), // BackupSchedule: types.StringValue("schedule"),
"cpu": types.Int64Value(12), // Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
"ram": types.Int64Value(34), // "id": types.StringNull(),
}), // "description": types.StringNull(),
Replicas: types.Int64Value(56), // "cpu": types.Int64Value(12),
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{ // "ram": types.Int64Value(34),
"class": types.StringValue("class"), // }),
"size": types.Int64Value(78), // Replicas: types.Int64Value(56),
}), // Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
Version: types.StringValue("version"), // "class": types.StringValue("class"),
Region: types.StringValue(testRegion), // "size": types.Int64Value(78),
}, // }),
true, // Version: types.StringValue("version"),
}, // Region: types.StringValue(testRegion),
{ // },
"simple_values_no_flavor_and_storage", // true,
Model{ // },
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
},
&postgresflex.GetInstanceResponse{
// Acl: &[]string{
// "ip1",
// "ip2",
// "",
// },
BackupSchedule: utils.Ptr("schedule"),
FlavorId: nil,
Id: utils.Ptr("iid"),
Name: utils.Ptr("name"),
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
Storage: nil,
Version: utils.Ptr("version"),
},
&flavorModel{
CPU: types.Int64Value(12),
RAM: types.Int64Value(34),
},
&storageModel{
Class: types.StringValue("class"),
Size: types.Int64Value(78),
},
&encryptionModel{},
&networkModel{},
testRegion,
Model{
Id: types.StringValue("pid,region,iid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
// ACL: types.ListValueMust(types.StringType, []attr.Value{
// types.StringValue("ip1"),
// types.StringValue("ip2"),
// types.StringValue(""),
// }),
BackupSchedule: types.StringValue("schedule"),
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
"id": types.StringNull(),
"description": types.StringNull(),
"cpu": types.Int64Value(12),
"ram": types.Int64Value(34),
}),
Replicas: types.Int64Value(56),
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
"class": types.StringValue("class"),
"size": types.Int64Value(78),
}),
Version: types.StringValue("version"),
Region: types.StringValue(testRegion),
},
true,
},
{
"acl_unordered",
Model{
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
// ACL: types.ListValueMust(types.StringType, []attr.Value{
// types.StringValue("ip2"),
// types.StringValue(""),
// types.StringValue("ip1"),
// }),
},
&postgresflex.GetInstanceResponse{
// Acl: &[]string{
// "",
// "ip1",
// "ip2",
// },
BackupSchedule: utils.Ptr("schedule"),
FlavorId: nil,
Id: utils.Ptr("iid"),
Name: utils.Ptr("name"),
Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))),
Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")),
Storage: nil,
Version: utils.Ptr("version"),
},
&flavorModel{
CPU: types.Int64Value(12),
RAM: types.Int64Value(34),
},
&storageModel{
Class: types.StringValue("class"),
Size: types.Int64Value(78),
},
&encryptionModel{},
&networkModel{},
testRegion,
Model{
Id: types.StringValue("pid,region,iid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
// ACL: types.ListValueMust(types.StringType, []attr.Value{
// types.StringValue("ip2"),
// types.StringValue(""),
// types.StringValue("ip1"),
// }),
BackupSchedule: types.StringValue("schedule"),
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
"id": types.StringNull(),
"description": types.StringNull(),
"cpu": types.Int64Value(12),
"ram": types.Int64Value(34),
}),
Replicas: types.Int64Value(56),
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
"class": types.StringValue("class"),
"size": types.Int64Value(78),
}),
Version: types.StringValue("version"),
Region: types.StringValue(testRegion),
},
true,
},
{ {
"nil_response", "nil_response",
Model{ Model{
@ -300,7 +205,7 @@ func TestMapFields(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(tt.state, tt.expected) diff := cmp.Diff(tt.expected, tt.state)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -310,7 +215,6 @@ func TestMapFields(t *testing.T) {
} }
func TestToCreatePayload(t *testing.T) { func TestToCreatePayload(t *testing.T) {
t.Skip("Skipping - needs refactoring")
tests := []struct { tests := []struct {
description string description string
input *Model input *Model
@ -324,89 +228,50 @@ func TestToCreatePayload(t *testing.T) {
}{ }{
{ {
"default_values", "default_values",
&Model{}, &Model{
Replicas: types.Int64Value(1),
},
[]string{}, []string{},
&flavorModel{}, &flavorModel{},
&storageModel{}, &storageModel{},
&encryptionModel{}, &encryptionModel{},
&networkModel{}, &networkModel{
ACL: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
},
&postgresflex.CreateInstanceRequestPayload{
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}),
Encryption: &postgresflex.InstanceEncryption{},
Network: &postgresflex.InstanceNetwork{
Acl: &[]string{"0.0.0.0/0"},
},
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(1))),
},
true,
},
{
"use flavor node_type instead of replicas",
&Model{},
[]string{},
&flavorModel{
NodeType: types.StringValue("Single"),
},
&storageModel{},
&encryptionModel{},
&networkModel{
ACL: types.ListValueMust(types.StringType, []attr.Value{
types.StringValue("0.0.0.0/0"),
}),
},
&postgresflex.CreateInstanceRequestPayload{ &postgresflex.CreateInstanceRequestPayload{
//Acl: &[]string{}, //Acl: &[]string{},
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}), Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}),
}, Encryption: &postgresflex.InstanceEncryption{},
true, Network: &postgresflex.InstanceNetwork{
}, Acl: &[]string{"0.0.0.0/0"},
{ },
"simple_values", Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(1))),
&Model{
BackupSchedule: types.StringValue("schedule"),
Name: types.StringValue("name"),
Replicas: types.Int64Value(12),
Version: types.StringValue("version"),
},
[]string{
"ip_1",
"ip_2",
},
&flavorModel{
Id: types.StringValue("flavor_id"),
},
&storageModel{
Class: types.StringValue("class"),
Size: types.Int64Value(34),
},
&encryptionModel{},
&networkModel{},
&postgresflex.CreateInstanceRequestPayload{
// Acl: &[]string{
// "ip_1",
// "ip_2",
// },
BackupSchedule: utils.Ptr("schedule"),
FlavorId: utils.Ptr("flavor_id"),
Name: utils.Ptr("name"),
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(56))),
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{
PerformanceClass: utils.Ptr("class"),
Size: utils.Ptr(int64(34)),
}),
Version: utils.Ptr("version"),
},
true,
},
{
" ^^1null_fields_and_int_conversions",
&Model{
BackupSchedule: types.StringNull(),
Name: types.StringNull(),
Replicas: types.Int64Value(2123456789),
Version: types.StringNull(),
},
[]string{
"",
},
&flavorModel{
Id: types.StringNull(),
},
&storageModel{
Class: types.StringNull(),
Size: types.Int64Null(),
},
&encryptionModel{},
&networkModel{},
&postgresflex.CreateInstanceRequestPayload{
// Acl: &[]string{
// "",
// },
BackupSchedule: nil,
FlavorId: nil,
Name: nil,
Replicas: postgresflex.CreateInstanceRequestPayloadGetReplicasAttributeType(utils.Ptr(int32(2123456789))),
Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{
PerformanceClass: nil,
Size: nil,
}),
Version: nil,
}, },
true, true,
}, },
@ -465,7 +330,7 @@ func TestToCreatePayload(t *testing.T) {
t.Fatalf("Should not have failed: %v", err) t.Fatalf("Should not have failed: %v", err)
} }
if tt.isValid { if tt.isValid {
diff := cmp.Diff(output, tt.expected) diff := cmp.Diff(tt.expected, output)
if diff != "" { if diff != "" {
t.Fatalf("Data does not match: %s", diff) t.Fatalf("Data does not match: %s", diff)
} }
@ -780,3 +645,22 @@ func TestToCreatePayload(t *testing.T) {
// }) // })
// } // }
// } // }
func TestNewInstanceResource(t *testing.T) {
tests := []struct {
name string
want resource.Resource
}{
{
name: "create empty instance resource",
want: &instanceResource{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewInstanceResource(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewInstanceResource() = %v, want %v", got, tt.want)
}
})
}
}