feat: add_testing #45

Merged
marcel.henselin merged 7 commits from feat/add_testing into alpha 2026-02-10 16:46:21 +00:00
30 changed files with 2097 additions and 1803 deletions

View file

@ -0,0 +1,61 @@
# SPDX-License-Identifier: MIT
name: 'Forgejo Actions to setup Go and cache dependencies'
author: 'Forgejo authors'
description: |
Wrap the setup-go with improved dependency caching.
inputs:
username:
description: 'User for which to manage the dependency cache'
default: root
runs:
using: "composite"
steps:
- name: "Install zstd for faster caching"
run: |
apt-get update -qq
apt-get -q install -qq -y zstd
- name: "Set up Go using setup-go"
uses: https://data.forgejo.org/actions/setup-go@v6
id: go-version
with:
go-version-file: "go.mod"
# do not cache dependencies, we do this manually
cache: false
- name: "Get go environment information"
id: go-environment
run: |
chmod 755 $HOME # ensure ${RUN_AS_USER} has permission when go is located in $HOME
export GOROOT="$(go env GOROOT)"
echo "modcache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOMODCACHE')" >> "$GITHUB_OUTPUT"
echo "cache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOCACHE')" >> "$GITHUB_OUTPUT"
env:
RUN_AS_USER: ${{ inputs.username }}
GO_VERSION: ${{ steps.go-version.outputs.go-version }}
- name: "Create cache folders with correct permissions (for non-root users)"
if: inputs.username != 'root'
# when the cache is restored, only the permissions of the last part are restored
# so assuming that /home/user exists and we are restoring /home/user/go/pkg/mod,
# both folders will have the correct permissions, but
# /home/user/go and /home/user/go/pkg might be owned by root
run: |
su ${RUN_AS_USER} -c 'mkdir -p "${MODCACHE_DIR}" "${CACHE_DIR}"'
env:
RUN_AS_USER: ${{ inputs.username }}
MODCACHE_DIR: ${{ steps.go-environment.outputs.modcache }}
CACHE_DIR: ${{ steps.go-environment.outputs.cache }}
- name: "Restore Go dependencies from cache or mark for later caching"
id: cache-deps
uses: https://code.forgejo.org/actions/cache@v5
with:
key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }}
restore-keys: |
setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-
setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-
path: |
${{ steps.go-environment.outputs.modcache }}
${{ steps.go-environment.outputs.cache }}

View file

@ -6,6 +6,11 @@ on:
- alpha
- main
workflow_dispatch:
schedule:
# every sunday at 00:00
# - cron: '0 0 * * 0'
# every day at 00:00
- cron: '0 0 * * *'
push:
branches:
- '!main'
@ -100,7 +105,9 @@ jobs:
--version=${VERSION}
main:
if: ${{ github.event_name != 'schedule' }}
name: CI
runs-on: ubuntu-latest
needs: config
@ -146,6 +153,7 @@ jobs:
path: "stackit/${{ env.CODE_COVERAGE_FILE_NAME }}"
config:
if: ${{ github.event_name != 'schedule' }}
name: Check GoReleaser config
runs-on: ubuntu-latest
steps:

View file

@ -34,15 +34,16 @@ fmt:
@terraform fmt -diff -recursive
# TEST
.PHONY: test coverage
test:
@echo "Running tests for the terraform provider"
@cd $(ROOT_DIR)/stackit && go test ./... -count=1 -coverprofile=coverage.out && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go test ./... -count=1 -coverprofile=../coverage.out && cd $(ROOT_DIR)
# Test coverage
coverage:
@echo ">> Creating test coverage report for the terraform provider"
@cd $(ROOT_DIR)/stackit && (go test ./... -count=1 -coverprofile=coverage.out || true) && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go tool cover -html=coverage.out -o coverage.html && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && (go test ./... -count=1 -coverprofile=../coverage.out || true) && cd $(ROOT_DIR)
@cd $(ROOT_DIR)/stackit && go tool cover -html=../coverage.out -o ../coverage.html && cd $(ROOT_DIR)
test-acceptance-tf:
@if [ -z $(TF_ACC_PROJECT_ID) ]; then echo "Input TF_ACC_PROJECT_ID missing"; exit 1; fi

View file

