Some checks failed
CI Workflow / Check GoReleaser config (pull_request) Successful in 6s
TF Acceptance Tests Workflow / Acceptance Tests (pull_request) Failing after 4m51s
CI Workflow / Prepare GO cache (pull_request) Successful in 6m12s
CI Workflow / Code coverage report (pull_request) Has been cancelled
CI Workflow / Test readiness for publishing provider (pull_request) Has been cancelled
CI Workflow / CI run tests (pull_request) Has been cancelled
CI Workflow / CI run build and linting (pull_request) Has been cancelled
990 lines
31 KiB
Go
990 lines
31 KiB
Go
package postgresflexalpha_test
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform-plugin-testing/compare"
|
|
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
|
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
|
|
"github.com/hashicorp/terraform-plugin-testing/plancheck"
|
|
"github.com/hashicorp/terraform-plugin-testing/statecheck"
|
|
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
|
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
|
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
|
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex/v3alpha1api"
|
|
|
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
|
postgresflexalphaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance"
|
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/postgresflexalpha"
|
|
|
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
|
|
// The fwresource import alias is so there is no collision
|
|
// with the more typical acceptance testing import:
|
|
// "github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
|
fwresource "github.com/hashicorp/terraform-plugin-framework/resource"
|
|
)
|
|
|
|
const (
|
|
pfx = "stackitprivatepreview_postgresflexalpha"
|
|
dataPfx = "data.stackitprivatepreview_postgresflexalpha"
|
|
|
|
singleFlavorID = "2.4"
|
|
replicasFlavorID = "2.4-replica"
|
|
)
|
|
|
|
func TestInstanceResourceSchema(t *testing.T) {
|
|
// t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
schemaRequest := fwresource.SchemaRequest{}
|
|
schemaResponse := &fwresource.SchemaResponse{}
|
|
|
|
// Instantiate the resource.Resource and call its Schema method
|
|
postgresflexalphaInstance.NewInstanceResource().Schema(ctx, schemaRequest, schemaResponse)
|
|
|
|
if schemaResponse.Diagnostics.HasError() {
|
|
t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics)
|
|
}
|
|
|
|
// Validate the schema
|
|
diagnostics := schemaResponse.Schema.ValidateImplementation(ctx)
|
|
|
|
if diagnostics.HasError() {
|
|
t.Fatalf("Schema validation diagnostics: %+v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
testutils.Setup()
|
|
code := m.Run()
|
|
// shutdown()
|
|
os.Exit(code)
|
|
}
|
|
|
|
func testAccPreCheck(t *testing.T) {
|
|
if _, ok := os.LookupEnv("TF_ACC_PROJECT_ID"); !ok {
|
|
t.Fatalf("could not find env var TF_ACC_PROJECT_ID")
|
|
}
|
|
}
|
|
|
|
type resData struct {
|
|
ServiceAccountFilePath string
|
|
ProjectID string
|
|
Region string
|
|
Name string
|
|
TfName string
|
|
FlavorID string
|
|
BackupSchedule string
|
|
UseEncryption bool
|
|
KekKeyID string
|
|
KekKeyRingID string
|
|
KekKeyVersion uint8
|
|
KekServiceAccount string
|
|
PerformanceClass string
|
|
Replicas uint32
|
|
Size uint32
|
|
ACLStrings []string
|
|
AccessScope string
|
|
RetentionDays uint32
|
|
Version string
|
|
Users []User
|
|
Databases []Database
|
|
DataSourceTest bool
|
|
}
|
|
|
|
type User struct {
|
|
Name string
|
|
ProjectID string
|
|
Roles []string
|
|
}
|
|
|
|
type Database struct {
|
|
Name string
|
|
ProjectID string
|
|
Owner string
|
|
}
|
|
|
|
func getExample() resData {
|
|
name := acctest.RandomWithPrefix("tf-acc")
|
|
return resData{
|
|
Region: os.Getenv("TF_ACC_REGION"),
|
|
ServiceAccountFilePath: os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE"),
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Name: name,
|
|
TfName: name,
|
|
FlavorID: singleFlavorID,
|
|
BackupSchedule: "0 0 * * *",
|
|
UseEncryption: false,
|
|
RetentionDays: 33,
|
|
Replicas: 1,
|
|
PerformanceClass: "premium-perf2-stackit",
|
|
Size: 10,
|
|
ACLStrings: []string{"0.0.0.0/0"},
|
|
AccessScope: "PUBLIC",
|
|
Version: "17",
|
|
}
|
|
}
|
|
|
|
func TestAccInstance(t *testing.T) {
|
|
exData := getExample()
|
|
exData.Version = "16"
|
|
|
|
updNameData := exData
|
|
updNameData.Name = "name-updated"
|
|
|
|
updSizeData := exData
|
|
updSizeData.Size = 25
|
|
|
|
updBackupSched := updSizeData
|
|
// api should complain about more than one daily backup
|
|
updBackupSched.BackupSchedule = "30 3 * * *"
|
|
|
|
updNetACL := updBackupSched
|
|
updNetACL.ACLStrings = append(updNetACL.ACLStrings, "192.168.0.0/24")
|
|
|
|
updVersion := updNetACL
|
|
updVersion.Version = "17"
|
|
|
|
testItemID := testutils.ResStr(pfx, "instance", exData.TfName)
|
|
compareValuesSame := statecheck.CompareValue(compare.ValuesSame())
|
|
resource.ParallelTest(
|
|
t, resource.TestCase{
|
|
PreCheck: func() {
|
|
testAccPreCheck(t)
|
|
t.Logf(" ... %s - %s", t.Name(), exData.TfName)
|
|
},
|
|
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
|
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
// Create and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf("testing: %s - %s", t.Name(), "create and verify")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
exData,
|
|
),
|
|
ConfigStateChecks: []statecheck.StateCheck{
|
|
compareValuesSame.AddStateValue(
|
|
testItemID,
|
|
tfjsonpath.New("id"),
|
|
),
|
|
statecheck.ExpectKnownValue(
|
|
testItemID,
|
|
tfjsonpath.New("is_deletable"),
|
|
knownvalue.Bool(true),
|
|
),
|
|
statecheck.ExpectKnownValue(
|
|
testItemID,
|
|
tfjsonpath.New("connection_info"),
|
|
knownvalue.MapExact(map[string]knownvalue.Check{
|
|
"write": knownvalue.MapExact(map[string]knownvalue.Check{
|
|
"host": knownvalue.StringRegexp(regexp.MustCompile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}.postgresql.[a-z0-9]+.onstackit.cloud")),
|
|
"port": knownvalue.Int32Func(func(v int32) error {
|
|
if v < 0 {
|
|
return fmt.Errorf("value is negative")
|
|
}
|
|
if v <= 1024 {
|
|
return fmt.Errorf("value uses protected port range")
|
|
}
|
|
return nil
|
|
}),
|
|
}),
|
|
}),
|
|
),
|
|
},
|
|
Check: defaultNoEncInstanceTestChecks(testItemID, exData),
|
|
},
|
|
// Second apply should not have changes
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "second apply")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
exData,
|
|
),
|
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
|
PreApply: []plancheck.PlanCheck{
|
|
plancheck.ExpectEmptyPlan(),
|
|
},
|
|
},
|
|
ConfigStateChecks: []statecheck.StateCheck{
|
|
compareValuesSame.AddStateValue(
|
|
testItemID,
|
|
tfjsonpath.New("id"),
|
|
),
|
|
statecheck.ExpectKnownValue(
|
|
testItemID,
|
|
tfjsonpath.New("is_deletable"),
|
|
knownvalue.Bool(true),
|
|
),
|
|
},
|
|
},
|
|
// Refresh state test
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "refresh state")
|
|
},
|
|
RefreshState: true,
|
|
},
|
|
// Update name and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "update name")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
updNameData,
|
|
),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
testItemID,
|
|
"name",
|
|
updNameData.Name,
|
|
),
|
|
),
|
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
|
PreApply: []plancheck.PlanCheck{
|
|
plancheck.ExpectNonEmptyPlan(),
|
|
},
|
|
},
|
|
},
|
|
// Update size and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "update storage.size")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
updSizeData,
|
|
),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
testItemID,
|
|
"storage.size",
|
|
strconv.Itoa(int(updSizeData.Size)),
|
|
),
|
|
// network should contain 4 sub entries
|
|
resource.TestCheckResourceAttr(testItemID, "network.acl.#", "1"),
|
|
),
|
|
},
|
|
// Update backup schedule
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "update backup schedule")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
updBackupSched,
|
|
),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
testItemID,
|
|
"backup_schedule",
|
|
updBackupSched.BackupSchedule,
|
|
),
|
|
// network should contain 4 sub entries
|
|
resource.TestCheckResourceAttr(testItemID, "network.acl.#", "1"),
|
|
),
|
|
},
|
|
// Update network ACL
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "update network.acl")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
updNetACL,
|
|
),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
testItemID,
|
|
"backup_schedule",
|
|
updBackupSched.BackupSchedule,
|
|
),
|
|
// network should contain 4 sub entries
|
|
resource.TestCheckResourceAttr(testItemID, "network.acl.#", "2"),
|
|
),
|
|
},
|
|
// Update version
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "update version")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
updVersion,
|
|
),
|
|
Check: resource.ComposeTestCheckFunc(
|
|
resource.TestCheckResourceAttr(
|
|
testItemID,
|
|
"version",
|
|
updVersion.Version,
|
|
),
|
|
),
|
|
},
|
|
// Import test
|
|
// test instance imports
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "import instance")
|
|
},
|
|
ResourceName: testItemID,
|
|
// ImportStateIdPrefix: "",
|
|
// ImportStateVerifyIdentifierAttribute: "id",
|
|
ImportStateIdFunc: getInstanceTestID(exData.TfName),
|
|
ImportStateKind: resource.ImportCommandWithID,
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
},
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestAccInstanceHA(t *testing.T) {
|
|
data := getExample()
|
|
data.FlavorID = replicasFlavorID
|
|
data.Replicas = 3
|
|
|
|
testItemID := testutils.ResStr(pfx, "instance", data.TfName)
|
|
|
|
resource.ParallelTest(
|
|
t, resource.TestCase{
|
|
PreCheck: func() {
|
|
testAccPreCheck(t)
|
|
t.Logf(" ... %s - %s", t.Name(), data.TfName)
|
|
},
|
|
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
|
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
// Create and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "create and verify")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
data,
|
|
),
|
|
Check: defaultNoEncInstanceTestChecks(testItemID, data),
|
|
},
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestAccInstanceWithUsers(t *testing.T) {
|
|
data := getExample()
|
|
|
|
userName := "testUser"
|
|
data.Users = []User{
|
|
{
|
|
Name: userName,
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Roles: []string{"login"},
|
|
},
|
|
}
|
|
|
|
testItemID := testutils.ResStr(pfx, "instance", data.TfName)
|
|
// TODO : implement check multiple users
|
|
testUserItemID := testutils.ResStr(pfx, "user", userName)
|
|
|
|
resource.ParallelTest(
|
|
t, resource.TestCase{
|
|
PreCheck: func() {
|
|
testAccPreCheck(t)
|
|
t.Logf(" ... %s - %s", t.Name(), data.TfName)
|
|
},
|
|
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
|
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
// Create and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "create and verify")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
data,
|
|
),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
defaultNoEncInstanceTestChecks(testItemID, data),
|
|
|
|
resource.TestCheckResourceAttr(testUserItemID, "name", userName),
|
|
resource.TestCheckResourceAttrSet(testUserItemID, "id"),
|
|
resource.TestCheckResourceAttr(testUserItemID, "roles.#", "1"),
|
|
),
|
|
},
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestAccInstanceWithDatabases(t *testing.T) {
|
|
data := getExample()
|
|
|
|
dbName := "testdb"
|
|
userName := "testUser"
|
|
data.Users = []User{
|
|
{
|
|
Name: userName,
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Roles: []string{"login"},
|
|
},
|
|
}
|
|
|
|
data.Databases = []Database{
|
|
{
|
|
Name: dbName,
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Owner: userName,
|
|
},
|
|
}
|
|
data.DataSourceTest = true
|
|
|
|
testItemID := testutils.ResStr(pfx, "instance", data.TfName)
|
|
resource.ParallelTest(
|
|
t, resource.TestCase{
|
|
PreCheck: func() {
|
|
testAccPreCheck(t)
|
|
t.Logf(" ... %s - %s", t.Name(), data.TfName)
|
|
},
|
|
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
|
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
// Create and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "create and verify")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
data,
|
|
),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
defaultNoEncInstanceTestChecks(testItemID, data),
|
|
|
|
// TODO - extract also to functions
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "user", userName), "name", userName),
|
|
resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "user", userName), "id"),
|
|
resource.TestCheckResourceAttrPair(
|
|
testItemID, "project_id",
|
|
testutils.ResStr(pfx, "user", userName), "project_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testItemID, "instance_id",
|
|
testutils.ResStr(pfx, "user", userName), "instance_id",
|
|
),
|
|
|
|
// TODO - extract also to functions
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "database", dbName), "name", dbName),
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "database", dbName), "owner", userName),
|
|
resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "database", dbName), "id"),
|
|
resource.TestCheckResourceAttrPair(
|
|
testItemID, "project_id",
|
|
testutils.ResStr(pfx, "database", dbName), "project_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testItemID, "instance_id",
|
|
testutils.ResStr(pfx, "database", dbName), "instance_id",
|
|
),
|
|
),
|
|
},
|
|
// data source
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "datasource")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
data,
|
|
),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
// Instance data
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "instance", data.TfName),
|
|
"project_id",
|
|
data.ProjectID,
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "instance", data.TfName),
|
|
"name",
|
|
data.Name,
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "instance", data.TfName), "project_id",
|
|
testutils.ResStr(pfx, "instance", data.TfName), "project_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "database", dbName), "instance_id",
|
|
testutils.ResStr(pfx, "instance", data.TfName), "instance_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "user", userName), "instance_id",
|
|
testutils.ResStr(pfx, "instance", data.TfName), "instance_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "user", userName), "instance_id",
|
|
testutils.ResStr(pfx, "user", userName), "instance_id",
|
|
),
|
|
|
|
// User data
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"project_id",
|
|
data.ProjectID,
|
|
),
|
|
resource.TestCheckResourceAttrSet(
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"user_id",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"name",
|
|
data.Users[0].Name,
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"roles.#",
|
|
"1",
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"roles.0",
|
|
data.Users[0].Roles[0],
|
|
),
|
|
|
|
// Database data
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "database", dbName),
|
|
"project_id",
|
|
data.ProjectID,
|
|
),
|
|
resource.TestCheckResourceAttr(
|
|
testutils.ResStr(dataPfx, "database", dbName),
|
|
"name",
|
|
dbName,
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "database", dbName),
|
|
"instance_id",
|
|
testutils.ResStr(pfx, "database", dbName),
|
|
"instance_id",
|
|
),
|
|
resource.TestCheckResourceAttrPair(
|
|
testutils.ResStr(dataPfx, "database", dbName),
|
|
"owner",
|
|
testutils.ResStr(dataPfx, "user", userName),
|
|
"name",
|
|
),
|
|
),
|
|
},
|
|
// test instance imports
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "import instance")
|
|
},
|
|
ResourceName: testItemID,
|
|
// ImportStateIdPrefix: "",
|
|
ImportStateVerifyIdentifierAttribute: "id",
|
|
ImportStateIdFunc: getInstanceTestID(data.TfName),
|
|
ImportStateKind: resource.ImportCommandWithID,
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
},
|
|
// test database imports
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "import database")
|
|
},
|
|
ResourceName: testutils.ResStr(pfx, "database", dbName),
|
|
// ImportStateIdPrefix: "",
|
|
// ImportStateVerifyIdentifierAttribute: "id",
|
|
ImportStateIdFunc: getDatabaseTestID(dbName),
|
|
ImportStateKind: resource.ImportCommandWithID,
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
},
|
|
// test user imports
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "import user")
|
|
},
|
|
ResourceName: testutils.ResStr(pfx, "user", userName),
|
|
// ImportStateIdPrefix: "",
|
|
// ImportStateVerifyIdentifierAttribute: "id",
|
|
ImportStateIdFunc: getUserTestID(userName),
|
|
ImportStateKind: resource.ImportCommandWithID,
|
|
ImportState: true,
|
|
ImportStateVerify: true,
|
|
ImportStateVerifyIgnore: []string{"password"},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
func TestAccEncryptedInstanceWithDatabases(t *testing.T) {
|
|
encKekKeyID, ok := os.LookupEnv("TF_ACC_KEK_KEY_ID")
|
|
if !ok || encKekKeyID == "" {
|
|
t.Skip("env var TF_ACC_KEK_KEY_ID needed for encryption test")
|
|
}
|
|
|
|
encKekKeyRingID, ok := os.LookupEnv("TF_ACC_KEK_KEY_RING_ID")
|
|
if !ok || encKekKeyRingID == "" {
|
|
t.Skip("env var TF_ACC_KEK_KEY_RING_ID needed for encryption test")
|
|
}
|
|
|
|
encKekKeyVersion, ok := os.LookupEnv("TF_ACC_KEK_KEY_VERSION")
|
|
if !ok || encKekKeyVersion == "" {
|
|
t.Skip("env var TF_ACC_KEK_KEY_VERSION needed for encryption test")
|
|
}
|
|
|
|
encSvcAcc, ok := os.LookupEnv("TF_ACC_KEK_SERVICE_ACCOUNT")
|
|
if !ok || encSvcAcc == "" {
|
|
t.Skip("env var TF_ACC_KEK_SERVICE_ACCOUNT needed for encryption test")
|
|
}
|
|
|
|
data := getExample()
|
|
data.UseEncryption = true
|
|
data.KekKeyID = encKekKeyID
|
|
data.KekKeyRingID = encKekKeyRingID
|
|
data.KekServiceAccount = encSvcAcc
|
|
encKekKeyVersionInt, err := strconv.Atoi(encKekKeyVersion)
|
|
if err != nil {
|
|
t.Errorf("error converting string to int")
|
|
}
|
|
if encKekKeyVersionInt > math.MaxUint8 {
|
|
t.Errorf("value too large to convert to uint8")
|
|
}
|
|
data.KekKeyVersion = uint8(encKekKeyVersionInt) //nolint:gosec // handled above
|
|
|
|
dbName := "testdb"
|
|
userName := "testUser"
|
|
data.Users = []User{
|
|
{
|
|
Name: userName,
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Roles: []string{"login"},
|
|
},
|
|
}
|
|
|
|
data.Databases = []Database{
|
|
{
|
|
Name: dbName,
|
|
ProjectID: os.Getenv("TF_ACC_PROJECT_ID"),
|
|
Owner: userName,
|
|
},
|
|
}
|
|
|
|
testItemID := testutils.ResStr(pfx, "instance", data.TfName)
|
|
resource.ParallelTest(
|
|
t, resource.TestCase{
|
|
PreCheck: func() {
|
|
testAccPreCheck(t)
|
|
t.Logf(" ... %s - %s", t.Name(), data.TfName)
|
|
},
|
|
CheckDestroy: testAccCheckPostgresFlexDestroy,
|
|
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
|
|
Steps: []resource.TestStep{
|
|
// Create and verify
|
|
{
|
|
PreConfig: func() {
|
|
t.Logf(" ... %s - %s", t.Name(), "create and verify")
|
|
},
|
|
Config: testutils.StringFromTemplateMust(
|
|
"testdata/instance_template.gompl",
|
|
data,
|
|
),
|
|
Check: resource.ComposeAggregateTestCheckFunc(
|
|
defaultEncInstanceTestChecks(testItemID, data),
|
|
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "user", userName), "name", userName),
|
|
resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "user", userName), "id"),
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "database", dbName), "name", dbName),
|
|
resource.TestCheckResourceAttr(testutils.ResStr(pfx, "database", dbName), "owner", userName),
|
|
resource.TestCheckResourceAttrSet(testutils.ResStr(pfx, "database", dbName), "id"),
|
|
),
|
|
},
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
func testAccCheckPostgresFlexDestroy(s *terraform.State) error {
|
|
testutils.Setup()
|
|
|
|
pID, ok := os.LookupEnv("TF_ACC_PROJECT_ID")
|
|
if !ok {
|
|
log.Fatalln("unable to read TF_ACC_PROJECT_ID")
|
|
}
|
|
|
|
ctx := context.Background()
|
|
var client *v3alpha1api.APIClient
|
|
var err error
|
|
|
|
var region, projectID string
|
|
region = testutils.Region
|
|
if region == "" {
|
|
region = "eu01"
|
|
}
|
|
|
|
projectID = pID
|
|
if projectID == "" {
|
|
return fmt.Errorf("projectID could not be determined in destroy function")
|
|
}
|
|
|
|
apiClientConfigOptions := []config.ConfigurationOption{
|
|
config.WithServiceAccountKeyPath(os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE")),
|
|
config.WithRegion(region),
|
|
}
|
|
if testutils.PostgresFlexCustomEndpoint != "" {
|
|
apiClientConfigOptions = append(
|
|
apiClientConfigOptions,
|
|
config.WithEndpoint(testutils.PostgresFlexCustomEndpoint),
|
|
)
|
|
}
|
|
client, err = v3alpha1api.NewAPIClient(apiClientConfigOptions...)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
instancesToDestroy := []string{}
|
|
for _, rs := range s.RootModule().Resources {
|
|
if rs.Type != "stackitprivatepreview_postgresflexalpha_instance" &&
|
|
rs.Type != "stackitprivatepreview_postgresflexbeta_instance" {
|
|
continue
|
|
}
|
|
|
|
// instance terraform ID: = "[project_id],[region],[instance_id]"
|
|
instanceID := strings.Split(rs.Primary.ID, core.Separator)[2]
|
|
instancesToDestroy = append(instancesToDestroy, instanceID)
|
|
}
|
|
|
|
instancesResp, err := client.DefaultAPI.ListInstancesRequest(ctx, projectID, region).
|
|
Size(100).
|
|
Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("getting instancesResp: %w", err)
|
|
}
|
|
|
|
items := instancesResp.GetInstances()
|
|
for i := range items {
|
|
if items[i].Id == "" {
|
|
continue
|
|
}
|
|
if utils.Contains(instancesToDestroy, items[i].Id) {
|
|
err := client.DefaultAPI.DeleteInstanceRequest(ctx, testutils.ProjectId, region, items[i].Id).Execute()
|
|
if err != nil {
|
|
return fmt.Errorf("deleting instance %s during CheckDestroy: %w", items[i].Id, err)
|
|
}
|
|
err = postgresflexalpha.DeleteInstanceWaitHandler(
|
|
ctx,
|
|
client.DefaultAPI,
|
|
testutils.ProjectId,
|
|
testutils.Region,
|
|
items[i].Id,
|
|
15*time.Minute,
|
|
10*time.Second,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("deleting instance %s during CheckDestroy: waiting for deletion %w", items[i].Id, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func defaultNoEncInstanceTestChecks(testItemID string, data resData) resource.TestCheckFunc {
|
|
return resource.ComposeAggregateTestCheckFunc(
|
|
defaultInstanceTestChecks(testItemID, data),
|
|
|
|
// check absent attr
|
|
resource.TestCheckNoResourceAttr(testItemID, "encryption"),
|
|
resource.TestCheckNoResourceAttr(testItemID, "encryption.kek_key_id"),
|
|
resource.TestCheckNoResourceAttr(testItemID, "encryption.kek_key_ring_id"),
|
|
resource.TestCheckNoResourceAttr(testItemID, "encryption.kek_key_version"),
|
|
resource.TestCheckNoResourceAttr(testItemID, "encryption.service_account"),
|
|
)
|
|
}
|
|
|
|
func defaultEncInstanceTestChecks(testItemID string, data resData) resource.TestCheckFunc {
|
|
return resource.ComposeAggregateTestCheckFunc(
|
|
defaultInstanceTestChecks(testItemID, data),
|
|
|
|
// check absent attr
|
|
resource.TestCheckResourceAttr(testItemID, "encryption.%", "4"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "encryption.kek_key_id"),
|
|
resource.TestCheckResourceAttr(testItemID, "encryption.kek_key_id", data.KekKeyID),
|
|
resource.TestCheckResourceAttrSet(testItemID, "encryption.kek_key_ring_id"),
|
|
resource.TestCheckResourceAttr(testItemID, "encryption.kek_key_ring_id", data.KekKeyRingID),
|
|
resource.TestCheckResourceAttrSet(testItemID, "encryption.kek_key_version"),
|
|
resource.TestCheckResourceAttr(testItemID, "encryption.kek_key_version", strconv.Itoa(int(data.KekKeyVersion))),
|
|
resource.TestCheckResourceAttrSet(testItemID, "encryption.service_account"),
|
|
resource.TestCheckResourceAttr(testItemID, "encryption.service_account", data.KekServiceAccount),
|
|
)
|
|
}
|
|
|
|
func defaultInstanceTestChecks(testItemID string, data resData) resource.TestCheckFunc {
|
|
// if AccessScope == SNA these are set
|
|
if data.AccessScope == "SNA" {
|
|
return resource.ComposeAggregateTestCheckFunc(
|
|
basicInstanceTestChecks(testItemID, data),
|
|
resource.TestCheckResourceAttrSet(testItemID, "network.instance_address"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "network.router_address"),
|
|
)
|
|
}
|
|
|
|
// if AccessScope == PUBLIC these are empty - but they are set
|
|
return resource.ComposeAggregateTestCheckFunc(
|
|
basicInstanceTestChecks(testItemID, data),
|
|
resource.TestCheckResourceAttr(testItemID, "network.instance_address", ""),
|
|
resource.TestCheckResourceAttr(testItemID, "network.router_address", ""),
|
|
)
|
|
}
|
|
|
|
func basicInstanceTestChecks(testItemID string, data resData) resource.TestCheckFunc {
|
|
return resource.ComposeAggregateTestCheckFunc(
|
|
resource.TestCheckResourceAttrSet(testItemID, "backup_schedule"),
|
|
resource.TestCheckResourceAttr(testItemID, "backup_schedule", data.BackupSchedule),
|
|
|
|
resource.TestCheckResourceAttr(testItemID, "connection_info.%", "1"),
|
|
resource.TestCheckResourceAttr(testItemID, "connection_info.write.%", "2"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "connection_info.write.host"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "connection_info.write.port"),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "flavor_id"),
|
|
resource.TestCheckResourceAttr(testItemID, "flavor_id", data.FlavorID),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "id"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "instance_id"),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "is_deletable"),
|
|
resource.TestCheckResourceAttr(testItemID, "is_deletable", "true"),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "name"),
|
|
resource.TestCheckResourceAttr(testItemID, "name", data.Name),
|
|
|
|
// network params check
|
|
resource.TestCheckResourceAttr(testItemID, "network.%", "4"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "network.access_scope"),
|
|
resource.TestCheckResourceAttr(testItemID, "network.access_scope", data.AccessScope),
|
|
// resource.TestCheckResourceAttrSet(testItemID, "network.acl"),
|
|
resource.TestCheckResourceAttr(testItemID, "network.acl.#", strconv.Itoa(len(data.ACLStrings))),
|
|
// instance_address and router_address are only checked in enc
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "project_id"),
|
|
resource.TestCheckResourceAttr(testItemID, "project_id", data.ProjectID),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "region"),
|
|
resource.TestCheckResourceAttr(testItemID, "region", data.Region),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "replicas"),
|
|
resource.TestCheckResourceAttr(testItemID, "replicas", strconv.Itoa(int(data.Replicas))),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "retention_days"),
|
|
resource.TestCheckResourceAttr(testItemID, "retention_days", strconv.Itoa(int(data.RetentionDays))),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "status"),
|
|
resource.TestCheckResourceAttr(testItemID, "status", "READY"),
|
|
|
|
// storage params check
|
|
resource.TestCheckResourceAttr(testItemID, "storage.%", "2"),
|
|
resource.TestCheckResourceAttrSet(testItemID, "storage.performance_class"),
|
|
resource.TestCheckResourceAttr(testItemID, "storage.performance_class", data.PerformanceClass),
|
|
resource.TestCheckResourceAttrSet(testItemID, "storage.size"),
|
|
resource.TestCheckResourceAttr(testItemID, "storage.size", strconv.Itoa(int(data.Size))),
|
|
|
|
resource.TestCheckResourceAttrSet(testItemID, "version"),
|
|
resource.TestCheckResourceAttr(testItemID, "version", data.Version),
|
|
)
|
|
}
|
|
|
|
func getInstanceTestID(name string) func(s *terraform.State) (string, error) {
|
|
return func(s *terraform.State) (string, error) {
|
|
r, ok := s.RootModule().Resources[testutils.ResStr(pfx, "instance", name)]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find resource stackitprivatepreview_postgresflexalpha_instance.%s", name)
|
|
}
|
|
projectID, ok := r.Primary.Attributes["project_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute project_id")
|
|
}
|
|
region, ok := r.Primary.Attributes["region"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute region")
|
|
}
|
|
instanceID, ok := r.Primary.Attributes["instance_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute instance_id")
|
|
}
|
|
return fmt.Sprintf("%s,%s,%s", projectID, region, instanceID), nil
|
|
}
|
|
}
|
|
|
|
func getDatabaseTestID(name string) func(s *terraform.State) (string, error) {
|
|
return func(s *terraform.State) (string, error) {
|
|
r, ok := s.RootModule().Resources[testutils.ResStr(pfx, "database", name)]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find resource stackitprivatepreview_postgresflexalpha_instance.%s", name)
|
|
}
|
|
projectID, ok := r.Primary.Attributes["project_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute project_id")
|
|
}
|
|
region, ok := r.Primary.Attributes["region"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute region")
|
|
}
|
|
instanceID, ok := r.Primary.Attributes["instance_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute instance_id")
|
|
}
|
|
databaseID, ok := r.Primary.Attributes["database_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute database_id")
|
|
}
|
|
return fmt.Sprintf("%s,%s,%s,%s", projectID, region, instanceID, databaseID), nil
|
|
}
|
|
}
|
|
|
|
func getUserTestID(name string) func(s *terraform.State) (string, error) {
|
|
return func(s *terraform.State) (string, error) {
|
|
r, ok := s.RootModule().Resources[testutils.ResStr(pfx, "user", name)]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find resource stackitprivatepreview_postgresflexalpha_instance.%s", name)
|
|
}
|
|
projectID, ok := r.Primary.Attributes["project_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute project_id")
|
|
}
|
|
region, ok := r.Primary.Attributes["region"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute region")
|
|
}
|
|
instanceID, ok := r.Primary.Attributes["instance_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute instance_id")
|
|
}
|
|
userID, ok := r.Primary.Attributes["user_id"]
|
|
if !ok {
|
|
return "", fmt.Errorf("couldn't find attribute user_id")
|
|
}
|
|
return fmt.Sprintf("%s,%s,%s,%s", projectID, region, instanceID, userID), nil
|
|
}
|
|
}
|