* Initial PoC for a Project Role Assignment resource Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * fix: move project_role_assignment into new "authorization" resource group Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * feat: add authorization_project_role_assignment acceptance test Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * docs: add authorization_project_role_assignment docs and examples Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * fix: linting Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * feat: add generic role_assignment resources Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * feat: add infrastructure for experimental features Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * feat: Make IAM resources part of the iam experiment Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * fix: Log an error if an experiment does not exist Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> * fix: Do not cache the experiment check Caching the experiment check causes problems when running the provider in debug mode, since configure in the provider can be called multiple times there with different configurations, with different experiments enabled. Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> --------- Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud> Co-authored-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>
473 lines
12 KiB
Go
473 lines
12 KiB
Go
package testutil
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
|
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
|
|
|
"github.com/stackitcloud/terraform-provider-stackit/stackit"
|
|
)
|
|
|
|
const (
|
|
// Default location of credentials JSON
|
|
credentialsFilePath = ".stackit/credentials.json" //nolint:gosec // linter false positive
|
|
)
|
|
|
|
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){
|
|
"stackit": providerserver.NewProtocol6WithError(stackit.New("test-version")()),
|
|
}
|
|
|
|
// 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")
|
|
// ServerId is the id of a server used for some tests
|
|
ServerId = getenv("TF_ACC_SERVER_ID", "")
|
|
// IaaSImageId is the id of an image used for IaaS acceptance tests.
|
|
// Default image is ubuntu 24.04
|
|
IaaSImageId = getenv("TF_ACC_IMAGE_ID", "59838a89-51b1-4892-b57f-b3caf598ee2f")
|
|
// 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")
|
|
// TestProjectParentContainerID 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")
|
|
|
|
ArgusCustomEndpoint = os.Getenv("TF_ACC_ARGUS_CUSTOM_ENDPOINT")
|
|
DnsCustomEndpoint = os.Getenv("TF_ACC_DNS_CUSTOM_ENDPOINT")
|
|
IaaSCustomEndpoint = os.Getenv("TF_ACC_IAAS_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")
|
|
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")
|
|
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")
|
|
SKECustomEndpoint = os.Getenv("TF_ACC_SKE_CUSTOM_ENDPOINT")
|
|
|
|
// OpenStack user domain name
|
|
OSUserDomainName = os.Getenv("TF_ACC_OS_USER_DOMAIN_NAME")
|
|
// OpenStack user name
|
|
OSUserName = os.Getenv("TF_ACC_OS_USER_NAME")
|
|
// OpenStack password
|
|
OSPassword = os.Getenv("TF_ACC_OS_PASSWORD")
|
|
)
|
|
|
|
// Provider config helper functions
|
|
|
|
func ArgusProviderConfig() string {
|
|
if ArgusCustomEndpoint == "" {
|
|
return `provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
argus_custom_endpoint = "%s"
|
|
}`,
|
|
ArgusCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
// Provider config helper functions
|
|
|
|
func ObservabilityProviderConfig() string {
|
|
if ObservabilityCustomEndpoint == "" {
|
|
return `provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
observability_custom_endpoint = "%s"
|
|
}`,
|
|
ObservabilityCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func DnsProviderConfig() string {
|
|
if DnsCustomEndpoint == "" {
|
|
return `provider "stackit" {}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
dns_custom_endpoint = "%s"
|
|
}`,
|
|
DnsCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func IaaSProviderConfig() string {
|
|
if IaaSCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
enable_beta_resources = true
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
iaas_custom_endpoint = "%s"
|
|
}`,
|
|
IaaSCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func LoadBalancerProviderConfig() string {
|
|
if LoadBalancerCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
enable_beta_resources = true
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
loadbalancer_custom_endpoint = "%s"
|
|
}`,
|
|
LoadBalancerCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func LogMeProviderConfig() string {
|
|
if LogMeCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
logme_custom_endpoint = "%s"
|
|
}`,
|
|
LogMeCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func MariaDBProviderConfig() string {
|
|
if MariaDBCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
mariadb_custom_endpoint = "%s"
|
|
}`,
|
|
MariaDBCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func MongoDBFlexProviderConfig() string {
|
|
if MongoDBFlexCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
mongodbflex_custom_endpoint = "%s"
|
|
}`,
|
|
MongoDBFlexCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func ObjectStorageProviderConfig() string {
|
|
if ObjectStorageCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
objectstorage_custom_endpoint = "%s"
|
|
}`,
|
|
ObjectStorageCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func OpenSearchProviderConfig() string {
|
|
if OpenSearchCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
opensearch_custom_endpoint = "%s"
|
|
}`,
|
|
OpenSearchCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func PostgresFlexProviderConfig() string {
|
|
if PostgresFlexCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
postgresflex_custom_endpoint = "%s"
|
|
}`,
|
|
PostgresFlexCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func RabbitMQProviderConfig() string {
|
|
if RabbitMQCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
rabbitmq_custom_endpoint = "%s"
|
|
}`,
|
|
RabbitMQCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func RedisProviderConfig() string {
|
|
if RedisCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
redis_custom_endpoint = "%s"
|
|
}`,
|
|
RedisCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func ResourceManagerProviderConfig() string {
|
|
token := getTestProjectServiceAccountToken("")
|
|
if ResourceManagerCustomEndpoint == "" || AuthorizationCustomEndpoint == "" {
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
service_account_email = "%s"
|
|
service_account_token = "%s"
|
|
}`,
|
|
TestProjectServiceAccountEmail,
|
|
token,
|
|
)
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
resourcemanager_custom_endpoint = "%s"
|
|
authorization_custom_endpoint = "%s"
|
|
service_account_email = "%s"
|
|
service_account_token = "%s"
|
|
}`,
|
|
ResourceManagerCustomEndpoint,
|
|
AuthorizationCustomEndpoint,
|
|
TestProjectServiceAccountEmail,
|
|
token,
|
|
)
|
|
}
|
|
|
|
func SecretsManagerProviderConfig() string {
|
|
if SecretsManagerCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
secretsmanager_custom_endpoint = "%s"
|
|
}`,
|
|
SecretsManagerCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func SQLServerFlexProviderConfig() string {
|
|
if SQLServerFlexCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
sqlserverflex_custom_endpoint = "%s"
|
|
}`,
|
|
SQLServerFlexCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func ServerBackupProviderConfig() string {
|
|
if ServerBackupCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
server_backup_custom_endpoint = "%s"
|
|
}`,
|
|
ServerBackupCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func ServerUpdateProviderConfig() string {
|
|
if ServerUpdateCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
server_update_custom_endpoint = "%s"
|
|
}`,
|
|
ServerUpdateCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func SKEProviderConfig() string {
|
|
if SKECustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
ske_custom_endpoint = "%s"
|
|
}`,
|
|
SKECustomEndpoint,
|
|
)
|
|
}
|
|
|
|
func AuthorizationProviderConfig() string {
|
|
if AuthorizationCustomEndpoint == "" {
|
|
return `
|
|
provider "stackit" {
|
|
region = "eu01"
|
|
experiments = ["iam"]
|
|
}`
|
|
}
|
|
return fmt.Sprintf(`
|
|
provider "stackit" {
|
|
authorization_custom_endpoint = "%s"
|
|
experiments = ["iam"]
|
|
}`,
|
|
AuthorizationCustomEndpoint,
|
|
)
|
|
}
|
|
|
|
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 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 getenv(key, defaultValue string) string {
|
|
val := os.Getenv(key)
|
|
if val == "" {
|
|
return defaultValue
|
|
}
|
|
return val
|
|
}
|
|
|
|
// 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
|
|
}
|