From ff9f47edc3df7eb09f41f8f9b246b4b67813a6fc Mon Sep 17 00:00:00 2001 From: "Marcel S. Henselin" Date: Mon, 29 Dec 2025 11:10:42 +0100 Subject: [PATCH] fix: fixed some tests --- docs/resources/postgresflexalpha_instance.md | 31 +- sample/main.tf | 10 - sample/postresql.tf | 20 +- sample/sqlserver.tf | 10 + sample/user.tf | 6 +- .../postgresflexalpha/database/datasource.go | 1 - .../postgresflexalpha/database/resource.go | 3 - .../services/postgresflexalpha/flavor_test.go | 514 ++++++++++++++++++ .../postgresflexalpha/instance/functions.go | 73 ++- .../postgresflexalpha/instance/models.go | 80 +++ .../postgresflexalpha/instance/resource.go | 185 +++---- .../instance/resource_test.go | 426 ++++++--------- 12 files changed, 941 insertions(+), 418 deletions(-) create mode 100644 stackit/internal/services/postgresflexalpha/flavor_test.go create mode 100644 stackit/internal/services/postgresflexalpha/instance/models.go diff --git a/docs/resources/postgresflexalpha_instance.md b/docs/resources/postgresflexalpha_instance.md index 944425b5..60d4f2f4 100644 --- a/docs/resources/postgresflexalpha_instance.md +++ b/docs/resources/postgresflexalpha_instance.md @@ -44,13 +44,13 @@ import { - `backup_schedule` (String) - `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. -- `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. - `replicas` (Number) - `storage` (Attributes) (see [below for nested schema](#nestedatt--storage)) -- `version` (String) +- `version` (String) The database version used. ### Optional @@ -67,9 +67,9 @@ import { Required: - `key_id` (String) Key ID of the encryption key. -- `key_version` (String) -- `keyring_id` (String) -- `service_account` (String) +- `key_version` (String) Key version of the encryption key. +- `keyring_id` (String) KeyRing ID of the encryption key. +- `service_account` (String) The service account ID of the service account. @@ -77,16 +77,17 @@ Required: Required: -- `cpu` (Number) -- `ram` (Number) +- `cpu` (Number) The CPU count of the flavor. +- `ram` (Number) The RAM count of the flavor. Optional: -- `id` (String) +- `id` (String) The ID of the flavor. +- `node_type` (String) The node type of the flavor. (Single or Replicas) Read-Only: -- `description` (String) +- `description` (String) The flavor detailed flavor name. @@ -98,9 +99,9 @@ Required: Optional: -- `access_scope` (String) -- `instance_address` (String) -- `router_address` (String) +- `access_scope` (String) The access scope. (Either SNA or PUBLIC) +- `instance_address` (String) The returned instance address. +- `router_address` (String) The returned router address. @@ -108,5 +109,5 @@ Optional: Required: -- `class` (String) -- `size` (Number) +- `class` (String) The storage class used. +- `size` (Number) The disk size of the storage. diff --git a/sample/main.tf b/sample/main.tf index 64cbbe25..8f791797 100644 --- a/sample/main.tf +++ b/sample/main.tf @@ -1,11 +1 @@ # 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 -} diff --git a/sample/postresql.tf b/sample/postresql.tf index 39e50e7d..64f86884 100644 --- a/sample/postresql.tf +++ b/sample/postresql.tf @@ -6,7 +6,7 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "ptlsdbsrv" { cpu = 2 ram = 4 } - replicas = 3 + replicas = 1 storage = { class = "premium-perf2-stackit" size = 5 @@ -26,12 +26,12 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "ptlsdbsrv" { version = 14 } -data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" { - project_id = var.project_id - instance_id = "fdb6573e-2dea-4e1d-a638-9157cf90c3ba" - region = "eu01" -} - -output "sample_psqlinstance" { - value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql -} +# data "stackitprivatepreview_postgresflexalpha_instance" "datapsql" { +# project_id = var.project_id +# instance_id = "fdb6573e-2dea-4e1d-a638-9157cf90c3ba" +# region = "eu01" +# } +# +# output "sample_psqlinstance" { +# value = data.stackitprivatepreview_postgresflexalpha_instance.datapsql +# } diff --git a/sample/sqlserver.tf b/sample/sqlserver.tf index 3c33a221..a333c0cc 100644 --- a/sample/sqlserver.tf +++ b/sample/sqlserver.tf @@ -88,3 +88,13 @@ resource "stackitprivatepreview_sqlserverflexalpha_instance" "ptlsdbsqlsrv" { # instance_id = var.instance_id # 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 +# } diff --git a/sample/user.tf b/sample/user.tf index 2ebbb040..1671cc6d 100644 --- a/sample/user.tf +++ b/sample/user.tf @@ -1,12 +1,12 @@ data "stackitprivatepreview_postgresflexalpha_user" "example" { 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 } resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" { 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 # roles = ["createdb", "login", "createrole"] roles = ["createdb", "login"] @@ -14,7 +14,7 @@ resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" { resource "stackitprivatepreview_sqlserverflexalpha_user" "ptlsdbuser" { 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 roles = ["login"] } diff --git a/stackit/internal/services/postgresflexalpha/database/datasource.go b/stackit/internal/services/postgresflexalpha/database/datasource.go index 3eecc35f..e1180c0f 100644 --- a/stackit/internal/services/postgresflexalpha/database/datasource.go +++ b/stackit/internal/services/postgresflexalpha/database/datasource.go @@ -127,7 +127,6 @@ func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaReques // Read refreshes the Terraform state with the latest data. func (r *databaseDataSource) Read( ctx context.Context, - // TODO - make it pointer req datasource.ReadRequest, resp *datasource.ReadResponse, ) { // nolint:gocritic // function signature required by Terraform diff --git a/stackit/internal/services/postgresflexalpha/database/resource.go b/stackit/internal/services/postgresflexalpha/database/resource.go index 983bb7c2..c75b7702 100644 --- a/stackit/internal/services/postgresflexalpha/database/resource.go +++ b/stackit/internal/services/postgresflexalpha/database/resource.go @@ -58,7 +58,6 @@ type databaseResource struct { // Use the modifier to set the effective region in the current plan. func (r *databaseResource) ModifyPlan( ctx context.Context, - // TODO - make it pointer req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse, ) { // 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. func (r *databaseResource) Create( ctx context.Context, - // TODO - make it pointer req resource.CreateRequest, resp *resource.CreateResponse, ) { // nolint:gocritic // function signature required by Terraform @@ -292,7 +290,6 @@ func (r *databaseResource) Create( // Read refreshes the Terraform state with the latest data. func (r *databaseResource) Read( ctx context.Context, - // TODO - make it pointer req resource.ReadRequest, resp *resource.ReadResponse, ) { // nolint:gocritic // function signature required by Terraform diff --git a/stackit/internal/services/postgresflexalpha/flavor_test.go b/stackit/internal/services/postgresflexalpha/flavor_test.go new file mode 100644 index 00000000..ecdedb8b --- /dev/null +++ b/stackit/internal/services/postgresflexalpha/flavor_test.go @@ -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) + } + }) + } +} diff --git a/stackit/internal/services/postgresflexalpha/instance/functions.go b/stackit/internal/services/postgresflexalpha/instance/functions.go index abb2288e..7fd976e9 100644 --- a/stackit/internal/services/postgresflexalpha/instance/functions.go +++ b/stackit/internal/services/postgresflexalpha/instance/functions.go @@ -101,7 +101,7 @@ func mapFields( } var flavorValues map[string]attr.Value - if instance.FlavorId == nil { + if instance.FlavorId == nil || *instance.FlavorId == "" { return fmt.Errorf("instance has no flavor id") } if !flavor.Id.IsUnknown() && !flavor.Id.IsNull() { @@ -110,12 +110,28 @@ func mapFields( } } 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{ "id": flavor.Id, "description": flavor.Description, "cpu": flavor.CPU, "ram": flavor.RAM, - "node_type": flavor.NodeType, + "node_type": types.StringValue(nodeType), } } else { flavorValues = model.Flavor.Attributes() @@ -143,6 +159,11 @@ func mapFields( 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.InstanceId = types.StringValue(instanceId) model.Name = types.StringPointerValue(instance.Name) @@ -159,7 +180,13 @@ func mapFields( 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 { 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") } - if model.Replicas.ValueInt64() > math.MaxInt32 { - return nil, fmt.Errorf("replica count too big: %d", model.Replicas.ValueInt64()) + var replVal int32 + 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{ 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{} if net != nil { 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{ BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule), FlavorId: conversion.StringValueToPointer(flavor.Id), @@ -259,10 +306,22 @@ func loadFlavorId(ctx context.Context, client postgresflexClient, model *Model, if ram == nil { return fmt.Errorf("nil RAM") } + nodeType := conversion.StringValueToPointer(flavor.NodeType) 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) if storageClass == nil { return fmt.Errorf("nil StorageClass") diff --git a/stackit/internal/services/postgresflexalpha/instance/models.go b/stackit/internal/services/postgresflexalpha/instance/models.go new file mode 100644 index 00000000..1e550135 --- /dev/null +++ b/stackit/internal/services/postgresflexalpha/instance/models.go @@ -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{}, +} diff --git a/stackit/internal/services/postgresflexalpha/instance/resource.go b/stackit/internal/services/postgresflexalpha/instance/resource.go index 7d5f4ccb..1340bd07 100644 --- a/stackit/internal/services/postgresflexalpha/instance/resource.go +++ b/stackit/internal/services/postgresflexalpha/instance/resource.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha/wait" postgresflexUtils "github.com/mhenselin/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/utils" @@ -35,85 +36,13 @@ import ( // Ensure the implementation satisfies the expected interfaces. var ( - _ resource.Resource = &instanceResource{} - _ resource.ResourceWithConfigure = &instanceResource{} - _ resource.ResourceWithImportState = &instanceResource{} - _ resource.ResourceWithModifyPlan = &instanceResource{} + _ resource.Resource = &instanceResource{} + _ resource.ResourceWithConfigure = &instanceResource{} + _ resource.ResourceWithImportState = &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. func NewInstanceResource() resource.Resource { return &instanceResource{} @@ -125,6 +54,24 @@ type instanceResource struct { 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. // 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 @@ -179,18 +126,35 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure // Schema defines the schema for the resource. func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { 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.", - "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".", - "instance_id": "ID of the PostgresFlex instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "name": "Instance name.", - "acl": "The Access Control List (ACL) for the PostgresFlex instance.", - "region": "The resource region. If not defined, the provider region is used.", - "encryption": "The encryption block.", - "key_id": "Key ID of the encryption key.", + "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`\".", + "instance_id": "ID of the PostgresFlex instance.", + "project_id": "STACKIT project ID to which the instance is associated.", + "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.", + "flavor": "The block that defines the flavor data.", + "flavor_id": "The ID of the flavor.", + "flavor_description": "The flavor detailed flavor name.", + "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{ Description: descriptions["main"], @@ -240,44 +204,64 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, Required: true, }, "flavor": schema.SingleNestedAttribute{ - Required: true, + Required: true, + Description: descriptions["flavor"], Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ - Computed: true, - Optional: true, + Description: descriptions["flavor_id"], + Computed: true, + Optional: true, PlanModifiers: []planmodifier.String{ UseStateForUnknownIfFlavorUnchanged(req), stringplanmodifier.RequiresReplace(), }, }, "description": schema.StringAttribute{ - Computed: true, + Computed: true, + Description: descriptions["flavor_description"], PlanModifiers: []planmodifier.String{ UseStateForUnknownIfFlavorUnchanged(req), }, }, "cpu": schema.Int64Attribute{ - Required: true, + Description: descriptions["flavor_cpu"], + Required: true, }, "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{ Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, }, "storage": schema.SingleNestedAttribute{ Required: true, Attributes: map[string]schema.Attribute{ "class": schema.StringAttribute{ - Required: true, + Required: true, + Description: descriptions["storage_class"], PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "size": schema.Int64Attribute{ - Required: true, + Description: descriptions["storage_size"], + Required: true, // PlanModifiers: []planmodifier.Int64{ // TODO - req replace if new size smaller than state size // int64planmodifier.RequiresReplaceIf(), @@ -286,7 +270,8 @@ func (r *instanceResource) Schema(_ context.Context, req resource.SchemaRequest, }, }, "version": schema.StringAttribute{ - Required: true, + Description: descriptions["version"], + Required: true, }, "region": schema.StringAttribute{ 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. -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 var model Model diags := req.Plan.Get(ctx, &model) diff --git a/stackit/internal/services/postgresflexalpha/instance/resource_test.go b/stackit/internal/services/postgresflexalpha/instance/resource_test.go index 17747a2f..47d334d2 100644 --- a/stackit/internal/services/postgresflexalpha/instance/resource_test.go +++ b/stackit/internal/services/postgresflexalpha/instance/resource_test.go @@ -2,10 +2,12 @@ package postgresflexalpha import ( "context" + "reflect" "testing" "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" postgresflex "github.com/mhenselin/terraform-provider-stackitprivatepreview/pkg/postgresflexalpha" "github.com/stackitcloud/stackit-sdk-go/core/utils" @@ -25,7 +27,6 @@ import ( // } func TestMapFields(t *testing.T) { - t.Skip("Skipping - needs refactoring") const testRegion = "region" tests := []struct { description string @@ -40,16 +41,26 @@ func TestMapFields(t *testing.T) { isValid bool }{ { - "default_values", + "default_values does exactly mean what?", Model{ InstanceId: types.StringValue("iid"), 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{}, &encryptionModel{}, - &networkModel{}, + &networkModel{ + ACL: types.ListValueMust(types.StringType, []attr.Value{ + types.StringValue("0.0.0.0/0"), + }), + }, testRegion, Model{ Id: types.StringValue("pid,region,iid"), @@ -63,202 +74,96 @@ func TestMapFields(t *testing.T) { "description": types.StringNull(), "cpu": 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{ "class": types.StringNull(), "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(), Region: types.StringValue(testRegion), }, true, }, - { - "simple_values", - Model{ - InstanceId: types.StringValue("iid"), - ProjectId: types.StringValue("pid"), - }, - &postgresflex.GetInstanceResponse{ - // Acl: &[]string{ - // "ip1", - // "ip2", - // "", - // }, - BackupSchedule: utils.Ptr("schedule"), - // Flavor: &postgresflex.Flavor{ - // Cpu: utils.Ptr(int64(12)), - // Description: utils.Ptr("description"), - // Id: utils.Ptr("flavor_id"), - // Ram: utils.Ptr(int64(34)), - // }, - Id: utils.Ptr("iid"), - Name: utils.Ptr("name"), - Replicas: postgresflex.GetInstanceResponseGetReplicasAttributeType(utils.Ptr(int32(56))), - Status: postgresflex.GetInstanceResponseGetStatusAttributeType(utils.Ptr("status")), - Storage: &postgresflex.Storage{ - PerformanceClass: utils.Ptr("class"), - Size: utils.Ptr(int64(78)), - }, - Version: utils.Ptr("version"), - }, - &flavorModel{}, - &storageModel{}, - &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.StringValue("flavor_id"), - "description": types.StringValue("description"), - "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, - }, - { - "simple_values_no_flavor_and_storage", - 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, - }, + // { + // "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", Model{ @@ -300,7 +205,7 @@ func TestMapFields(t *testing.T) { t.Fatalf("Should not have failed: %v", err) } if tt.isValid { - diff := cmp.Diff(tt.state, tt.expected) + diff := cmp.Diff(tt.expected, tt.state) if diff != "" { t.Fatalf("Data does not match: %s", diff) } @@ -310,7 +215,6 @@ func TestMapFields(t *testing.T) { } func TestToCreatePayload(t *testing.T) { - t.Skip("Skipping - needs refactoring") tests := []struct { description string input *Model @@ -324,89 +228,50 @@ func TestToCreatePayload(t *testing.T) { }{ { "default_values", - &Model{}, + &Model{ + Replicas: types.Int64Value(1), + }, []string{}, &flavorModel{}, &storageModel{}, &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{ //Acl: &[]string{}, - Storage: postgresflex.CreateInstanceRequestPayloadGetStorageAttributeType(&postgresflex.Storage{}), - }, - true, - }, - { - "simple_values", - &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, + 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, }, @@ -465,7 +330,7 @@ func TestToCreatePayload(t *testing.T) { t.Fatalf("Should not have failed: %v", err) } if tt.isValid { - diff := cmp.Diff(output, tt.expected) + diff := cmp.Diff(tt.expected, output) if 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) + } + }) + } +}