Some checks failed
Publish / Check GoReleaser config (push) Successful in 7s
Publish / Publish provider (push) Successful in 7m41s
CI Workflow / Check GoReleaser config (pull_request) Successful in 6s
CI Workflow / Prepare GO cache (pull_request) Successful in 10m18s
CI Workflow / Test readiness for publishing provider (pull_request) Has been cancelled
CI Workflow / Code coverage report (pull_request) Has been cancelled
CI Workflow / CI run build and linting (pull_request) Has been cancelled
CI Workflow / CI run tests (pull_request) Has been cancelled
TF Acceptance Tests Workflow / Acceptance Tests (pull_request) Failing after 26m13s
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Co-authored-by: Marcel S. Henselin <marcel.henselin@stackit.cloud>
Reviewed-on: #85
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
|
|
}
|
|
}
|