From b9b6aad2ec8116d6a4873b44432e66561f356623 Mon Sep 17 00:00:00 2001 From: Alexander Dahmen Date: Mon, 16 Mar 2026 10:29:57 +0100 Subject: [PATCH 1/2] chore(serverbackup): Improve acceptance tests Signed-off-by: Alexander Dahmen --- .../serverbackup/serverbackup_acc_test.go | 136 ++++++++++++++---- .../serverbackup/testdata/resource-max.tf | 38 ++++- .../serverbackup/testdata/resource-min.tf | 38 ++++- .../serverupdate/serverupdate_acc_test.go | 18 ++- stackit/internal/testutil/testutil.go | 2 +- 5 files changed, 191 insertions(+), 41 deletions(-) diff --git a/stackit/internal/services/serverbackup/serverbackup_acc_test.go b/stackit/internal/services/serverbackup/serverbackup_acc_test.go index 9793291b3..d65ad6442 100644 --- a/stackit/internal/services/serverbackup/serverbackup_acc_test.go +++ b/stackit/internal/services/serverbackup/serverbackup_acc_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" core_config "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" "github.com/stackitcloud/stackit-sdk-go/services/serverbackup" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" @@ -31,23 +32,31 @@ var ( var testConfigVarsMin = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), - "server_id": config.StringVariable(testutil.ServerId), "schedule_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "rrule": config.StringVariable("DTSTART;TZID=Europe/Sofia:20200803T023000 RRULE:FREQ=DAILY;INTERVAL=1"), "enabled": config.BoolVariable(true), "backup_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "retention_period": config.IntegerVariable(14), + "server_name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))), + "network_name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))), + "machine_type": config.StringVariable("t1.1"), + // image needs to contain the STACKIT Server Agent + "image_id": config.StringVariable("fb5b3fa8-5e20-478a-929a-2b7da1676b18"), } var testConfigVarsMax = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), - "server_id": config.StringVariable(testutil.ServerId), "schedule_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "rrule": config.StringVariable("DTSTART;TZID=Europe/Sofia:20200803T023000 RRULE:FREQ=DAILY;INTERVAL=1"), "enabled": config.BoolVariable(true), "backup_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "retention_period": config.IntegerVariable(14), "region": config.StringVariable("eu01"), + "server_name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))), + "network_name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))), + "machine_type": config.StringVariable("t1.1"), + // image needs to contain the STACKIT Server Agent + "image_id": config.StringVariable("fb5b3fa8-5e20-478a-929a-2b7da1676b18"), } func configVarsInvalid(vars config.Variables) config.Variables { @@ -72,13 +81,12 @@ func configVarsMaxUpdated() config.Variables { } func TestAccServerBackupScheduleMinResource(t *testing.T) { - if testutil.ServerId == "" { - fmt.Println("TF_ACC_SERVER_ID not set, skipping test") - return - } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, - CheckDestroy: testAccCheckServerBackupScheduleDestroy, + CheckDestroy: resource.ComposeTestCheckFunc( + testAccCheckServerBackupScheduleDestroy, + testAccCheckServerDestroy, + ), Steps: []resource.TestStep{ // Creation fail { @@ -93,13 +101,15 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Backup schedule data resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), - resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "server_id", testutil.ConvertConfigVariable(testConfigVarsMin["server_id"])), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "backup_schedule_id"), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "id"), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "name", testutil.ConvertConfigVariable(testConfigVarsMin["schedule_name"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "rrule", testutil.ConvertConfigVariable(testConfigVarsMin["rrule"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "enabled", strconv.FormatBool(true)), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.name", testutil.ConvertConfigVariable(testConfigVarsMin["backup_name"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "server_id"), ), }, // data source @@ -109,7 +119,6 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Server backup schedule data resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMin["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedule.schedule_data_test", "backup_schedule_id"), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedule.schedule_data_test", "id"), resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "name", testutil.ConvertConfigVariable(testConfigVarsMin["schedule_name"])), @@ -119,8 +128,8 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { // Server backup schedules data resource.TestCheckResourceAttr("data.stackit_server_backup_schedules.schedules_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_backup_schedules.schedules_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMin["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedules.schedules_data_test", "id"), + resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedules.schedules_data_test", "server_id"), ), }, // Import @@ -136,7 +145,11 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find attribute backup_schedule_id") } - return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, testutil.ServerId, scheduleId), nil + serverId, ok := r.Primary.Attributes["server_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute server_id") + } + return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, serverId, scheduleId), nil }, ImportState: true, ImportStateVerify: true, @@ -148,7 +161,6 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Backup schedule data resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "project_id", testutil.ConvertConfigVariable(configVarsMinUpdated()["project_id"])), - resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "server_id", testutil.ConvertConfigVariable(configVarsMinUpdated()["server_id"])), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "backup_schedule_id"), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "id"), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "name", testutil.ConvertConfigVariable(configVarsMinUpdated()["schedule_name"])), @@ -156,6 +168,9 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(configVarsMinUpdated()["enabled"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.retention_period", testutil.ConvertConfigVariable(configVarsMinUpdated()["retention_period"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.name", testutil.ConvertConfigVariable(configVarsMinUpdated()["backup_name"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "server_id"), ), }, // Deletion is done by the framework implicitly @@ -164,13 +179,12 @@ func TestAccServerBackupScheduleMinResource(t *testing.T) { } func TestAccServerBackupScheduleMaxResource(t *testing.T) { - if testutil.ServerId == "" { - fmt.Println("TF_ACC_SERVER_ID not set, skipping test") - return - } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, - CheckDestroy: testAccCheckServerBackupScheduleDestroy, + CheckDestroy: resource.ComposeTestCheckFunc( + testAccCheckServerBackupScheduleDestroy, + testAccCheckServerDestroy, + ), Steps: []resource.TestStep{ // Creation fail { @@ -185,13 +199,15 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Backup schedule data resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), - resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "server_id", testutil.ConvertConfigVariable(testConfigVarsMax["server_id"])), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "backup_schedule_id"), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "id"), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "name", testutil.ConvertConfigVariable(testConfigVarsMax["schedule_name"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "rrule", testutil.ConvertConfigVariable(testConfigVarsMax["rrule"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "enabled", strconv.FormatBool(true)), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.name", testutil.ConvertConfigVariable(testConfigVarsMax["backup_name"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "server_id"), ), }, // data source @@ -201,7 +217,6 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Server backup schedule data resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMax["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedule.schedule_data_test", "backup_schedule_id"), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedule.schedule_data_test", "id"), resource.TestCheckResourceAttr("data.stackit_server_backup_schedule.schedule_data_test", "name", testutil.ConvertConfigVariable(testConfigVarsMax["schedule_name"])), @@ -211,8 +226,8 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { // Server backup schedules data resource.TestCheckResourceAttr("data.stackit_server_backup_schedules.schedules_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_backup_schedules.schedules_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMax["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedules.schedules_data_test", "id"), + resource.TestCheckResourceAttrSet("data.stackit_server_backup_schedules.schedules_data_test", "server_id"), ), }, // Import @@ -228,7 +243,11 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find attribute backup_schedule_id") } - return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, testutil.ServerId, scheduleId), nil + serverId, ok := r.Primary.Attributes["server_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute server_id") + } + return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, serverId, scheduleId), nil }, ImportState: true, ImportStateVerify: true, @@ -240,7 +259,6 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Backup schedule data resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "project_id", testutil.ConvertConfigVariable(configVarsMaxUpdated()["project_id"])), - resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "server_id", testutil.ConvertConfigVariable(configVarsMaxUpdated()["server_id"])), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "backup_schedule_id"), resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "id"), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "name", testutil.ConvertConfigVariable(configVarsMaxUpdated()["schedule_name"])), @@ -248,6 +266,9 @@ func TestAccServerBackupScheduleMaxResource(t *testing.T) { resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["enabled"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.retention_period", testutil.ConvertConfigVariable(configVarsMaxUpdated()["retention_period"])), resource.TestCheckResourceAttr("stackit_server_backup_schedule.test_schedule", "backup_properties.name", testutil.ConvertConfigVariable(configVarsMaxUpdated()["backup_name"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_backup_schedule.test_schedule", "server_id"), ), }, // Deletion is done by the framework implicitly @@ -267,7 +288,20 @@ func testAccCheckServerBackupScheduleDestroy(s *terraform.State) error { ) } if err != nil { - return fmt.Errorf("creating client: %w", err) + return fmt.Errorf("creating serverbackup client: %w", err) + } + + var serverId string + for _, rs := range s.RootModule().Resources { + if rs.Type == "stackit_server" { + // server terraform ID: "[project_id],[region],[server_id]" + serverId = strings.Split(rs.Primary.ID, core.Separator)[2] + break + } + } + + if serverId == "" { + return fmt.Errorf("could not find server ID in state") } schedulesToDestroy := []string{} @@ -280,8 +314,13 @@ func testAccCheckServerBackupScheduleDestroy(s *terraform.State) error { schedulesToDestroy = append(schedulesToDestroy, scheduleId) } - schedulesResp, err := client.ListBackupSchedules(ctx, testutil.ProjectId, testutil.ServerId, testutil.Region).Execute() + schedulesResp, err := client.ListBackupSchedules(ctx, testutil.ProjectId, serverId, testutil.Region).Execute() + // The destroy functions are called after all resources are cleaned up. + // If the server was successfully destroyed we should see a 404 here. if err != nil { + if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "Server not found") { + return nil + } return fmt.Errorf("getting schedulesResp: %w", err) } @@ -292,7 +331,7 @@ func testAccCheckServerBackupScheduleDestroy(s *terraform.State) error { } scheduleId := strconv.FormatInt(*schedules[i].Id, 10) if utils.Contains(schedulesToDestroy, scheduleId) { - err := client.DeleteBackupScheduleExecute(ctx, testutil.ProjectId, testutil.ServerId, scheduleId, testutil.Region) + err := client.DeleteBackupScheduleExecute(ctx, testutil.ProjectId, serverId, scheduleId, testutil.Region) if err != nil { return fmt.Errorf("destroying server backup schedule %s during CheckDestroy: %w", scheduleId, err) } @@ -300,3 +339,50 @@ func testAccCheckServerBackupScheduleDestroy(s *terraform.State) error { } return nil } + +// Additional function to check if the server was deleted if something went wrong in the first case. +func testAccCheckServerDestroy(s *terraform.State) error { + ctx := context.Background() + var client *iaas.APIClient + var err error + + if testutil.IaaSCustomEndpoint == "" { + client, err = iaas.NewAPIClient() + } else { + client, err = iaas.NewAPIClient( + core_config.WithEndpoint(testutil.ServerBackupCustomEndpoint), + ) + } + if err != nil { + return fmt.Errorf("creating client: %w", err) + } + + serversToDestroy := []string{} + for _, rs := range s.RootModule().Resources { + if rs.Type != "stackit_server" { + continue + } + // server terraform ID: "[project_id],[region],[server_id]" + serverId := strings.Split(rs.Primary.ID, core.Separator)[2] + serversToDestroy = append(serversToDestroy, serverId) + } + + serversResp, err := client.ListServersExecute(ctx, testutil.ProjectId, testutil.Region) + if err != nil { + return fmt.Errorf("getting serversResp: %w", err) + } + + servers := *serversResp.Items + for i := range servers { + if servers[i].Id == nil { + continue + } + if utils.Contains(serversToDestroy, *servers[i].Id) { + err := client.DeleteServerExecute(ctx, testutil.ProjectId, testutil.Region, *servers[i].Id) + if err != nil { + return fmt.Errorf("destroying server %s during CheckDestroy: %w", *servers[i].Id, err) + } + } + } + return nil +} diff --git a/stackit/internal/services/serverbackup/testdata/resource-max.tf b/stackit/internal/services/serverbackup/testdata/resource-max.tf index 802b73b71..f41159a93 100644 --- a/stackit/internal/services/serverbackup/testdata/resource-max.tf +++ b/stackit/internal/services/serverbackup/testdata/resource-max.tf @@ -1,5 +1,4 @@ variable "project_id" {} -variable "server_id" {} variable "schedule_name" {} variable "rrule" {} variable "enabled" {} @@ -7,10 +6,41 @@ variable "backup_name" {} variable "retention_period" {} variable "region" {} +# server +variable "server_name" {} +variable "network_name" {} +variable "machine_type" {} +variable "image_id" {} + +# create server +resource "stackit_network" "network" { + project_id = var.project_id + name = var.network_name +} + +resource "stackit_network_interface" "nic" { + project_id = var.project_id + network_id = stackit_network.network.network_id +} + +resource "stackit_server" "server" { + project_id = var.project_id + name = var.server_name + machine_type = var.machine_type + boot_volume = { + source_type = "image" + size = 16 + source_id = var.image_id + delete_on_termination = true + } + network_interfaces = [ + stackit_network_interface.nic.network_interface_id + ] +} resource "stackit_server_backup_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id name = var.schedule_name rrule = var.rrule enabled = var.enabled @@ -24,11 +54,11 @@ resource "stackit_server_backup_schedule" "test_schedule" { data "stackit_server_backup_schedule" "schedule_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id backup_schedule_id = stackit_server_backup_schedule.test_schedule.backup_schedule_id } data "stackit_server_backup_schedules" "schedules_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id } diff --git a/stackit/internal/services/serverbackup/testdata/resource-min.tf b/stackit/internal/services/serverbackup/testdata/resource-min.tf index 5cdf30376..666aa0c5f 100644 --- a/stackit/internal/services/serverbackup/testdata/resource-min.tf +++ b/stackit/internal/services/serverbackup/testdata/resource-min.tf @@ -1,15 +1,45 @@ variable "project_id" {} -variable "server_id" {} variable "schedule_name" {} variable "rrule" {} variable "enabled" {} variable "backup_name" {} variable "retention_period" {} +# server +variable "server_name" {} +variable "network_name" {} +variable "machine_type" {} +variable "image_id" {} + +# create server +resource "stackit_network" "network" { + project_id = var.project_id + name = var.network_name +} + +resource "stackit_network_interface" "nic" { + project_id = var.project_id + network_id = stackit_network.network.network_id +} + +resource "stackit_server" "server" { + project_id = var.project_id + name = var.server_name + machine_type = var.machine_type + boot_volume = { + source_type = "image" + size = 16 + source_id = var.image_id + delete_on_termination = true + } + network_interfaces = [ + stackit_network_interface.nic.network_interface_id + ] +} resource "stackit_server_backup_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id name = var.schedule_name rrule = var.rrule enabled = var.enabled @@ -22,11 +52,11 @@ resource "stackit_server_backup_schedule" "test_schedule" { data "stackit_server_backup_schedule" "schedule_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id backup_schedule_id = stackit_server_backup_schedule.test_schedule.backup_schedule_id } data "stackit_server_backup_schedules" "schedules_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id } diff --git a/stackit/internal/services/serverupdate/serverupdate_acc_test.go b/stackit/internal/services/serverupdate/serverupdate_acc_test.go index 3d45a70a1..a564a002e 100644 --- a/stackit/internal/services/serverupdate/serverupdate_acc_test.go +++ b/stackit/internal/services/serverupdate/serverupdate_acc_test.go @@ -37,7 +37,7 @@ var testConfigVarsMin = config.Variables{ "rrule": config.StringVariable("DTSTART;TZID=Europe/Sofia:20200803T023000 RRULE:FREQ=DAILY;INTERVAL=1"), "enabled": config.BoolVariable(true), "maintenance_window": config.IntegerVariable(1), - "server_id": config.StringVariable(testutil.ServerId), + //"server_id": config.StringVariable(testutil.ServerId), } var testConfigVarsMax = config.Variables{ @@ -48,7 +48,7 @@ var testConfigVarsMax = config.Variables{ "enabled": config.BoolVariable(true), "maintenance_window": config.IntegerVariable(1), "region": config.StringVariable("eu01"), - "server_id": config.StringVariable(testutil.ServerId), + //"server_id": config.StringVariable(testutil.ServerId), } func configVarsInvalid(vars config.Variables) config.Variables { @@ -73,10 +73,10 @@ func configVarsMaxUpdated() config.Variables { } func TestAccServerUpdateScheduleMinResource(t *testing.T) { - if testutil.ServerId == "" { + /*if testutil.ServerId == "" { fmt.Println("TF_ACC_SERVER_ID not set, skipping test") return - } + }*/ resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, CheckDestroy: testAccCheckServerUpdateScheduleDestroy, @@ -160,10 +160,10 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { } func TestAccServerUpdateScheduleMaxResource(t *testing.T) { - if testutil.ServerId == "" { + /*if testutil.ServerId == "" { fmt.Println("TF_ACC_SERVER_ID not set, skipping test") return - } + }*/ resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, CheckDestroy: testAccCheckServerUpdateScheduleDestroy, @@ -222,7 +222,11 @@ func TestAccServerUpdateScheduleMaxResource(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find attribute update_schedule_id") } - return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, testutil.ServerId, scheduleId), nil + serverId, ok := r.Primary.Attributes["server_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute server_id") + } + return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, serverId, scheduleId), nil }, ImportState: true, ImportStateVerify: true, diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index 4df640a00..1a8fd731c 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -52,7 +52,7 @@ var ( 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", "") + ServerId = getenv("TF_ACC_SERVER_ID", "") // TODO: ALEX REMOVE // 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 From d5c19e6b240c6f0434ad11bf9f513e43fb8f18ad Mon Sep 17 00:00:00 2001 From: Alexander Dahmen Date: Tue, 17 Mar 2026 14:51:35 +0100 Subject: [PATCH 2/2] chore(serverupdate): Improve acceptance tests Signed-off-by: Alexander Dahmen --- README.md | 3 +- .../serverupdate/serverupdate_acc_test.go | 129 +++++++++++++++--- .../serverupdate/testdata/resource-max.tf | 40 +++++- .../serverupdate/testdata/resource-min.tf | 41 +++++- stackit/internal/testutil/testutil.go | 2 - 5 files changed, 181 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 1da34359e..36de53f4f 100644 --- a/README.md +++ b/README.md @@ -225,8 +225,7 @@ Additionally: | Env var | Value | Example value | needed for Acc tests of the following services | |---------------------------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------------|------------------------------------------------| | `TF_ACC_ORGANIZATION_ID` | ID of the STACKIT test organization | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `authorization`, `iaas` | -| `TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL` | Email of the STACKIT service account | `abc-serviceaccount@sa.stackit.cloud` | `authorization`, `resourcemanager` | -| `TF_ACC_SERVER_ID` | ID of a STACKIT Server with STACKIT Server Agent enabled | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `serverbackup`, `serverupdate` | +| `TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL` | Email of the STACKIT service account | `abc-serviceaccount@sa.stackit.cloud` | `authorization`, `resourcemanager` | | | `TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID` | Container ID of the project parent container (folder within an organization or the organization itself) | `organization-d2b7087` | `resourcemanager` | | `TF_ACC_TEST_PROJECT_PARENT_UUID` | UUID ID of the project parent container (folder within an organization or the organization itself) | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `resourcemanager` | diff --git a/stackit/internal/services/serverupdate/serverupdate_acc_test.go b/stackit/internal/services/serverupdate/serverupdate_acc_test.go index a564a002e..0655e87ee 100644 --- a/stackit/internal/services/serverupdate/serverupdate_acc_test.go +++ b/stackit/internal/services/serverupdate/serverupdate_acc_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" core_config "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/iaas" "github.com/stackitcloud/stackit-sdk-go/services/serverupdate" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" @@ -32,23 +33,29 @@ var ( var testConfigVarsMin = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), - "server_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "schedule_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "rrule": config.StringVariable("DTSTART;TZID=Europe/Sofia:20200803T023000 RRULE:FREQ=DAILY;INTERVAL=1"), "enabled": config.BoolVariable(true), "maintenance_window": config.IntegerVariable(1), - //"server_id": config.StringVariable(testutil.ServerId), + "server_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlphaNum)), + "network_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlphaNum)), + "machine_type": config.StringVariable("t1.1"), + // image needs to contain the STACKIT Server Agent + "image_id": config.StringVariable("fb5b3fa8-5e20-478a-929a-2b7da1676b18"), } var testConfigVarsMax = config.Variables{ "project_id": config.StringVariable(testutil.ProjectId), - "server_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "schedule_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), "rrule": config.StringVariable("DTSTART;TZID=Europe/Sofia:20200803T023000 RRULE:FREQ=DAILY;INTERVAL=1"), "enabled": config.BoolVariable(true), "maintenance_window": config.IntegerVariable(1), "region": config.StringVariable("eu01"), - //"server_id": config.StringVariable(testutil.ServerId), + "server_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlphaNum)), + "network_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlphaNum)), + "machine_type": config.StringVariable("t1.1"), + // image needs to contain the STACKIT Server Agent + "image_id": config.StringVariable("fb5b3fa8-5e20-478a-929a-2b7da1676b18"), } func configVarsInvalid(vars config.Variables) config.Variables { @@ -73,13 +80,12 @@ func configVarsMaxUpdated() config.Variables { } func TestAccServerUpdateScheduleMinResource(t *testing.T) { - /*if testutil.ServerId == "" { - fmt.Println("TF_ACC_SERVER_ID not set, skipping test") - return - }*/ resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, - CheckDestroy: testAccCheckServerUpdateScheduleDestroy, + CheckDestroy: resource.ComposeTestCheckFunc( + testAccCheckServerUpdateScheduleDestroy, + testAccCheckServerDestroy, + ), Steps: []resource.TestStep{ // Creation fail { @@ -94,12 +100,14 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( // Update schedule data resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), - resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "server_id"), resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "update_schedule_id"), resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "id"), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "name", testutil.ConvertConfigVariable(testConfigVarsMin["schedule_name"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "rrule", testutil.ConvertConfigVariable(testConfigVarsMin["rrule"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(testConfigVarsMin["enabled"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "server_id"), ), }, // data source @@ -117,8 +125,10 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { // Server update schedules data resource.TestCheckResourceAttr("data.stackit_server_update_schedules.schedules_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_update_schedules.schedules_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMin["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_update_schedules.schedules_data_test", "id"), + + // server + resource.TestCheckResourceAttrSet("data.stackit_server_update_schedules.schedules_data_test", "server_id"), ), }, // Import @@ -134,7 +144,11 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { if !ok { return "", fmt.Errorf("couldn't find attribute update_schedule_id") } - return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, testutil.ServerId, scheduleId), nil + serverId, ok := r.Primary.Attributes["server_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute server_id") + } + return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, serverId, scheduleId), nil }, ImportState: true, ImportStateVerify: true, @@ -152,6 +166,9 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "rrule", testutil.ConvertConfigVariable(configVarsMinUpdated()["rrule"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(configVarsMinUpdated()["enabled"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "maintenance_window", testutil.ConvertConfigVariable(configVarsMinUpdated()["maintenance_window"])), + + // server + resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "server_id"), ), }, // Deletion is done by the framework implicitly @@ -160,13 +177,12 @@ func TestAccServerUpdateScheduleMinResource(t *testing.T) { } func TestAccServerUpdateScheduleMaxResource(t *testing.T) { - /*if testutil.ServerId == "" { - fmt.Println("TF_ACC_SERVER_ID not set, skipping test") - return - }*/ resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, - CheckDestroy: testAccCheckServerUpdateScheduleDestroy, + CheckDestroy: resource.ComposeTestCheckFunc( + testAccCheckServerUpdateScheduleDestroy, + testAccCheckServerDestroy, + ), Steps: []resource.TestStep{ // Creation fail { @@ -188,6 +204,9 @@ func TestAccServerUpdateScheduleMaxResource(t *testing.T) { resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "rrule", testutil.ConvertConfigVariable(testConfigVarsMax["rrule"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(testConfigVarsMax["enabled"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "region", testutil.Region), + + // server + resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "server_id"), ), }, // data source @@ -205,8 +224,10 @@ func TestAccServerUpdateScheduleMaxResource(t *testing.T) { // Server update schedules data resource.TestCheckResourceAttr("data.stackit_server_update_schedules.schedules_data_test", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), - resource.TestCheckResourceAttr("data.stackit_server_update_schedules.schedules_data_test", "server_id", testutil.ConvertConfigVariable(testConfigVarsMax["server_id"])), resource.TestCheckResourceAttrSet("data.stackit_server_update_schedules.schedules_data_test", "id"), + + // server + resource.TestCheckResourceAttrSet("data.stackit_server_update_schedules.schedules_data_test", "server_id"), ), }, // Import @@ -244,6 +265,9 @@ func TestAccServerUpdateScheduleMaxResource(t *testing.T) { resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "enabled", testutil.ConvertConfigVariable(configVarsMinUpdated()["enabled"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "maintenance_window", testutil.ConvertConfigVariable(configVarsMinUpdated()["maintenance_window"])), resource.TestCheckResourceAttr("stackit_server_update_schedule.test_schedule", "region", testutil.Region), + + // server + resource.TestCheckResourceAttrSet("stackit_server_update_schedule.test_schedule", "server_id"), ), }, // Deletion is done by the framework implicitly @@ -274,6 +298,19 @@ func deleteSchedule(ctx context.Context, s *terraform.State) error { return fmt.Errorf("creating client: %w", err) } + var serverId string + for _, rs := range s.RootModule().Resources { + if rs.Type == "stackit_server" { + // server terraform ID: "[project_id],[region],[server_id]" + serverId = strings.Split(rs.Primary.ID, core.Separator)[2] + break + } + } + + if serverId == "" { + return fmt.Errorf("could not find server ID in state") + } + schedulesToDestroy := []string{} for _, rs := range s.RootModule().Resources { if rs.Type != "stackit_server_update_schedule" { @@ -284,8 +321,13 @@ func deleteSchedule(ctx context.Context, s *terraform.State) error { schedulesToDestroy = append(schedulesToDestroy, scheduleId) } - schedulesResp, err := client.ListUpdateSchedules(ctx, testutil.ProjectId, testutil.ServerId, testutil.Region).Execute() + schedulesResp, err := client.ListUpdateSchedules(ctx, testutil.ProjectId, serverId, testutil.Region).Execute() + // The destroy functions are called after all resources are cleaned up. + // If the server was successfully destroyed we should see a 404 here. if err != nil { + if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "Server not found") { + return nil + } return fmt.Errorf("getting schedulesResp: %w", err) } @@ -296,7 +338,7 @@ func deleteSchedule(ctx context.Context, s *terraform.State) error { } scheduleId := strconv.FormatInt(*schedules[i].Id, 10) if utils.Contains(schedulesToDestroy, scheduleId) { - err := client.DeleteUpdateScheduleExecute(ctx, testutil.ProjectId, testutil.ServerId, scheduleId, testutil.Region) + err := client.DeleteUpdateScheduleExecute(ctx, testutil.ProjectId, serverId, scheduleId, testutil.Region) if err != nil { return fmt.Errorf("destroying server update schedule %s during CheckDestroy: %w", scheduleId, err) } @@ -304,3 +346,50 @@ func deleteSchedule(ctx context.Context, s *terraform.State) error { } return nil } + +// Additional function to check if the server was deleted if something went wrong in the first case. +func testAccCheckServerDestroy(s *terraform.State) error { + ctx := context.Background() + var client *iaas.APIClient + var err error + + if testutil.IaaSCustomEndpoint == "" { + client, err = iaas.NewAPIClient() + } else { + client, err = iaas.NewAPIClient( + core_config.WithEndpoint(testutil.ServerBackupCustomEndpoint), + ) + } + if err != nil { + return fmt.Errorf("creating client: %w", err) + } + + serversToDestroy := []string{} + for _, rs := range s.RootModule().Resources { + if rs.Type != "stackit_server" { + continue + } + // server terraform ID: "[project_id],[region],[server_id]" + serverId := strings.Split(rs.Primary.ID, core.Separator)[2] + serversToDestroy = append(serversToDestroy, serverId) + } + + serversResp, err := client.ListServersExecute(ctx, testutil.ProjectId, testutil.Region) + if err != nil { + return fmt.Errorf("getting serversResp: %w", err) + } + + servers := *serversResp.Items + for i := range servers { + if servers[i].Id == nil { + continue + } + if utils.Contains(serversToDestroy, *servers[i].Id) { + err := client.DeleteServerExecute(ctx, testutil.ProjectId, testutil.Region, *servers[i].Id) + if err != nil { + return fmt.Errorf("destroying server %s during CheckDestroy: %w", *servers[i].Id, err) + } + } + } + return nil +} diff --git a/stackit/internal/services/serverupdate/testdata/resource-max.tf b/stackit/internal/services/serverupdate/testdata/resource-max.tf index 631c4fd87..da5531321 100644 --- a/stackit/internal/services/serverupdate/testdata/resource-max.tf +++ b/stackit/internal/services/serverupdate/testdata/resource-max.tf @@ -1,15 +1,45 @@ variable "project_id" {} -variable "server_name" {} variable "schedule_name" {} variable "rrule" {} variable "enabled" {} variable "maintenance_window" {} -variable "server_id" {} variable "region" {} +# server +variable "server_name" {} +variable "network_name" {} +variable "machine_type" {} +variable "image_id" {} + +# create server +resource "stackit_network" "network" { + project_id = var.project_id + name = var.network_name +} + +resource "stackit_network_interface" "nic" { + project_id = var.project_id + network_id = stackit_network.network.network_id +} + +resource "stackit_server" "server" { + project_id = var.project_id + name = var.server_name + machine_type = var.machine_type + boot_volume = { + source_type = "image" + size = 16 + source_id = var.image_id + delete_on_termination = true + } + network_interfaces = [ + stackit_network_interface.nic.network_interface_id + ] +} + resource "stackit_server_update_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id name = var.schedule_name rrule = var.rrule enabled = var.enabled @@ -19,11 +49,11 @@ resource "stackit_server_update_schedule" "test_schedule" { data "stackit_server_update_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id update_schedule_id = stackit_server_update_schedule.test_schedule.update_schedule_id } data "stackit_server_update_schedules" "schedules_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id } diff --git a/stackit/internal/services/serverupdate/testdata/resource-min.tf b/stackit/internal/services/serverupdate/testdata/resource-min.tf index f2fd7a3bd..1e1129279 100644 --- a/stackit/internal/services/serverupdate/testdata/resource-min.tf +++ b/stackit/internal/services/serverupdate/testdata/resource-min.tf @@ -1,14 +1,45 @@ variable "project_id" {} -variable "server_name" {} variable "schedule_name" {} variable "rrule" {} variable "enabled" {} variable "maintenance_window" {} -variable "server_id" {} + +# server +variable "server_name" {} +variable "network_name" {} +variable "machine_type" {} +variable "image_id" {} + +# create server +resource "stackit_network" "network" { + project_id = var.project_id + name = var.network_name +} + +resource "stackit_network_interface" "nic" { + project_id = var.project_id + network_id = stackit_network.network.network_id +} + +resource "stackit_server" "server" { + project_id = var.project_id + name = var.server_name + machine_type = var.machine_type + boot_volume = { + source_type = "image" + size = 16 + source_id = var.image_id + delete_on_termination = true + } + network_interfaces = [ + stackit_network_interface.nic.network_interface_id + ] +} + resource "stackit_server_update_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id name = var.schedule_name rrule = var.rrule enabled = var.enabled @@ -17,11 +48,11 @@ resource "stackit_server_update_schedule" "test_schedule" { data "stackit_server_update_schedule" "test_schedule" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id update_schedule_id = stackit_server_update_schedule.test_schedule.update_schedule_id } data "stackit_server_update_schedules" "schedules_data_test" { project_id = var.project_id - server_id = var.server_id + server_id = stackit_server.server.server_id } diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index 1a8fd731c..36c083a04 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -51,8 +51,6 @@ var ( // 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", "") // TODO: ALEX REMOVE // 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