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) }