From 9914c694f5ef56a55c49411fb3cf87e97b1f5e58 Mon Sep 17 00:00:00 2001 From: Murad Biashimov Date: Wed, 4 Dec 2024 17:43:31 +0100 Subject: [PATCH] feat: add disaster_recovery integration type --- CHANGELOG.md | 4 ++ docs/resources/cassandra.md | 2 +- docs/resources/clickhouse.md | 4 +- docs/resources/dragonfly.md | 2 +- docs/resources/flink.md | 2 +- docs/resources/grafana.md | 2 +- docs/resources/kafka.md | 2 +- docs/resources/kafka_connect.md | 2 +- docs/resources/kafka_mirrormaker.md | 2 +- docs/resources/m3aggregator.md | 2 +- docs/resources/m3db.md | 2 +- docs/resources/mysql.md | 2 +- docs/resources/opensearch.md | 2 +- docs/resources/pg.md | 2 +- docs/resources/redis.md | 2 +- docs/resources/thanos.md | 2 +- docs/resources/valkey.md | 2 +- internal/acctest/acctest.go | 2 +- internal/schemautil/service.go | 54 +++++++++++++++------ internal/schemautil/wait.go | 10 +++- internal/sdkprovider/service/pg/pg_test.go | 55 ++++++++++++++++++++++ 21 files changed, 124 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec196ec8..2aa4d9643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ nav_order: 1 +## [MAJOR.MINOR.PATCH] - YYYY-MM-DD --> + +- Add `disaster_recovery` service integration type support + ## [4.30.0] - 2024-12-05 - Add `aiven_kafka_native_acl` resource diff --git a/docs/resources/cassandra.md b/docs/resources/cassandra.md index 7bef47bd1..4a1795096 100644 --- a/docs/resources/cassandra.md +++ b/docs/resources/cassandra.md @@ -149,7 +149,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/clickhouse.md b/docs/resources/clickhouse.md index 1f75e64da..cd22b02f5 100644 --- a/docs/resources/clickhouse.md +++ b/docs/resources/clickhouse.md @@ -142,8 +142,8 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. Supported integrations are `clickhouse_kafka` and `clickhouse_postgresql`. -- `source_service_name` (String) Name of the source service. +- `integration_type` (String) Type of the service integration +- `source_service_name` (String) Name of the source service diff --git a/docs/resources/dragonfly.md b/docs/resources/dragonfly.md index b58dc8eea..b838c2bc8 100644 --- a/docs/resources/dragonfly.md +++ b/docs/resources/dragonfly.md @@ -166,7 +166,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/flink.md b/docs/resources/flink.md index 7a54433e9..38a410c4e 100644 --- a/docs/resources/flink.md +++ b/docs/resources/flink.md @@ -130,7 +130,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/grafana.md b/docs/resources/grafana.md index a6b8736e0..0aab220ec 100644 --- a/docs/resources/grafana.md +++ b/docs/resources/grafana.md @@ -303,7 +303,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/kafka.md b/docs/resources/kafka.md index 757b85da8..60d2c0209 100644 --- a/docs/resources/kafka.md +++ b/docs/resources/kafka.md @@ -386,7 +386,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/kafka_connect.md b/docs/resources/kafka_connect.md index ae89ef988..67c323264 100644 --- a/docs/resources/kafka_connect.md +++ b/docs/resources/kafka_connect.md @@ -236,7 +236,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/kafka_mirrormaker.md b/docs/resources/kafka_mirrormaker.md index b034ff88f..7487caa8d 100644 --- a/docs/resources/kafka_mirrormaker.md +++ b/docs/resources/kafka_mirrormaker.md @@ -124,7 +124,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/m3aggregator.md b/docs/resources/m3aggregator.md index 5230f169a..ebb2d5610 100644 --- a/docs/resources/m3aggregator.md +++ b/docs/resources/m3aggregator.md @@ -113,7 +113,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/m3db.md b/docs/resources/m3db.md index 18dfd965e..49e5de062 100644 --- a/docs/resources/m3db.md +++ b/docs/resources/m3db.md @@ -264,7 +264,7 @@ Required: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/mysql.md b/docs/resources/mysql.md index 72979bb3a..c43cfecec 100644 --- a/docs/resources/mysql.md +++ b/docs/resources/mysql.md @@ -237,7 +237,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration. The possible values are `read_replica`. - `source_service_name` (String) Name of the source service diff --git a/docs/resources/opensearch.md b/docs/resources/opensearch.md index 440532586..c503c3c1d 100644 --- a/docs/resources/opensearch.md +++ b/docs/resources/opensearch.md @@ -573,7 +573,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/pg.md b/docs/resources/pg.md index 25c065508..e65936267 100644 --- a/docs/resources/pg.md +++ b/docs/resources/pg.md @@ -355,7 +355,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration. The possible values are `read_replica` and `disaster_recovery`. - `source_service_name` (String) Name of the source service diff --git a/docs/resources/redis.md b/docs/resources/redis.md index bff5865ff..6e3d3ba24 100644 --- a/docs/resources/redis.md +++ b/docs/resources/redis.md @@ -184,7 +184,7 @@ Optional: Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/thanos.md b/docs/resources/thanos.md index 670c528ac..b3a4c6acf 100644 --- a/docs/resources/thanos.md +++ b/docs/resources/thanos.md @@ -75,7 +75,7 @@ resource "aiven_thanos" "example_thanos" { Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/docs/resources/valkey.md b/docs/resources/valkey.md index e172195fd..be0bcfa0f 100644 --- a/docs/resources/valkey.md +++ b/docs/resources/valkey.md @@ -72,7 +72,7 @@ resource "aiven_valkey" "example_valkey" { Required: -- `integration_type` (String) Type of the service integration. The only supported value at the moment is `read_replica` +- `integration_type` (String) Type of the service integration - `source_service_name` (String) Name of the source service diff --git a/internal/acctest/acctest.go b/internal/acctest/acctest.go index 35d233d71..4e30e71b1 100644 --- a/internal/acctest/acctest.go +++ b/internal/acctest/acctest.go @@ -142,7 +142,7 @@ const ( DefaultResourceNamePrefix = "test-acc" // DefaultRandomSuffixLength is the default length of the random suffix used in acceptance tests. - DefaultRandomSuffixLength = 10 + DefaultRandomSuffixLength = 6 ) func RandStr() string { diff --git a/internal/schemautil/service.go b/internal/schemautil/service.go index d573f4acf..8197a74f0 100644 --- a/internal/schemautil/service.go +++ b/internal/schemautil/service.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "slices" "strconv" "strings" "time" @@ -19,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/aiven/terraform-provider-aiven/internal/common" + "github.com/aiven/terraform-provider-aiven/internal/schemautil/userconfig" "github.com/aiven/terraform-provider-aiven/internal/sdkprovider/userconfig/converters" ) @@ -71,9 +73,31 @@ var TechEmailsResourceSchema = &schema.Resource{ func ServiceCommonSchemaWithUserConfig(kind string) map[string]*schema.Schema { s := ServiceCommonSchema() converters.SetUserConfig(converters.ServiceUserConfig, kind, s) + + // Assigns the integration types that are allowed to be set when creating a service + integrations := getBootstrapIntegrationTypes(kind) + if len(integrations) > 0 { + r := s[serviceIntegrationsKey].Elem.(*schema.Resource) + r.Schema["integration_type"].Description = userconfig.Desc(r.Schema["integration_type"].Description).PossibleValuesString(FlattenToString(integrations)...).Build() + } + return s } +// getBootstrapIntegrationTypes returns the integration types that are allowed to be set when creating a service. +func getBootstrapIntegrationTypes(kind string) []service.IntegrationType { + list := make([]service.IntegrationType, 0) + if kind == ServiceTypePG || kind == ServiceTypeMySQL { + list = append(list, service.IntegrationTypeReadReplica) + } + + if kind == ServiceTypePG { + list = append(list, service.IntegrationTypeDisasterRecovery) + } + + return list +} + func ServiceCommonSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ "project": CommonSchemaProjectReference, @@ -222,7 +246,7 @@ func ServiceCommonSchema() map[string]*schema.Schema { "integration_type": { Type: schema.TypeString, Required: true, - Description: "Type of the service integration. The only supported value at the moment is `read_replica`", + Description: "Type of the service integration", }, }, }, @@ -521,7 +545,7 @@ func ResourceServiceUpdate(ctx context.Context, d *schema.ResourceData, m interf return nil } - if len(getIntegrationsForTerraform(s.ServiceIntegrations, service.IntegrationTypeAutoscaler)) == 0 { + if len(flattenIntegrations(s.ServiceIntegrations, service.IntegrationTypeAutoscaler)) == 0 { diskSpace, err := getDiskSpaceFromStateOrDiff(ctx, d, client) if err != nil { return diag.Errorf("error getting default disc space: %s", err) @@ -614,18 +638,18 @@ func getTechnicalEmailsForTerraform(s *service.ServiceGetOut) *schema.Set { return schema.NewSet(schema.HashResource(TechEmailsResourceSchema), techEmails) } -func getIntegrationsForTerraform(integrations []service.ServiceIntegrationOut, integrationType service.IntegrationType) []map[string]interface{} { - var filteredIntegrations []map[string]interface{} - for _, integration := range integrations { - if integration.IntegrationType == integrationType { - integrationMap := map[string]interface{}{ - "integration_type": integration.IntegrationType, - "source_service_name": integration.SourceService, - } - filteredIntegrations = append(filteredIntegrations, integrationMap) +// flattenIntegrations converts the service integrations into a list of maps +func flattenIntegrations(integrations []service.ServiceIntegrationOut, kinds ...service.IntegrationType) []map[string]interface{} { + result := make([]map[string]any, 0) + for _, v := range integrations { + if slices.Contains(kinds, v.IntegrationType) { + result = append(result, map[string]any{ + "integration_type": v.IntegrationType, + "source_service_name": v.SourceService, + }) } } - return filteredIntegrations + return result } func ResourceServiceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -776,9 +800,9 @@ func copyServicePropertiesFromAPIResponseToTerraform( return fmt.Errorf("cannot set `components` : %w", err) } - // Handle read_replica service integrations - readReplicaIntegrations := getIntegrationsForTerraform(s.ServiceIntegrations, service.IntegrationTypeReadReplica) - if err := d.Set("service_integrations", readReplicaIntegrations); err != nil { + // Handle service integrations + integrations := flattenIntegrations(s.ServiceIntegrations, getBootstrapIntegrationTypes(serviceType)...) + if err := d.Set(serviceIntegrationsKey, integrations); err != nil { return err } diff --git a/internal/schemautil/wait.go b/internal/schemautil/wait.go index 694bf9786..205539578 100644 --- a/internal/schemautil/wait.go +++ b/internal/schemautil/wait.go @@ -14,6 +14,7 @@ import ( "github.com/aiven/go-client-codegen/handler/staticip" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/samber/lo" "github.com/aiven/terraform-provider-aiven/internal/common" ) @@ -257,8 +258,13 @@ func backupsReady(s *service.ServiceGetOut) bool { // No backups for read replicas type of service // See https://github.com/aiven/terraform-provider-aiven/pull/172 for _, i := range s.ServiceIntegrations { - if i.IntegrationType == "read_replica" && *i.DestService == s.ServiceName { - return true + switch i.IntegrationType { + case service.IntegrationTypeReadReplica, service.IntegrationTypeDisasterRecovery: + // fixme: disaster recovery will have a backup eventually, + // remove this when BE is ready + if lo.FromPtr(i.DestService) == s.ServiceName { + return true + } } } diff --git a/internal/sdkprovider/service/pg/pg_test.go b/internal/sdkprovider/service/pg/pg_test.go index 13290a95b..782f80dd5 100644 --- a/internal/sdkprovider/service/pg/pg_test.go +++ b/internal/sdkprovider/service/pg/pg_test.go @@ -1088,3 +1088,58 @@ func testAccCheckAivenServicePGAttributes(n string) resource.TestCheckFunc { return nil } } + +func TestAccAivenServicePG_disaster_recovery(t *testing.T) { + primaryName := "aiven_pg.primary" + secondaryName := "aiven_pg.secondary" + projectName := os.Getenv("AIVEN_PROJECT_NAME") + randStr := acc.RandStr() + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acc.TestProtoV6ProviderFactories, + CheckDestroy: acc.TestAccCheckAivenServiceResourceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPGServiceResourceDisasterRecovery(projectName, randStr), + Check: resource.ComposeTestCheckFunc( + acc.TestAccCheckAivenServiceCommonAttributes(primaryName), + acc.TestAccCheckAivenServiceCommonAttributes(secondaryName), + // One service integration should be created + resource.TestCheckResourceAttr(secondaryName, "service_integrations.#", "1"), + // Which is of type disaster_recovery + resource.TestCheckTypeSetElemNestedAttrs(secondaryName, "service_integrations.*", map[string]string{ + "integration_type": "disaster_recovery", + "source_service_name": "test-acc-primary-" + randStr, + }), + ), + }, + }, + }) +} + +func testAccPGServiceResourceDisasterRecovery(project, randStr string) string { + return fmt.Sprintf(` +resource "aiven_pg" "primary" { + project = "%[1]s" + cloud_name = "google-europe-west1" + plan = "startup-4" + service_name = "test-acc-primary-%[2]s" + maintenance_window_dow = "monday" + maintenance_window_time = "10:00:00" +} + +resource "aiven_pg" "secondary" { + project = aiven_pg.primary.project + cloud_name = "google-europe-west1" + plan = "startup-4" + service_name = "test-acc-secondary-%[2]s" + maintenance_window_dow = "monday" + maintenance_window_time = "10:00:00" + + service_integrations { + integration_type = "disaster_recovery" + source_service_name = "test-acc-primary-%[2]s" + } +} +`, project, randStr) +}