From 8cb63b3d22bbf59bc7f0b538258556e7e8d2e2a4 Mon Sep 17 00:00:00 2001 From: Timo Riski Date: Mon, 28 Oct 2024 14:12:50 +0200 Subject: [PATCH] feat: forbid using `additional_disk_space` if `autoscaler` integration is enabled Autoscaler and additional disk space conflict with each other. If the service hits its storage threshold, autoscaler integration will bump the service disk space. This would invalidate `additional_disk_space` set on the service resource so we disallow setting the field if autoscaler integration is enabled. Additionally adds tests for autoscaler integration. --- internal/schemautil/service.go | 28 ++++--- .../service_integration_test.go | 73 ++++++++++++++++++- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/internal/schemautil/service.go b/internal/schemautil/service.go index a920a5983..8d043efb2 100644 --- a/internal/schemautil/service.go +++ b/internal/schemautil/service.go @@ -56,6 +56,8 @@ const ( ServiceTypeValkey = "valkey" ) +var ErrAutoscalerConflict = errors.New("autoscaler integration is enabled, additional_disk_space cannot be set") + var TechEmailsResourceSchema = &schema.Resource{ Schema: map[string]*schema.Schema{ "email": { @@ -514,6 +516,17 @@ func ResourceServiceUpdate(ctx context.Context, d *schema.ResourceData, m interf return diag.FromErr(err) } + // Note: Only service_integrations are needed here, but no specific API exists yet. + s, err := avnGen.ServiceGet(ctx, projectName, serviceName, service.ServiceGetIncludeSecrets(true)) + if err != nil { + return diag.Errorf("unable to GET service %s: %s", d.Id(), err) + } + + autoscalerIntegrations := getIntegrationsForTerraform(s.ServiceIntegrations, service.IntegrationTypeAutoscaler) + if _, ok := d.GetOk("additional_disk_space"); ok && len(autoscalerIntegrations) > 0 { + return diag.FromErr(ErrAutoscalerConflict) + } + cloud := d.Get("cloud_name").(string) plan := d.Get("plan").(string) powered := true @@ -604,18 +617,18 @@ func getTechnicalEmailsForTerraform(s *service.ServiceGetOut) *schema.Set { return schema.NewSet(schema.HashResource(TechEmailsResourceSchema), techEmails) } -func getReadReplicaIntegrationsForTerraform(integrations []service.ServiceIntegrationOut) ([]map[string]interface{}, error) { - var readReplicaIntegrations []map[string]interface{} +func getIntegrationsForTerraform(integrations []service.ServiceIntegrationOut, integrationType service.IntegrationType) []map[string]interface{} { + var filteredIntegrations []map[string]interface{} for _, integration := range integrations { - if integration.IntegrationType == "read_replica" { + if integration.IntegrationType == integrationType { integrationMap := map[string]interface{}{ "integration_type": integration.IntegrationType, "source_service_name": integration.SourceService, } - readReplicaIntegrations = append(readReplicaIntegrations, integrationMap) + filteredIntegrations = append(filteredIntegrations, integrationMap) } } - return readReplicaIntegrations, nil + return filteredIntegrations } func ResourceServiceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -781,10 +794,7 @@ func copyServicePropertiesFromAPIResponseToTerraform( } // Handle read_replica service integrations - readReplicaIntegrations, err := getReadReplicaIntegrationsForTerraform(s.ServiceIntegrations) - if err != nil { - return err - } + readReplicaIntegrations := getIntegrationsForTerraform(s.ServiceIntegrations, service.IntegrationTypeReadReplica) if err := d.Set("service_integrations", readReplicaIntegrations); err != nil { return err } diff --git a/internal/sdkprovider/service/serviceintegration/service_integration_test.go b/internal/sdkprovider/service/serviceintegration/service_integration_test.go index 120992219..a52deb6fa 100644 --- a/internal/sdkprovider/service/serviceintegration/service_integration_test.go +++ b/internal/sdkprovider/service/serviceintegration/service_integration_test.go @@ -455,7 +455,6 @@ resource "aiven_service_integration" "bar" { cluster_alias = "source" } }` - } func testAccCheckAivenServiceIntegrationResourceDestroy(s *terraform.State) error { @@ -690,3 +689,75 @@ func TestAccAivenServiceIntegration_clickhouse_postgres_user_config_creates(t *t }, }) } + +func testAccServiceIntegrationAutoscaler(prefix string, includeDiskSpace bool) string { + additionalDiskSpace := "" + if includeDiskSpace { + additionalDiskSpace = `additional_disk_space = "30GiB"` + } + + return fmt.Sprintf(` +data "aiven_project" "project" { + project = %[1]q +} + +resource "aiven_pg" "test_pg" { + project = data.aiven_project.project.project + cloud_name = "google-europe-north1" + service_name = "%[2]s-pg" + plan = "startup-4" + %[3]s +} + +resource "aiven_service_integration_endpoint" "test_endpoint" { + project = data.aiven_project.project.project + endpoint_name = "%[2]s-autoscaler" + endpoint_type = "autoscaler" + + autoscaler_user_config { + autoscaling { + cap_gb = 200 + type = "autoscale_disk" + } + } +} + +resource "aiven_service_integration" "test_autoscaler" { + project = data.aiven_project.project.project + integration_type = "autoscaler" + source_service_name = aiven_pg.test_pg.service_name + destination_endpoint_id = aiven_service_integration_endpoint.test_endpoint.id +} +`, os.Getenv("AIVEN_PROJECT_NAME"), prefix, additionalDiskSpace) +} + +func TestAccAivenServiceIntegration_autoscaler(t *testing.T) { + project := os.Getenv("AIVEN_PROJECT_NAME") + prefix := "test-acc-" + acctest.RandString(7) + resourceName := "aiven_service_integration.test_autoscaler" + endpointResourceName := "aiven_service_integration_endpoint.test_endpoint" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { /* Add necessary pre-checks here */ }, + ProtoV6ProviderFactories: acc.TestProtoV6ProviderFactories, + CheckDestroy: testAccCheckAivenServiceIntegrationResourceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccServiceIntegrationAutoscaler(prefix, false), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "integration_type", "autoscaler"), + resource.TestCheckResourceAttr(resourceName, "project", project), + resource.TestCheckResourceAttr(resourceName, "source_service_name", fmt.Sprintf("%s-pg", prefix)), + resource.TestCheckResourceAttrSet(resourceName, "destination_endpoint_id"), + resource.TestCheckResourceAttr(endpointResourceName, "project", project), + resource.TestCheckResourceAttr(endpointResourceName, "endpoint_name", fmt.Sprintf("%s-autoscaler", prefix)), + resource.TestCheckResourceAttr(endpointResourceName, "endpoint_type", "autoscaler"), + resource.TestCheckResourceAttr(endpointResourceName, "autoscaler_user_config.0.autoscaling.0.cap_gb", "200"), + ), + }, + { + Config: testAccServiceIntegrationAutoscaler(prefix, true), + ExpectError: regexp.MustCompile(schemautil.ErrAutoscalerConflict.Error()), + }, + }, + }) +}