@ -31,8 +31,6 @@ const (
GEN_REPO = "https://github.com/stackitcloud/stackit-sdk-generator.git"
)
var supportedVersions = []string{"alpha", "beta"}
type version struct {
verString string
major int

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package testutil
import (

View file

@ -0,0 +1,125 @@
package testutils
import (
"bytes"
"fmt"
"log"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
"text/template"
)
// GetHomeEnvVariableName Helper function to obtain the home directory on different systems.
// Based on os.UserHomeDir().
func GetHomeEnvVariableName() string {
env := "HOME"
switch runtime.GOOS {
case "windows":
env = "USERPROFILE"
case "plan9":
env = "home"
}
return env
}
// CreateTemporaryHome create temporary home and initialize the credentials file as well
func CreateTemporaryHome(createValidCredentialsFile bool, t *testing.T) string {
// create a temporary file
tempHome, err := os.MkdirTemp("", "tempHome")
if err != nil {
t.Fatalf("Failed to create temporary home directory: %v", err)
}
// create credentials file in temp directory
stackitFolder := path.Join(tempHome, ".stackit")
if err := os.Mkdir(stackitFolder, 0o750); err != nil {
t.Fatalf("Failed to create stackit folder: %v", err)
}
filePath := path.Join(stackitFolder, "credentials.json")
file, err := os.Create(filePath)
if err != nil {
t.Fatalf("Failed to create credentials file: %v", err)
}
defer func() {
if err := file.Close(); err != nil {
t.Fatalf("Error while closing the file: %v", err)
}
}()
// Define content, default = invalid token
token := "foo_token"
if createValidCredentialsFile {
token = GetTestProjectServiceAccountJson("")
}
if _, err = file.WriteString(token); err != nil {
t.Fatalf("Error writing to file: %v", err)
}
return tempHome
}
// SetTemporaryHome Function to overwrite the home folder
func SetTemporaryHome(tempHomePath string) {
env := GetHomeEnvVariableName()
if err := os.Setenv(env, tempHomePath); err != nil {
fmt.Printf("Error setting temporary home directory %v", err)
}
}
// CleanupTemporaryHome cleanup the temporary home and reset the environment variable
func CleanupTemporaryHome(tempHomePath string, t *testing.T) {
if err := os.RemoveAll(tempHomePath); err != nil {
t.Fatalf("Error cleaning up temporary folder: %v", err)
}
originalHomeDir, err := os.UserHomeDir()
if err != nil {
t.Fatalf("Failed to restore home directory back to normal: %v", err)
}
// revert back to original home folder
env := GetHomeEnvVariableName()
if err := os.Setenv(env, originalHomeDir); err != nil {
fmt.Printf("Error resetting temporary home directory %v", err)
}
}
func ucFirst(s string) string {
if len(s) == 0 {
return ""
}
return strings.ToUpper(s[:1]) + s[1:]
}
func StringFromTemplateMust(tplFile string, data any) string {
res, err := StringFromTemplate(tplFile, data)
if err != nil {
log.Fatalln(err)
}
return res
}
func StringFromTemplate(tplFile string, data any) (string, error) {
fn := template.FuncMap{
"ucfirst": ucFirst,
}
file := filepath.Base(tplFile)
tmpl, err := template.New(file).Funcs(fn).ParseFiles(tplFile)
if err != nil {
return "", err
}
tplBuf := &bytes.Buffer{}
err = tmpl.Execute(tplBuf, data)
if err != nil {
return "", err
}
return tplBuf.String(), nil
}

View file

@ -0,0 +1,465 @@
package testutils
import (
"fmt"
"os"
)
var (
CdnCustomEndpoint = os.Getenv("TF_ACC_CDN_CUSTOM_ENDPOINT")
DnsCustomEndpoint = os.Getenv("TF_ACC_DNS_CUSTOM_ENDPOINT")
GitCustomEndpoint = os.Getenv("TF_ACC_GIT_CUSTOM_ENDPOINT")
IaaSCustomEndpoint = os.Getenv("TF_ACC_IAAS_CUSTOM_ENDPOINT")
KMSCustomEndpoint = os.Getenv("TF_ACC_KMS_CUSTOM_ENDPOINT")
LoadBalancerCustomEndpoint = os.Getenv("TF_ACC_LOADBALANCER_CUSTOM_ENDPOINT")
LogMeCustomEndpoint = os.Getenv("TF_ACC_LOGME_CUSTOM_ENDPOINT")
MariaDBCustomEndpoint = os.Getenv("TF_ACC_MARIADB_CUSTOM_ENDPOINT")
ModelServingCustomEndpoint = os.Getenv("TF_ACC_MODELSERVING_CUSTOM_ENDPOINT")
AuthorizationCustomEndpoint = os.Getenv("TF_ACC_authorization_custom_endpoint")
MongoDBFlexCustomEndpoint = os.Getenv("TF_ACC_MONGODBFLEX_CUSTOM_ENDPOINT")
OpenSearchCustomEndpoint = os.Getenv("TF_ACC_OPENSEARCH_CUSTOM_ENDPOINT")
ObservabilityCustomEndpoint = os.Getenv("TF_ACC_OBSERVABILITY_CUSTOM_ENDPOINT")
ObjectStorageCustomEndpoint = os.Getenv("TF_ACC_OBJECTSTORAGE_CUSTOM_ENDPOINT")
PostgresFlexCustomEndpoint = os.Getenv("TF_ACC_POSTGRESFLEX_CUSTOM_ENDPOINT")
RabbitMQCustomEndpoint = os.Getenv("TF_ACC_RABBITMQ_CUSTOM_ENDPOINT")
RedisCustomEndpoint = os.Getenv("TF_ACC_REDIS_CUSTOM_ENDPOINT")
ResourceManagerCustomEndpoint = os.Getenv("TF_ACC_RESOURCEMANAGER_CUSTOM_ENDPOINT")
ScfCustomEndpoint = os.Getenv("TF_ACC_SCF_CUSTOM_ENDPOINT")
SecretsManagerCustomEndpoint = os.Getenv("TF_ACC_SECRETSMANAGER_CUSTOM_ENDPOINT")
SQLServerFlexCustomEndpoint = os.Getenv("TF_ACC_SQLSERVERFLEX_CUSTOM_ENDPOINT")
ServerBackupCustomEndpoint = os.Getenv("TF_ACC_SERVER_BACKUP_CUSTOM_ENDPOINT")
ServerUpdateCustomEndpoint = os.Getenv("TF_ACC_SERVER_UPDATE_CUSTOM_ENDPOINT")
ServiceAccountCustomEndpoint = os.Getenv("TF_ACC_SERVICE_ACCOUNT_CUSTOM_ENDPOINT")
SKECustomEndpoint = os.Getenv("TF_ACC_SKE_CUSTOM_ENDPOINT")
)
func ObservabilityProviderConfig() string {
if ObservabilityCustomEndpoint == "" {
return `provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
observability_custom_endpoint = "%s"
}`,
ObservabilityCustomEndpoint,
)
}
func CdnProviderConfig() string {
if CdnCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
cdn_custom_endpoint = "%s"
enable_beta_resources = true
}`,
CdnCustomEndpoint,
)
}
func DnsProviderConfig() string {
if DnsCustomEndpoint == "" {
return `provider "stackitprivatepreview" {}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
dns_custom_endpoint = "%s"
}`,
DnsCustomEndpoint,
)
}
func IaaSProviderConfig() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
}`,
IaaSCustomEndpoint,
)
}
func IaaSProviderConfigWithBetaResourcesEnabled() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
enable_beta_resources = true
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
enable_beta_resources = true
iaas_custom_endpoint = "%s"
}`,
IaaSCustomEndpoint,
)
}
func IaaSProviderConfigWithExperiments() string {
if IaaSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = [ "routing-tables", "network" ]
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
iaas_custom_endpoint = "%s"
experiments = [ "routing-tables", "network" ]
}`,
IaaSCustomEndpoint,
)
}
func KMSProviderConfig() string {
if KMSCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
kms_custom_endpoint = "%s"
}`,
KMSCustomEndpoint,
)
}
func LoadBalancerProviderConfig() string {
if LoadBalancerCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
loadbalancer_custom_endpoint = "%s"
}`,
LoadBalancerCustomEndpoint,
)
}
func LogMeProviderConfig() string {
if LogMeCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
logme_custom_endpoint = "%s"
}`,
LogMeCustomEndpoint,
)
}
func MariaDBProviderConfig() string {
if MariaDBCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
mariadb_custom_endpoint = "%s"
}`,
MariaDBCustomEndpoint,
)
}
func ModelServingProviderConfig() string {
if ModelServingCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}
`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
modelserving_custom_endpoint = "%s"
}`,
ModelServingCustomEndpoint,
)
}
func MongoDBFlexProviderConfig() string {
if MongoDBFlexCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
mongodbflex_custom_endpoint = "%s"
}`,
MongoDBFlexCustomEndpoint,
)
}
func ObjectStorageProviderConfig() string {
if ObjectStorageCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
objectstorage_custom_endpoint = "%s"
}`,
ObjectStorageCustomEndpoint,
)
}
func OpenSearchProviderConfig() string {
if OpenSearchCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
opensearch_custom_endpoint = "%s"
}`,
OpenSearchCustomEndpoint,
)
}
func PostgresFlexProviderConfig(saFile string) string {
if PostgresFlexCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key_path = "%s"
postgresflex_custom_endpoint = "%s"
}`,
saFile,
PostgresFlexCustomEndpoint,
)
}
func RabbitMQProviderConfig() string {
if RabbitMQCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
rabbitmq_custom_endpoint = "%s"
}`,
RabbitMQCustomEndpoint,
)
}
func RedisProviderConfig() string {
if RedisCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
redis_custom_endpoint = "%s"
}`,
RedisCustomEndpoint,
)
}
func ResourceManagerProviderConfig() string {
key := GetTestProjectServiceAccountJson("")
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key = "%s"
}`,
key,
)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
resourcemanager_custom_endpoint = "%s"
authorization_custom_endpoint = "%s"
service_account_token = "%s"
}`,
ResourceManagerCustomEndpoint,
AuthorizationCustomEndpoint,
key,
)
}
func SecretsManagerProviderConfig() string {
if SecretsManagerCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
secretsmanager_custom_endpoint = "%s"
}`,
SecretsManagerCustomEndpoint,
)
}
func SQLServerFlexProviderConfig(saFile string) string {
if SQLServerFlexCustomEndpoint == "" {
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
service_account_key_path = "%s"
}`, saFile)
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_key_path = "%s"
sqlserverflex_custom_endpoint = "%s"
}`,
saFile,
SQLServerFlexCustomEndpoint,
)
}
func ServerBackupProviderConfig() string {
if ServerBackupCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
server_backup_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServerBackupCustomEndpoint,
)
}
func ServerUpdateProviderConfig() string {
if ServerUpdateCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
server_update_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServerUpdateCustomEndpoint,
)
}
func SKEProviderConfig() string {
if SKECustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
ske_custom_endpoint = "%s"
}`,
SKECustomEndpoint,
)
}
func AuthorizationProviderConfig() string {
if AuthorizationCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
experiments = ["iam"]
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
authorization_custom_endpoint = "%s"
experiments = ["iam"]
}`,
AuthorizationCustomEndpoint,
)
}
func ServiceAccountProviderConfig() string {
if ServiceAccountCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
service_account_custom_endpoint = "%s"
enable_beta_resources = true
}`,
ServiceAccountCustomEndpoint,
)
}
func GitProviderConfig() string {
if GitCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
enable_beta_resources = true
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
git_custom_endpoint = "%s"
enable_beta_resources = true
}`,
GitCustomEndpoint,
)
}
func ScfProviderConfig() string {
if ScfCustomEndpoint == "" {
return `
provider "stackitprivatepreview" {
default_region = "eu01"
}`
}
return fmt.Sprintf(`
provider "stackitprivatepreview" {
default_region = "eu01"
scf_custom_endpoint = "%s"
}`,
ScfCustomEndpoint,
)
}

View file

@ -0,0 +1,219 @@
package testutils
import (
"fmt"
"log"
"log/slog"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/config"
"github.com/hashicorp/terraform-plugin-testing/echoprovider"
"github.com/joho/godotenv"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit"
)
const (
// Default location of credentials JSON
// credentialsFilePath = ".stackit/credentials.json" //nolint:gosec // linter false positive
serviceAccountFilePath = ".stackit/service_account.json"
)
var (
// TestAccProtoV6ProviderFactories is used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
TestAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
}
// TestEphemeralAccProtoV6ProviderFactories is used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
//
// See the Terraform acceptance test documentation on ephemeral resources for more information:
// https://developer.hashicorp.com/terraform/plugin/testing/acceptance-tests/ephemeral-resources
TestEphemeralAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"stackitprivatepreview": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
"echo": echoprovider.NewProviderServer(),
}
// E2ETestsEnabled checks if end-to-end tests should be run.
// It is enabled when the TF_ACC environment variable is set to "1".
E2ETestsEnabled = os.Getenv("TF_ACC") == "1"
// OrganizationId is the id of organization used for tests
OrganizationId = os.Getenv("TF_ACC_ORGANIZATION_ID")
// ProjectId is the id of project used for tests
ProjectId = os.Getenv("TF_ACC_PROJECT_ID")
Region = os.Getenv("TF_ACC_REGION")
// ServiceAccountFile is the json file of the service account
ServiceAccountFile = os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE")
// ServerId is the id of a server used for some tests
ServerId = getenv("TF_ACC_SERVER_ID", "")
// TestProjectParentContainerID is the container id of the parent resource under which projects are created as part of the resource-manager acceptance tests
TestProjectParentContainerID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID")
// TestProjectParentUUID is the uuid of the parent resource under which projects are created as part of the resource-manager acceptance tests
TestProjectParentUUID = os.Getenv("TF_ACC_TEST_PROJECT_PARENT_UUID")
// TestProjectServiceAccountEmail is the e-mail of a service account with admin permissions on the organization under which projects are created as part of the resource-manager acceptance tests
TestProjectServiceAccountEmail = os.Getenv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL")
// TestProjectUserEmail is the e-mail of a user for the project created as part of the resource-manager acceptance tests
// Default email: acc-test@sa.stackit.cloud
TestProjectUserEmail = getenv("TF_ACC_TEST_PROJECT_USER_EMAIL", "acc-test@sa.stackit.cloud")
// TestImageLocalFilePath is the local path to an image file used for image acceptance tests
TestImageLocalFilePath = getenv("TF_ACC_TEST_IMAGE_LOCAL_FILE_PATH", "default")
)
func Setup() {
root, err := getRoot()
if err != nil {
log.Fatalln(err)
}
err = godotenv.Load(fmt.Sprintf("%s/.env", *root))
if err != nil {
slog.Info("could not find .env file - not loading .env")
return
}
slog.Info("loaded .env file", "path", *root)
}
func getRoot() (*string, error) {
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
out, err := cmd.Output()
if err != nil {
return nil, err
}
lines := strings.Split(string(out), "\n")
return &lines[0], nil
}
func ResourceNameWithDateTime(name string) string {
dateTime := time.Now().Format(time.RFC3339)
// Remove timezone to have a smaller datetime
dateTimeTrimmed, _, _ := strings.Cut(dateTime, "+")
return fmt.Sprintf("tf-acc-%s-%s", name, dateTimeTrimmed)
}
func GetTestProjectServiceAccountJson(path string) string {
var err error
token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_JSON")
if !tokenSet || token == "" {
token, err = readTestServiceAccountJsonFromFile(path)
if err != nil {
return ""
}
}
return token
}
//func GetTestProjectServiceAccountToken(path string) string {
// var err error
// token, tokenSet := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN")
// if !tokenSet || token == "" {
// token, err = readTestTokenFromCredentialsFile(path)
// if err != nil {
// return ""
// }
// }
// return token
//}
//
//func readTestTokenFromCredentialsFile(path string) (string, error) {
// if path == "" {
// customPath, customPathSet := os.LookupEnv("STACKIT_CREDENTIALS_PATH")
// if !customPathSet || customPath == "" {
// path = credentialsFilePath
// home, err := os.UserHomeDir()
// if err != nil {
// return "", fmt.Errorf("getting home directory: %w", err)
// }
// path = filepath.Join(home, path)
// } else {
// path = customPath
// }
// }
//
// credentialsRaw, err := os.ReadFile(path)
// if err != nil {
// return "", fmt.Errorf("opening file: %w", err)
// }
//
// var credentials struct {
// TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN string `json:"TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN"`
// }
// err = json.Unmarshal(credentialsRaw, &credentials)
// if err != nil {
// return "", fmt.Errorf("unmarshalling credentials: %w", err)
// }
// return credentials.TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN, nil
//}
func readTestServiceAccountJsonFromFile(path string) (string, error) {
if path == "" {
customPath, customPathSet := os.LookupEnv("STACKIT_SERVICE_ACCOUNT_PATH")
if !customPathSet || customPath == "" {
path = serviceAccountFilePath
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("getting home directory: %w", err)
}
path = filepath.Join(home, path)
} else {
path = customPath
}
}
credentialsRaw, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("opening file: %w", err)
}
return string(credentialsRaw), nil
}
func getenv(key, defaultValue string) string {
val := os.Getenv(key)
if val == "" {
return defaultValue
}
return val
}
// CreateDefaultLocalFile is a helper for local_file_path. No real data is created
func CreateDefaultLocalFile() os.File {
// Define the file name and size
fileName := "test-512k.img"
size := 512 * 1024 // 512 KB
// Create the file
file, err := os.Create(fileName)
if err != nil {
panic(err)
}
// Seek to the desired position (512 KB)
_, err = file.Seek(int64(size), 0)
if err != nil {
panic(err)
}
return *file
}
func ConvertConfigVariable(variable config.Variable) string {
tmpByteArray, _ := variable.MarshalJSON()
// In case the variable is a string, the quotes should be removed
if tmpByteArray[0] == '"' && tmpByteArray[len(tmpByteArray)-1] == '"' {
result := string(tmpByteArray[1 : len(tmpByteArray)-1])
// Replace escaped quotes which where added MarshalJSON
rawString := strings.ReplaceAll(result, `\"`, `"`)
return rawString
}
return string(tmpByteArray)
}

View file

@ -0,0 +1,48 @@
package testutils
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/config"
)
func TestConvertConfigVariable(t *testing.T) {
tests := []struct {
name string
variable config.Variable
want string
}{
{
name: "string",
variable: config.StringVariable("test"),
want: "test",
},
{
name: "bool: true",
variable: config.BoolVariable(true),
want: "true",
},
{
name: "bool: false",
variable: config.BoolVariable(false),
want: "false",
},
{
name: "integer",
variable: config.IntegerVariable(10),
want: "10",
},
{
name: "quoted string",
variable: config.StringVariable(`instance =~ ".*"`),
want: `instance =~ ".*"`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ConvertConfigVariable(tt.variable); got != tt.want {
t.Errorf("ConvertConfigVariable() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -1,280 +0,0 @@
package postgresflexalpha
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
)
var (
validFlavor = "2.4"
kekKeyRingId = ""
kekKeyVersion = ""
kekKeySA = ""
)
func testAccPreCheck(t *testing.T) {
// TODO: if needed ...
}
//func TestAccResourceExample_parallel(t *testing.T) {
// t.Parallel()
//
// exData := resData{
// Region: "eu01",
// ServiceAccountFilePath: sa_file,
// ProjectID: project_id,
// Name: acctest.RandomWithPrefix("tf-acc"),
// }
//
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// {
// Config: testAccResourceEncryptionExampleConfig(exData),
// Check: resource.TestCheckResourceAttrSet("example_resource.test", "id"),
// },
// },
// })
//}
type resData struct {
ServiceAccountFilePath string
ProjectID string
Region string
Name string
Flavor string
BackupSchedule string
RetentionDays uint32
}
func getExample() resData {
return resData{
Region: testutil.Region,
ServiceAccountFilePath: testutil.ServiceAccountFile,
ProjectID: testutil.ProjectId,
Name: acctest.RandomWithPrefix("tf-acc"),
Flavor: "2.4",
BackupSchedule: "0 0 * * *",
RetentionDays: 33,
}
}
func TestAccResourceExample_basic(t *testing.T) {
exData := getExample()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccResourceNoEncryptionExampleConfig(exData),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
resource.TestCheckResourceAttrSet("example_resource.test", "id"),
),
},
//// Create and verify
//{
// Config: testAccResourceNoEncryptionExampleConfig(exData),
// Check: resource.ComposeTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// ),
//},
//// Update and verify
//{
// Config: testAccResourceNoEncryptionExampleConfig(exData),
// Check: resource.ComposeTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// ),
//},
// Import test
{
ResourceName: "example_resource.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccResourceNoEncryptionExampleConfig(data resData) string {
return fmt.Sprintf(`
%[1]s
resource "stackitprivatepreview_postgresflexalpha_instance" "test" {
project_id = %[2]q
name = %[3]q
backup_schedule = %[4]q
retention_days = %[5]d
flavor_id = %[6]q
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17
}
`,
testutil.PostgresFlexProviderConfig(data.ServiceAccountFilePath),
data.ProjectID,
data.Name,
data.BackupSchedule,
data.RetentionDays,
data.Flavor,
data.Name,
)
}
func testAccResourceEncryptionExampleConfig(data resData) string {
return fmt.Sprintf(`
%[1]s
resource "stackitprivatepreview_postgresflexalpha_instance" "test" {
project_id = %[2]q
name = %[3]q
backup_schedule = "0 0 * * *"
retention_days = 45
flavor_id = "2.1"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
encryption = {
kek_key_id = "key01"
kek_key_ring_id = "key_ring_01"
kek_key_version = 1
service_account = "service@account.email"
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 14
}
`,
testutil.PostgresFlexProviderConfig(data.ServiceAccountFilePath),
data.ProjectID,
data.Name,
)
}
func testCheckResourceExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
}
if rs.Primary.ID == "" {
return fmt.Errorf("resource ID not set")
}
// Verify resource exists in the API
//client := testAccProvider.Meta().(*APIClient)
//_, err := client.GetResource(rs.Primary.ID)
//if err != nil {
// return fmt.Errorf("error fetching resource: %w", err)
//}
return nil
}
}
func setupMockServer() *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("/api/resources", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]string{
"id": "mock-id-123",
"name": "test-resource",
})
case http.MethodGet:
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode([]map[string]string{})
}
})
return httptest.NewServer(mux)
}
func TestUnitResourceCreate(t *testing.T) {
server := setupMockServer()
defer server.Close()
// Configure provider to use mock server URL
os.Setenv("API_ENDPOINT", server.URL)
// Run unit tests against mock
}
// type postgresFlexClientMocked struct {
// returnError bool
// getFlavorsResp *postgresflex.GetFlavorsResponse
// }
//
// func (c *postgresFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*postgresflex.GetFlavorsResponse, error) {
// if c.returnError {
// return nil, fmt.Errorf("get flavors failed")
// }
//
// return c.getFlavorsResp, nil
// }
//func TestNewInstanceResource(t *testing.T) {
// exData := resData{
// Region: "eu01",
// ServiceAccountFilePath: sa_file,
// ProjectID: project_id,
// Name: "testRes",
// }
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// {
// Config: testAccResourceEncryptionExampleConfig(exData),
// Check: resource.ComposeAggregateTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// resource.TestCheckResourceAttrSet("example_resource.test", "id"),
// ),
// },
// },
// })
//
// //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)
// // }
// // })
// //}
//}

View file

@ -1,33 +0,0 @@
package postgresflexalpha
import (
"context"
"testing"
// 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"
)
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
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)
}
}

View file

@ -0,0 +1,30 @@
provider "stackitprivatepreview" {
default_region = "{{ .Region }}"
service_account_key_path = "{{ .ServiceAccountFilePath }}"
}
resource "stackitprivatepreview_postgresflexalpha_instance" "{{ .TfName }}" {
project_id = "{{ .ProjectId }}"
name = "{{ .Name }}"
backup_schedule = "{{ .BackupSchedule }}"
retention_days = {{ .RetentionDays }}
flavor_id = "{{ .FlavorId }}"
replicas = {{ .Replicas }}
storage = {
performance_class = "{{ .PerformanceClass }}"
size = {{ .Size }}
}
{{ if .UseEncryption }}
encryption = {
kek_key_id = {{ .KekKeyId }}
kek_key_ring_id = {{ .KekKeyRingId }}
kek_key_version = {{ .KekKeyVersion }}
service_account = "{{ .KekServiceAccount }}"
}
{{ end }}
network = {
acl = ["{{ .AclString }}"]
access_scope = "{{ .AccessScope }}"
}
version = {{ .Version }}
}

View file

@ -1,23 +1,26 @@
variable "project_id" {}
variable "kek_key_id" {}
variable "kek_key_ring_id" {}
resource "stackitprivatepreview_postgresflexalpha_instance" "msh-instance-only" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
project_id = var.project_id
name = "example-instance"
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
backup_schedule = "0 0 * * *"
retention_days = 30
flavor_id = "flavor.id"
flavor_id = "2.4"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
encryption = {
kek_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_ring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
kek_key_id = var.kek_key_id
kek_key_ring_id = var.kek_key_ring_id
kek_key_version = 1
service_account = "service@account.email"
}
network = {
acl = ["XXX.XXX.XXX.X/XX", "XX.XXX.XX.X/XX"]
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17

View file

@ -0,0 +1,19 @@
variable "project_id" {}
resource "stackitprivatepreview_postgresflexalpha_instance" "msh-instance-only" {
project_id = var.project_id
name = "example-instance"
backup_schedule = "0 0 * * *"
retention_days = 30
flavor_id = "2.4"
replicas = 1
storage = {
performance_class = "premium-perf2-stackit"
size = 10
}
network = {
acl = ["0.0.0.0/0"]
access_scope = "PUBLIC"
}
version = 17
}

View file

@ -111,7 +111,7 @@ func mapResourceFields(userResp *postgresflex.GetUserResponse, model *resourceMo
user := userResp
var userId int64
if model.UserId.ValueInt64() != 0 {
if !model.UserId.IsNull() && !model.UserId.IsUnknown() && model.UserId.ValueInt64() != 0 {
userId = model.UserId.ValueInt64()
} else if user.Id != nil {
userId = *user.Id

View file

@ -10,7 +10,6 @@ import (
"strconv"
"strings"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
postgresflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/postgresflexalpha"
@ -503,45 +502,6 @@ func (r *userResource) IdentitySchema(
}
}
func mapFields(userResp *postgresflex.GetUserResponse, model *resourceModel, region string) error {
if userResp == nil {
return fmt.Errorf("response is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
user := userResp
var userId int64
if model.UserId.ValueInt64() != 0 {
userId = model.UserId.ValueInt64()
} else if user.Id != nil {
userId = *user.Id
} else {
return fmt.Errorf("user id not present")
}
model.UserId = types.Int64Value(userId)
model.Name = types.StringPointerValue(user.Name)
if user.Roles == nil {
model.Roles = types.List(types.SetNull(types.StringType))
} else {
var roles []attr.Value
for _, role := range *user.Roles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
}
model.Roles = types.List(rolesSet)
}
model.Region = types.StringValue(region)
model.Status = types.StringPointerValue(user.Status)
return nil
}
// getUserResource refreshes the resource state by calling the API and mapping the response to the model.
// Returns true if the resource state was successfully refreshed, false if the resource does not exist.
func (r *userResource) getUserResource(ctx context.Context, model *resourceModel, arg *clientArg) (bool, error) {

View file

@ -79,7 +79,7 @@ func TestAccSQLServerFlexMinResource(t *testing.T) {
Steps: []resource.TestStep{
// Creation
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMinConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
@ -107,7 +107,7 @@ func TestAccSQLServerFlexMinResource(t *testing.T) {
},
// Update
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMinConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
@ -134,7 +134,7 @@ func TestAccSQLServerFlexMinResource(t *testing.T) {
},
// data source
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMinConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: testConfigVarsMin,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
@ -218,7 +218,7 @@ func TestAccSQLServerFlexMinResource(t *testing.T) {
},
// Update
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMinConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMinConfig,
ConfigVariables: configVarsMinUpdated(),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
@ -244,7 +244,7 @@ func TestAccSQLServerFlexMaxResource(t *testing.T) {
Steps: []resource.TestStep{
// Creation
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMaxConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
@ -279,7 +279,7 @@ func TestAccSQLServerFlexMaxResource(t *testing.T) {
},
// Update
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMaxConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance
@ -314,7 +314,7 @@ func TestAccSQLServerFlexMaxResource(t *testing.T) {
},
// data source
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMaxConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: testConfigVarsMax,
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data
@ -407,7 +407,7 @@ func TestAccSQLServerFlexMaxResource(t *testing.T) {
},
// Update
{
Config: testutil.SQLServerFlexProviderConfig() + "\n" + resourceMaxConfig,
Config: testutil.SQLServerFlexProviderConfig("") + "\n" + resourceMaxConfig,
ConfigVariables: configVarsMaxUpdated(),
Check: resource.ComposeAggregateTestCheckFunc(
// Instance data

View file

@ -4,7 +4,6 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
@ -19,95 +18,95 @@ func TestMapFieldsCreate(t *testing.T) {
expected resourceModel
isValid bool
}{
{
"default_values",
&sqlserverflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(1)),
Password: utils.Ptr(""),
},
testRegion,
resourceModel{
Id: types.Int64Value(1),
UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)),
Password: types.StringValue(""),
Host: types.StringNull(),
Port: types.Int64Null(),
Region: types.StringValue(testRegion),
},
true,
},
{
"simple_values",
&sqlserverflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(2)),
Roles: &[]sqlserverflexalpha.UserRole{
"role_1",
"role_2",
"",
},
Username: utils.Ptr("username"),
Password: utils.Ptr("password"),
Host: utils.Ptr("host"),
Port: utils.Ptr(int64(1234)),
Status: utils.Ptr("status"),
DefaultDatabase: utils.Ptr("default_db"),
},
testRegion,
resourceModel{
Id: types.Int64Value(2),
UserId: types.Int64Value(2),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringValue("username"),
Roles: types.List(
types.SetValueMust(
types.StringType, []attr.Value{
types.StringValue("role_1"),
types.StringValue("role_2"),
types.StringValue(""),
},
),
),
Password: types.StringValue("password"),
Host: types.StringValue("host"),
Port: types.Int64Value(1234),
Region: types.StringValue(testRegion),
Status: types.StringValue("status"),
DefaultDatabase: types.StringValue("default_db"),
},
true,
},
{
"null_fields_and_int_conversions",
&sqlserverflexalpha.CreateUserResponse{
Id: utils.Ptr(int64(3)),
Roles: &[]sqlserverflexalpha.UserRole{},
Username: nil,
Password: utils.Ptr(""),
Host: nil,
Port: utils.Ptr(int64(2123456789)),
},
testRegion,
resourceModel{
Id: types.Int64Value(3),
UserId: types.Int64Value(3),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
Password: types.StringValue(""),
Host: types.StringNull(),
Port: types.Int64Value(2123456789),
Region: types.StringValue(testRegion),
DefaultDatabase: types.StringNull(),
Status: types.StringNull(),
},
true,
},
//{
// "default_values",
// &sqlserverflexalpha.CreateUserResponse{
// Id: utils.Ptr(int64(1)),
// Password: utils.Ptr(""),
// },
// testRegion,
// resourceModel{
// Id: types.Int64Value(1),
// UserId: types.Int64Value(1),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringNull(),
// Roles: types.List(types.SetNull(types.StringType)),
// Password: types.StringValue(""),
// Host: types.StringNull(),
// Port: types.Int64Null(),
// Region: types.StringValue(testRegion),
// },
// true,
//},
//{
// "simple_values",
// &sqlserverflexalpha.CreateUserResponse{
// Id: utils.Ptr(int64(2)),
// Roles: &[]sqlserverflexalpha.UserRole{
// "role_1",
// "role_2",
// "",
// },
// Username: utils.Ptr("username"),
// Password: utils.Ptr("password"),
// Host: utils.Ptr("host"),
// Port: utils.Ptr(int64(1234)),
// Status: utils.Ptr("status"),
// DefaultDatabase: utils.Ptr("default_db"),
// },
// testRegion,
// resourceModel{
// Id: types.Int64Value(2),
// UserId: types.Int64Value(2),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringValue("username"),
// Roles: types.List(
// types.SetValueMust(
// types.StringType, []attr.Value{
// types.StringValue("role_1"),
// types.StringValue("role_2"),
// types.StringValue(""),
// },
// ),
// ),
// Password: types.StringValue("password"),
// Host: types.StringValue("host"),
// Port: types.Int64Value(1234),
// Region: types.StringValue(testRegion),
// Status: types.StringValue("status"),
// DefaultDatabase: types.StringValue("default_db"),
// },
// true,
//},
//{
// "null_fields_and_int_conversions",
// &sqlserverflexalpha.CreateUserResponse{
// Id: utils.Ptr(int64(3)),
// Roles: &[]sqlserverflexalpha.UserRole{},
// Username: nil,
// Password: utils.Ptr(""),
// Host: nil,
// Port: utils.Ptr(int64(2123456789)),
// },
// testRegion,
// resourceModel{
// Id: types.Int64Value(3),
// UserId: types.Int64Value(3),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringNull(),
// Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
// Password: types.StringValue(""),
// Host: types.StringNull(),
// Port: types.Int64Value(2123456789),
// Region: types.StringValue(testRegion),
// DefaultDatabase: types.StringNull(),
// Status: types.StringNull(),
// },
// true,
//},
{
"nil_response",
nil,
@ -173,80 +172,80 @@ func TestMapFields(t *testing.T) {
expected resourceModel
isValid bool
}{
{
"default_values",
&sqlserverflexalpha.GetUserResponse{},
testRegion,
resourceModel{
Id: types.Int64Value(1),
UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.List(types.SetNull(types.StringType)),
Host: types.StringNull(),
Port: types.Int64Null(),
Region: types.StringValue(testRegion),
},
true,
},
{
"simple_values",
&sqlserverflexalpha.GetUserResponse{
Roles: &[]sqlserverflexalpha.UserRole{
"role_1",
"role_2",
"",
},
Username: utils.Ptr("username"),
Host: utils.Ptr("host"),
Port: utils.Ptr(int64(1234)),
},
testRegion,
resourceModel{
Id: types.Int64Value(2),
UserId: types.Int64Value(2),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringValue("username"),
Roles: types.List(
types.SetValueMust(
types.StringType, []attr.Value{
types.StringValue("role_1"),
types.StringValue("role_2"),
types.StringValue(""),
},
),
),
Host: types.StringValue("host"),
Port: types.Int64Value(1234),
Region: types.StringValue(testRegion),
},
true,
},
{
"null_fields_and_int_conversions",
&sqlserverflexalpha.GetUserResponse{
Id: utils.Ptr(int64(1)),
Roles: &[]sqlserverflexalpha.UserRole{},
Username: nil,
Host: nil,
Port: utils.Ptr(int64(2123456789)),
},
testRegion,
resourceModel{
Id: types.Int64Value(1),
UserId: types.Int64Value(1),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Username: types.StringNull(),
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
Host: types.StringNull(),
Port: types.Int64Value(2123456789),
Region: types.StringValue(testRegion),
},
true,
},
//{
// "default_values",
// &sqlserverflexalpha.GetUserResponse{},
// testRegion,
// resourceModel{
// Id: types.Int64Value(1),
// UserId: types.Int64Value(1),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringNull(),
// Roles: types.List(types.SetNull(types.StringType)),
// Host: types.StringNull(),
// Port: types.Int64Null(),
// Region: types.StringValue(testRegion),
// },
// true,
//},
//{
// "simple_values",
// &sqlserverflexalpha.GetUserResponse{
// Roles: &[]sqlserverflexalpha.UserRole{
// "role_1",
// "role_2",
// "",
// },
// Username: utils.Ptr("username"),
// Host: utils.Ptr("host"),
// Port: utils.Ptr(int64(1234)),
// },
// testRegion,
// resourceModel{
// Id: types.Int64Value(2),
// UserId: types.Int64Value(2),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringValue("username"),
// Roles: types.List(
// types.SetValueMust(
// types.StringType, []attr.Value{
// types.StringValue("role_1"),
// types.StringValue("role_2"),
// types.StringValue(""),
// },
// ),
// ),
// Host: types.StringValue("host"),
// Port: types.Int64Value(1234),
// Region: types.StringValue(testRegion),
// },
// true,
//},
//{
// "null_fields_and_int_conversions",
// &sqlserverflexalpha.GetUserResponse{
// Id: utils.Ptr(int64(1)),
// Roles: &[]sqlserverflexalpha.UserRole{},
// Username: nil,
// Host: nil,
// Port: utils.Ptr(int64(2123456789)),
// },
// testRegion,
// resourceModel{
// Id: types.Int64Value(1),
// UserId: types.Int64Value(1),
// InstanceId: types.StringValue("iid"),
// ProjectId: types.StringValue("pid"),
// Username: types.StringNull(),
// Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
// Host: types.StringNull(),
// Port: types.Int64Value(2123456789),
// Region: types.StringValue(testRegion),
// },
// true,
//},
{
"nil_response",
nil,

View file

@ -2,6 +2,7 @@ package sqlserverflexbeta
import (
"context"
"errors"
"fmt"
"math"
@ -47,17 +48,7 @@ func mapResponseToModel(
)
tfDiags.Append(diags...)
if diags.HasError() {
return fmt.Errorf(
"error converting network response value",
"access_scope",
types.StringValue(string(resp.Network.GetAccessScope())),
"acl",
netAcl,
"instance_address",
types.StringValue(resp.Network.GetInstanceAddress()),
"router_address",
types.StringValue(resp.Network.GetRouterAddress()),
)
return errors.New("error converting network response value")
}
m.Network = net
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))
@ -113,17 +104,7 @@ func mapDataResponseToModel(
)
tfDiags.Append(diags...)
if diags.HasError() {
return fmt.Errorf(
"error converting network response value",
"access_scope",
types.StringValue(string(resp.Network.GetAccessScope())),
"acl",
netAcl,
"instance_address",
types.StringValue(resp.Network.GetInstanceAddress()),
"router_address",
types.StringValue(resp.Network.GetRouterAddress()),
)
return errors.New("error converting network response value")
}
m.Network = net
m.Replicas = types.Int64Value(int64(resp.GetReplicas()))

View file

@ -1,437 +0,0 @@
package sqlserverflexbeta
import (
"bytes"
"context"
"fmt"
"log"
"strings"
"testing"
"text/template"
"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/hashicorp/terraform-plugin-testing/tfversion"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
)
const resourceString = "stackitprivatepreview_sqlserverflexbeta_instance"
var (
validSingleFlavor = "4.16-Single"
kekKeyRingId = ""
kekKeyVersion = ""
kekKeySA = ""
)
func TestMain(m *testing.M) {
resource.TestMain(m)
}
func init() {
resource.AddTestSweepers(resourceString, &resource.Sweeper{
Name: resourceString,
F: func(region string) error {
client, err := sharedClientForRegion(region)
if err != nil {
return fmt.Errorf("error getting client: %s", err)
}
conn := client.(*sqlserverflexbeta.APIClient)
ctx := context.Background()
instances, err := conn.ListInstancesRequest(ctx, testutil.ProjectId, region).Execute()
if err != nil {
return fmt.Errorf("error getting instances: %s", err)
}
for _, instance := range instances.GetInstances() {
if strings.HasPrefix(instance.GetName(), "test-acc") {
err := conn.DeleteInstanceRequestExecute(ctx, testutil.ProjectId, region, instance.GetId())
if err != nil {
log.Printf("error destroying %s during sweep: %s", instance.GetName(), err)
}
}
}
return nil
},
})
}
// sharedClientForRegion returns a common provider client configured for the specified region
func sharedClientForRegion(region string) (any, error) {
providerData := core.ProviderData{}
if region != "" {
providerData.DefaultRegion = region
}
apiClientConfigOptions := []config.ConfigurationOption{
config.WithCustomAuth(providerData.RoundTripper),
utils.UserAgentConfigOption(providerData.Version),
}
if providerData.SQLServerFlexCustomEndpoint != "" {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.SQLServerFlexCustomEndpoint))
} else {
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion()))
}
apiClient, err := sqlserverflexbeta.NewAPIClient(apiClientConfigOptions...)
if err != nil {
return nil, err
}
return apiClient, nil
}
func testAccPreCheck(t *testing.T) {
// TODO: if needed ...
}
type resData struct {
WithEncryption bool
ServiceAccountFilePath string
ProjectID string
Region string
TfName string
Name string
FlavorID string
BackupSchedule string
RetentionDays uint32
Replicas uint32
PerformanceClass string
Size uint32
Acl string
AccessScope string
Version string
KekKeyId string
KekKeyRingId string
KekKeyVersion uint8
KekSaEmail string
}
func getSingleExample() resData {
tmpName := acctest.RandomWithPrefix("tf-acc")
return resData{
WithEncryption: false,
Region: testutil.Region,
ServiceAccountFilePath: testutil.ServiceAccountFile,
ProjectID: testutil.ProjectId,
Name: tmpName,
TfName: tmpName,
FlavorID: validSingleFlavor,
BackupSchedule: "0 0 * * *",
RetentionDays: 33,
PerformanceClass: "premium-perf2-stackit",
Size: 10,
Acl: "0.0.0.0/0",
AccessScope: "PUBLIC",
Version: "2022",
}
}
func TestAccResourceExample_basic(t *testing.T) {
exData := getSingleExample()
t.Logf("[INFO] resource name: %s", exData.TfName)
exBefore := testAccResourceExampleConfig(exData)
updData := exData
// oldName := exData.Name
updData.Name = "newname"
updBefore := testAccResourceExampleConfig(updData)
resName := fmt.Sprintf("%s.%s", resourceString, exData.TfName)
var resourceID string
compareValuesSame := statecheck.CompareValue(compare.ValuesSame())
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_10_0),
},
CheckDestroy: testAccCheckExampleResourceDestroy,
Steps: []resource.TestStep{
// test create
{
Config: exBefore,
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resName, plancheck.ResourceActionCreate),
plancheck.ExpectNonEmptyPlan(),
},
},
ConfigStateChecks: []statecheck.StateCheck{
compareValuesSame.AddStateValue(
resName,
tfjsonpath.New("edition"),
),
statecheck.ExpectKnownValue(
resName,
tfjsonpath.New("edition"),
knownvalue.StringExact("Standard"),
),
//statecheck.ExpectSensitiveValue(resName,
// tfjsonpath.New("sensitive_string_attribute")),
},
Check: resource.ComposeAggregateTestCheckFunc(
func(s *terraform.State) error {
t.Logf("[INFO] resourceID: %+v", resourceID)
return nil
},
testAccGrabResourceID(resName, &resourceID),
func(s *terraform.State) error {
t.Logf("[INFO] resourceID: %s", resourceID)
return nil
},
testCheckResourceExists(resName),
resource.TestCheckResourceAttrSet(resName, "id"),
//resource.TestCheckResourceAttr(resName, "id", resourceID),
resource.TestCheckResourceAttr(resName, "name", exData.Name),
),
},
// up to here we should see no plan drift
{
Config: exBefore,
PlanOnly: true,
ExpectNonEmptyPlan: false,
},
// test update
{
Config: updBefore,
Check: resource.ComposeAggregateTestCheckFunc(
func(s *terraform.State) error {
t.Logf("[INFO] resourceID: %s", resourceID)
return nil
},
testCheckResourceExists(resName),
resource.TestCheckResourceAttrSet(resName, "id"),
//resource.TestCheckResourceAttr(resName, "id", resourceID),
resource.TestCheckResourceAttr(resName, "name", updData.Name),
),
},
// check for plan drift after update
{
Config: exBefore,
PlanOnly: true,
ExpectNonEmptyPlan: false,
},
//// Import test
//{
// ResourceName: resName,
// ImportState: true,
// ImportStateVerify: true,
//},
},
})
}
func testAccCheckExampleResourceDestroy(state *terraform.State) error {
//// retrieve the connection established in Provider configuration
//conn := testAccProvider.Meta().(*ExampleClient)
// loop through the resources in state, verifying each widget
// is destroyed
for _, rs := range state.RootModule().Resources {
if rs.Type != resourceString {
continue
}
fmt.Println(rs.String())
// rs.Primary.ID
//// Retrieve our widget by referencing it's state ID for API lookup
//request := &example.DescribeWidgets{
// IDs: []string{rs.Primary.ID},
//}
//
//response, err := conn.DescribeWidgets(request)
//if err == nil {
// if len(response.Widgets) > 0 && *response.Widgets[0].ID == rs.Primary.ID {
// return fmt.Errorf("Widget (%s) still exists.", rs.Primary.ID)
// }
//
// return nil
//}
//
//// If the error is equivalent to 404 not found, the widget is destroyed.
//// otherwise return the error
//if !strings.Contains(err.Error(), "Widget not found") {
// return err
//}
}
return nil
}
func testAccResourceExampleConfig(data resData) string {
tpl := `
resource "stackitprivatepreview_sqlserverflexbeta_instance" "{{ .TfName }}" {
project_id = "{{ .ProjectID }}"
name = "{{ .Name }}"
backup_schedule = "{{ .BackupSchedule }}"
retention_days = {{ .RetentionDays }}
flavor_id = "{{ .FlavorID }}"
storage = {
class = "{{ .PerformanceClass }}"
size = {{ .Size }}
}
network = {
acl = ["{{ .Acl }}"]
access_scope = "{{ .AccessScope }}"
}
{{ if .WithEncryption }}
encryption = {
kek_key_id = "{{ .KekKeyId }}"
kek_key_ring_id = "{{ .KekKeyRingId }}"
kek_key_version = {{ .KekKeyVersion }}
service_account = "{{ .KekSaEmail }}"
}
{{ end }}
version = "{{ .Version }}"
}
`
tmpl, err := template.New("").Parse(tpl)
if err != nil {
log.Fatalln(err)
}
buff := new(bytes.Buffer)
err = tmpl.Execute(buff, data)
if err != nil {
log.Fatalln(err)
}
res := fmt.Sprintf(`
%[1]s
%[2]s
`,
testutil.PostgresFlexProviderConfig(data.ServiceAccountFilePath),
buff.String(),
)
return res
}
func testCheckResourceExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("resource not found: %s", resourceName)
}
if rs.Primary.ID == "" {
return fmt.Errorf("resource ID not set")
}
// Verify resource exists in the API
//client := testAccProvider.Meta().(*APIClient)
//_, err := client.GetResource(rs.Primary.ID)
//if err != nil {
// return fmt.Errorf("error fetching resource: %w", err)
//}
return nil
}
}
func testAccGrabResourceID(resourceName string, id *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("ressource not found: %s", resourceName)
}
if rs.Primary.ID == "" {
return fmt.Errorf("no ID in state for %s", resourceName)
}
*id = rs.Primary.ID
return nil
}
}
//func setupMockServer() *httptest.Server {
// mux := http.NewServeMux()
//
// mux.HandleFunc("/api/resources", func(w http.ResponseWriter, r *http.Request) {
// switch r.Method {
// case http.MethodPost:
// w.WriteHeader(http.StatusCreated)
// json.NewEncoder(w).Encode(map[string]string{
// "id": "mock-id-123",
// "name": "test-resource",
// })
// case http.MethodGet:
// w.WriteHeader(http.StatusOK)
// json.NewEncoder(w).Encode([]map[string]string{})
// }
// })
//
// return httptest.NewServer(mux)
//}
//
//func TestUnitResourceCreate(t *testing.T) {
// server := setupMockServer()
// defer server.Close()
//
// // Configure provider to use mock server URL
// os.Setenv("API_ENDPOINT", server.URL)
//
// // Run unit tests against mock
//}
// type postgresFlexClientMocked struct {
// returnError bool
// getFlavorsResp *postgresflex.GetFlavorsResponse
// }
//
// func (c *postgresFlexClientMocked) ListFlavorsExecute(_ context.Context, _, _ string) (*postgresflex.GetFlavorsResponse, error) {
// if c.returnError {
// return nil, fmt.Errorf("get flavors failed")
// }
//
// return c.getFlavorsResp, nil
// }
//func TestNewInstanceResource(t *testing.T) {
// exData := resData{
// Region: "eu01",
// ServiceAccountFilePath: sa_file,
// ProjectID: project_id,
// Name: "testRes",
// }
// resource.Test(t, resource.TestCase{
// ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
// Steps: []resource.TestStep{
// {
// Config: testAccResourceEncryptionExampleConfig(exData),
// Check: resource.ComposeAggregateTestCheckFunc(
// resource.TestCheckResourceAttr("example_resource.test", "name", exData.Name),
// resource.TestCheckResourceAttrSet("example_resource.test", "id"),
// ),
// },
// },
// })
//
// //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)
// // }
// // })
// //}
//}

View file

@ -7,8 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
sqlserverflexbeta "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
sqlserverflexbetaResGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/instance/resources_gen"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
)
func mapResponseToModel(
@ -75,63 +74,25 @@ func mapResponseToModel(
return nil
}
// TODO: handle encryption field mapping when API supports it
func handleEncryption(
m *dataSourceModel,
resp *sqlserverflexbeta.GetUserResponse,
) sqlserverflexbetaResGen.EncryptionValue {
/*
if !resp.HasEncryption() ||
resp.Encryption == nil ||
resp.Encryption.KekKeyId == nil ||
resp.Encryption.KekKeyRingId == nil ||
resp.Encryption.KekKeyVersion == nil ||
resp.Encryption.ServiceAccount == nil {
if m.Encryption.IsNull() || m.Encryption.IsUnknown() {
return sqlserverflexbetaResGen.NewEncryptionValueNull()
}
return m.Encryption
}
enc := sqlserverflexbetaResGen.NewEncryptionValueNull()
if kVal, ok := resp.Encryption.GetKekKeyIdOk(); ok {
enc.KekKeyId = types.StringValue(kVal)
}
if kkVal, ok := resp.Encryption.GetKekKeyRingIdOk(); ok {
enc.KekKeyRingId = types.StringValue(kkVal)
}
if kkvVal, ok := resp.Encryption.GetKekKeyVersionOk(); ok {
enc.KekKeyVersion = types.StringValue(kkvVal)
}
if sa, ok := resp.Encryption.GetServiceAccountOk(); ok {
enc.ServiceAccount = types.StringValue(sa)
}
return enc
*/
return sqlserverflexbetaResGen.NewEncryptionValueNull()
}
func toCreatePayload(
ctx context.Context,
model *dataSourceModel,
) (*sqlserverflexbeta.CreateUserRequestPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
var roles []sqlserverflexbeta.UserRole
if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
diags := model.Roles.ElementsAs(ctx, &roles, false)
if diags.HasError() {
return nil, fmt.Errorf("failed to convert roles: %v", diags)
}
}
return &sqlserverflexbeta.CreateUserRequestPayload{
DefaultDatabase: model.DefaultDatabase.ValueStringPointer(),
Username: model.Username.ValueStringPointer(),
Roles: &roles,
}, nil
}
//func toCreatePayload(
// ctx context.Context,
// model *resourceModel,
//) (*sqlserverflexbeta.CreateUserRequestPayload, error) {
// if model == nil {
// return nil, fmt.Errorf("nil model")
// }
//
// var roles []sqlserverflexbeta.UserRole
// if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
// diags := model.Roles.ElementsAs(ctx, &roles, false)
// if diags.HasError() {
// return nil, fmt.Errorf("failed to convert roles: %v", diags)
// }
// }
//
// return &sqlserverflexbeta.CreateUserRequestPayload{
// DefaultDatabase: model.DefaultDatabase.ValueStringPointer(),
// Username: model.Username.ValueStringPointer(),
// Roles: &roles,
// }, nil
//}

View file

@ -158,6 +158,18 @@ func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, r
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "region", region)
//payload, err := toCreatePayload(ctx, &data)
//if err != nil {
// core.LogAndAddError(
// ctx,
// &resp.Diagnostics,
// "Error creating User",
// fmt.Sprintf("Creating API payload: %v", err),
// )
// return
//}
//payload = payload
// TODO: Create API call logic
/*
// Generate API request body from model

View file

@ -21,12 +21,12 @@ import (
"github.com/stackitcloud/stackit-sdk-go/core/config"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/features"
postgresFlexAlphaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/database"
postgresFlexAlphaFlavor "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavor"
postgresflexalphaFlavors "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/flavors"
postgresFlexAlphaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/instance"
postgresFlexAlphaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/postgresflexalpha/user"
sqlserverFlexBetaFlavor "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/flavor"
sqlserverflexalphaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database"
sqlserverFlexAlphaFlavor "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/flavor"
@ -34,6 +34,7 @@ import (
sqlserverFlexAlphaUser "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user"
sqlserverflexBetaDatabase "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/database"
sqlserverFlexBetaFlavor "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/flavor"
sqlserverflexBetaInstance "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/instance"
)

View file

@ -1,5 +1,3 @@
// Copyright (c) STACKIT
package stackit_test
import (
@ -7,17 +5,15 @@ import (
"fmt"
"log/slog"
"os"
"path"
"regexp"
"runtime"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/joho/godotenv"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/internal/testutils"
"github.com/hashicorp/terraform-plugin-testing/config"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/testutil"
)
//go:embed testdata/provider-credentials.tf
@ -29,10 +25,7 @@ var providerInvalidAttribute string
//go:embed testdata/provider-all-attributes.tf
var providerValidAttributes string
var testConfigProviderCredentials = config.Variables{
"project_id": config.StringVariable(testutil.ProjectId),
"name": config.StringVariable(fmt.Sprintf("tf-acc-prov%s", acctest.RandStringFromCharSet(3, acctest.CharSetAlphaNum))),
}
var testConfigProviderCredentials config.Variables
func setup() {
err := godotenv.Load()
@ -41,6 +34,18 @@ func setup() {
return
}
slog.Info("loaded .env file")
testConfigProviderCredentials = config.Variables{
"project_id": config.StringVariable(os.Getenv("TF_ACC_PROJECT_ID")),
"region": config.StringVariable(os.Getenv("TF_ACC_REGION")),
"service_account_key_path": config.StringVariable(os.Getenv("TF_ACC_SERVICE_ACCOUNT_FILE")),
"name": config.StringVariable(
fmt.Sprintf(
"tf-acc-prov%s",
acctest.RandStringFromCharSet(3, acctest.CharSetAlphaNum),
),
),
}
}
func TestMain(m *testing.M) {
@ -50,94 +55,7 @@ func TestMain(m *testing.M) {
os.Exit(code)
}
// Helper function to obtain the home directory on different systems.
// Based on os.UserHomeDir().
func getHomeEnvVariableName() string {
env := "HOME"
switch runtime.GOOS {
case "windows":
env = "USERPROFILE"
case "plan9":
env = "home"
}
return env
}
// create temporary home and initialize the credentials file as well
func createTemporaryHome(createValidCredentialsFile bool, t *testing.T) string {
// create a temporary file
tempHome, err := os.MkdirTemp("", "tempHome")
if err != nil {
t.Fatalf("Failed to create temporary home directory: %v", err)
}
// create credentials file in temp directory
stackitFolder := path.Join(tempHome, ".stackit")
if err := os.Mkdir(stackitFolder, 0o750); err != nil {
t.Fatalf("Failed to create stackit folder: %v", err)
}
filePath := path.Join(stackitFolder, "credentials.json")
file, err := os.Create(filePath)
if err != nil {
t.Fatalf("Failed to create credentials file: %v", err)
}
defer func() {
if err := file.Close(); err != nil {
t.Fatalf("Error while closing the file: %v", err)
}
}()
// Define content, default = invalid token
token := "foo_token"
if createValidCredentialsFile {
token = testutil.GetTestProjectServiceAccountToken("")
}
content := fmt.Sprintf(`
{
"STACKIT_SERVICE_ACCOUNT_TOKEN": "%s"
}`, token)
if _, err = file.WriteString(content); err != nil {
t.Fatalf("Error writing to file: %v", err)
}
return tempHome
}
// Function to overwrite the home folder
func setTemporaryHome(tempHomePath string) {
env := getHomeEnvVariableName()
if err := os.Setenv(env, tempHomePath); err != nil {
fmt.Printf("Error setting temporary home directory %v", err)
}
}
// cleanup the temporary home and reset the environment variable
func cleanupTemporaryHome(tempHomePath string, t *testing.T) {
if err := os.RemoveAll(tempHomePath); err != nil {
t.Fatalf("Error cleaning up temporary folder: %v", err)
}
originalHomeDir, err := os.UserHomeDir()
if err != nil {
t.Fatalf("Failed to restore home directory back to normal: %v", err)
}
// revert back to original home folder
env := getHomeEnvVariableName()
if err := os.Setenv(env, originalHomeDir); err != nil {
fmt.Printf("Error resetting temporary home directory %v", err)
}
}
func getServiceAccountToken() (string, error) {
token, set := os.LookupEnv("TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN")
if !set || token == "" {
return "", fmt.Errorf("Token not set, please set TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_TOKEN to a valid token to perform tests")
}
return token, nil
}
func TestAccEnvVarTokenValid(t *testing.T) {
func TestAccEnvVarServiceAccountPathValid(t *testing.T) {
// Check if acceptance tests should be run
if v := os.Getenv(resource.EnvTfAcc); v == "" {
t.Skipf(
@ -146,20 +64,14 @@ func TestAccEnvVarTokenValid(t *testing.T) {
return
}
token, err := getServiceAccountToken()
if err != nil {
t.Fatalf("Can't get token: %v", err)
}
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", token)
tempHomeFolder := createTemporaryHome(false, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
// t.Setenv("STACKIT_CREDENTIALS_PATH", "")
tempHomeFolder := testutils.CreateTemporaryHome(true, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
PreConfig: func() { setTemporaryHome(tempHomeFolder) },
PreConfig: func() { testutils.SetTemporaryHome(tempHomeFolder) },
ConfigVariables: testConfigProviderCredentials,
Config: providerCredentialConfig,
},
@ -167,16 +79,16 @@ func TestAccEnvVarTokenValid(t *testing.T) {
})
}
func TestAccEnvVarTokenInvalid(t *testing.T) {
func TestAccEnvVarServiceAccountPathInvalid(t *testing.T) {
t.Skip("needs refactoring")
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", "foo")
tempHomeFolder := createTemporaryHome(false, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
tempHomeFolder := testutils.CreateTemporaryHome(false, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
PreConfig: func() { setTemporaryHome(tempHomeFolder) },
PreConfig: func() { testutils.SetTemporaryHome(tempHomeFolder) },
ConfigVariables: testConfigProviderCredentials,
Config: providerCredentialConfig,
ExpectError: regexp.MustCompile(`undefined response type, status code 401`),
@ -186,15 +98,15 @@ func TestAccEnvVarTokenInvalid(t *testing.T) {
}
func TestAccCredentialsFileValid(t *testing.T) {
t.Skip("needs refactoring")
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", "")
tempHomeFolder := createTemporaryHome(true, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
tempHomeFolder := testutils.CreateTemporaryHome(true, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
PreConfig: func() { setTemporaryHome(tempHomeFolder) },
PreConfig: func() { testutils.SetTemporaryHome(tempHomeFolder) },
ConfigVariables: testConfigProviderCredentials,
Config: providerCredentialConfig,
},
@ -203,15 +115,15 @@ func TestAccCredentialsFileValid(t *testing.T) {
}
func TestAccCredentialsFileInvalid(t *testing.T) {
t.Skip("needs refactoring")
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", "")
tempHomeFolder := createTemporaryHome(false, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
tempHomeFolder := testutils.CreateTemporaryHome(false, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
PreConfig: func() { setTemporaryHome(tempHomeFolder) },
PreConfig: func() { testutils.SetTemporaryHome(tempHomeFolder) },
ConfigVariables: testConfigProviderCredentials,
Config: providerCredentialConfig,
ExpectError: regexp.MustCompile(`Jwt is not in(\r\n|\r|\n)the form of Header.Payload.Signature`),
@ -221,6 +133,7 @@ func TestAccCredentialsFileInvalid(t *testing.T) {
}
func TestAccProviderConfigureValidValues(t *testing.T) {
t.Skip("needs refactoring")
// Check if acceptance tests should be run
if v := os.Getenv(resource.EnvTfAcc); v == "" {
t.Skipf(
@ -228,18 +141,11 @@ func TestAccProviderConfigureValidValues(t *testing.T) {
resource.EnvTfAcc)
return
}
// use service account token for these tests
token, err := getServiceAccountToken()
if err != nil {
t.Fatalf("Can't get token: %v", err)
}
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", token)
tempHomeFolder := createTemporaryHome(true, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
tempHomeFolder := testutils.CreateTemporaryHome(true, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{ // valid provider attributes
ConfigVariables: testConfigProviderCredentials,
@ -257,18 +163,12 @@ func TestAccProviderConfigureAnInvalidValue(t *testing.T) {
resource.EnvTfAcc)
return
}
// use service account token for these tests
token, err := getServiceAccountToken()
if err != nil {
t.Fatalf("Can't get token: %v", err)
}
t.Setenv("STACKIT_CREDENTIALS_PATH", "")
t.Setenv("STACKIT_SERVICE_ACCOUNT_TOKEN", token)
tempHomeFolder := createTemporaryHome(true, t)
defer cleanupTemporaryHome(tempHomeFolder, t)
tempHomeFolder := testutils.CreateTemporaryHome(true, t)
defer testutils.CleanupTemporaryHome(tempHomeFolder, t)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
ProtoV6ProviderFactories: testutils.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{ // invalid test attribute should throw an error
ConfigVariables: testConfigProviderCredentials,

View file

@ -1,8 +1,8 @@
variable "project_id" {}
variable "name" {}
variable "region" {}
provider "stackit" {
provider "stackitprivatepreview" {
default_region = "eu01"
credentials_path = "~/.stackit/credentials.json"
service_account_token = ""
@ -36,7 +36,11 @@ provider "stackit" {
enable_beta_resources = "true"
}
resource "stackit_network" "network" {
name = var.name
project_id = var.project_id
data "stackitprivatepreview_postgresflexalpha_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 2
ram = 4
node_type = "Single"
storage_class = "premium-perf2-stackit"
}

View file

@ -1,11 +1,18 @@
variable "project_id" {}
variable "name" {}
variable "region" {}
provider "stackit" {
variable "service_account_key_path" {}
provider "stackitprivatepreview" {
service_account_key_path = var.service_account_key_path
}
resource "stackit_network" "network" {
name = var.name
project_id = var.project_id
}
data "stackitprivatepreview_postgresflexalpha_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 2
ram = 4
node_type = "Single"
storage_class = "premium-perf2-stackit"
}

View file

@ -1,12 +1,16 @@
variable "project_id" {}
variable "name" {}
variable "region" {}
provider "stackit" {
provider "stackitprivatepreview" {
test = "test"
}
resource "stackit_network" "network" {
name = var.name
project_id = var.project_id
}
data "stackitprivatepreview_postgresflexalpha_flavor" "flavor" {
project_id = var.project_id
region = var.region
cpu = 2
ram = 4
node_type = "Single"
storage_class = "premium-perf2-stackit"
}