From 3fc24f98e31bbc15fc7b97d9beecc1fc5b5d83c8 Mon Sep 17 00:00:00 2001 From: Harshil Sanjay Jain Date: Thu, 4 Apr 2024 13:43:55 -0400 Subject: [PATCH 1/3] Add configs for aurora serverless v2 --- .../java/gyro/aws/rds/DbClusterResource.java | 168 ++++++++++++++++-- .../aws/rds/ServerlessV2ScalingConfig.java | 64 +++++++ 2 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 src/main/java/gyro/aws/rds/ServerlessV2ScalingConfig.java diff --git a/src/main/java/gyro/aws/rds/DbClusterResource.java b/src/main/java/gyro/aws/rds/DbClusterResource.java index 6170713b1..99dcc0e7c 100644 --- a/src/main/java/gyro/aws/rds/DbClusterResource.java +++ b/src/main/java/gyro/aws/rds/DbClusterResource.java @@ -130,13 +130,22 @@ public class DbClusterResource extends RdsTaggableResource implements Copyable vpcSecurityGroups; + private String dbClusterInstanceClass; + private Boolean enableGlobalWriteForwarding; + private Integer iops; + private Boolean manageMasterUserPassword; + private KmsKeyResource masterUserSecretKmsKey; + private ServerlessV2ScalingConfig serverlessV2ScalingConfiguration; + private String storageType; + + // Read-only private String endpointAddress; private String readerEndpointAddress; - private DbClusterS3Import s3Import; /** * Apply modifications in this request and any pending modifications asynchronously as soon as possible, regardless of the `preferred-maintenance-window`. Default is false. @@ -291,7 +300,7 @@ public void setEnableIamDatabaseAuthentication(Boolean enableIamDatabaseAuthenti * The name of the database engine. */ @Required - @ValidStrings({"aurora", "aurora-mysql", "aurora-postgresql"}) + @ValidStrings({"mysql", "postgres", "aurora-mysql", "aurora-postgresql"}) public String getEngine() { return engine; } @@ -466,7 +475,7 @@ public void setSnapshotIdentifier(String snapshotIdentifier) { /** * The s3 import to restore the database from. */ - @ConflictsWith({ "snapshot-identifier" }) + @ConflictsWith({"snapshot-identifier"}) public DbClusterS3Import getS3Import() { return s3Import; } @@ -524,6 +533,92 @@ public void setVpcSecurityGroups(List vpcSecurityGroups) this.vpcSecurityGroups = vpcSecurityGroups; } + /** + * The compute and memory capacity of each DB instance in the Multi-AZ DB cluster. + * See `DB instance classes `_. + */ + @Updatable + public String getDbClusterInstanceClass() { + return dbClusterInstanceClass; + } + + public void setDbClusterInstanceClass(String dbClusterInstanceClass) { + this.dbClusterInstanceClass = dbClusterInstanceClass; + } + + /** + * When set to ``true``, enable this DB cluster to forward write operations to the primary cluster of a global cluster (Aurora global database). + */ + @Updatable + public Boolean getEnableGlobalWriteForwarding() { + return enableGlobalWriteForwarding; + } + + public void setEnableGlobalWriteForwarding(Boolean enableGlobalWriteForwarding) { + this.enableGlobalWriteForwarding = enableGlobalWriteForwarding; + } + + /** + * The amount of Provisioned IOPS (input/output operations per second) to be initially allocated for each DB instance in the Multi-AZ DB cluster. + */ + @Updatable + public Integer getIops() { + return iops; + } + + public void setIops(Integer iops) { + this.iops = iops; + } + + /** + * When set to ``true``, manage the master user password with Amazon Web Services Secrets Manager. + */ + @Updatable + public Boolean getManageMasterUserPassword() { + return manageMasterUserPassword; + } + + public void setManageMasterUserPassword(Boolean manageMasterUserPassword) { + this.manageMasterUserPassword = manageMasterUserPassword; + } + + /** + * The Amazon Web Services KMS key to encrypt a secret that is automatically generated and managed in Amazon Web Services Secrets Manager. + */ + @Updatable + public KmsKeyResource getMasterUserSecretKmsKey() { + return masterUserSecretKmsKey; + } + + public void setMasterUserSecretKmsKey(KmsKeyResource masterUserSecretKmsKey) { + this.masterUserSecretKmsKey = masterUserSecretKmsKey; + } + + /** + * The serverless scaling configuration. + */ + @Updatable + public ServerlessV2ScalingConfig getServerlessV2ScalingConfiguration() { + return serverlessV2ScalingConfiguration; + } + + public void setServerlessV2ScalingConfiguration(ServerlessV2ScalingConfig serverlessV2ScalingConfiguration) { + this.serverlessV2ScalingConfiguration = serverlessV2ScalingConfiguration; + } + + /** + * The storage type to associate with the DB cluster. + */ + @Updatable + @ValidStrings({"gp3", "io1", "io2", "aurora", "aurora-iopt1"}) + public String getStorageType() { + return storageType; + } + + public void setStorageType(String storageType) { + this.storageType = storageType; + } + /** * DNS hostname to access the primary instance of the cluster. */ @@ -600,6 +695,21 @@ public void copyFrom(DBCluster cluster) { setEndpointAddress(cluster.endpoint()); setReaderEndpointAddress(cluster.readerEndpoint()); + setDbClusterInstanceClass(cluster.dbClusterInstanceClass()); + setEnableGlobalWriteForwarding(cluster.globalWriteForwardingRequested()); + setIops(cluster.iops()); + setStorageType(cluster.storageType()); + + if (cluster.masterUserSecret() != null && cluster.masterUserSecret().kmsKeyId() != null) { + setManageMasterUserPassword(true); + setMasterUserSecretKmsKey(findById(KmsKeyResource.class, cluster.masterUserSecret().kmsKeyId())); + } + + if (cluster.serverlessV2ScalingConfiguration() != null) { + ServerlessV2ScalingConfig config = new ServerlessV2ScalingConfig(); + config.copyFrom(cluster.serverlessV2ScalingConfiguration()); + setServerlessV2ScalingConfiguration(config); + } } @Override @@ -627,14 +737,15 @@ protected boolean doRefresh() { @Override protected void doCreate(GyroUI ui, State state) { RdsClient client = createClient(RdsClient.class); - software.amazon.awssdk.services.rds.model.ScalingConfiguration scalingConfiguration = getScalingConfiguration() != null - ? software.amazon.awssdk.services.rds.model.ScalingConfiguration.builder() + software.amazon.awssdk.services.rds.model.ScalingConfiguration scalingConfiguration = + getScalingConfiguration() != null + ? software.amazon.awssdk.services.rds.model.ScalingConfiguration.builder() .autoPause(getScalingConfiguration().getAutoPause()) .maxCapacity(getScalingConfiguration().getMaxCapacity()) .minCapacity(getScalingConfiguration().getMinCapacity()) .secondsUntilAutoPause(getScalingConfiguration().getSecondsUntilAutoPause()) .build() - : null; + : null; if (getSnapshotIdentifier() != null) { RestoreDbClusterFromSnapshotResponse response = client.restoreDBClusterFromSnapshot( @@ -660,6 +771,11 @@ protected void doCreate(GyroUI ui, State state) { .stream() .map(SecurityGroupResource::getId) .collect(Collectors.toList()) : null) + .dbClusterInstanceClass(getDbClusterInstanceClass()) + .iops(getIops()) + .serverlessV2ScalingConfiguration(getServerlessV2ScalingConfiguration() != null ? + getServerlessV2ScalingConfiguration().toServerlessV2ScalingConfiguration() : null) + .storageType(getStorageType()) ); setArn(response.dbCluster().dbClusterArn()); @@ -709,7 +825,13 @@ protected void doCreate(GyroUI ui, State state) { .vpcSecurityGroupIds(getVpcSecurityGroups() != null ? getVpcSecurityGroups() .stream() .map(SecurityGroupResource::getId) - .collect(Collectors.toList()) : null)); + .collect(Collectors.toList()) : null) + .manageMasterUserPassword(getManageMasterUserPassword()) + .masterUserSecretKmsKeyId(getMasterUserSecretKmsKey().getId()) + .serverlessV2ScalingConfiguration(getServerlessV2ScalingConfiguration() != null ? + getServerlessV2ScalingConfiguration().toServerlessV2ScalingConfiguration() : null) + .storageType(getStorageType()) + ); setArn(response.dbCluster().dbClusterArn()); state.save(); @@ -757,6 +879,14 @@ protected void doCreate(GyroUI ui, State state) { .stream() .map(SecurityGroupResource::getId) .collect(Collectors.toList()) : null) + .dbClusterInstanceClass(getDbClusterInstanceClass()) + .enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()) + .iops(getIops()) + .manageMasterUserPassword(getManageMasterUserPassword()) + .masterUserSecretKmsKeyId(getMasterUserSecretKmsKey().getId()) + .serverlessV2ScalingConfiguration(getServerlessV2ScalingConfiguration() != null ? + getServerlessV2ScalingConfiguration().toServerlessV2ScalingConfiguration() : null) + .storageType(getStorageType()) ); setArn(response.dbCluster().dbClusterArn()); @@ -784,16 +914,18 @@ private boolean isAvailable(RdsClient client) { protected void doUpdate(Resource config, Set changedProperties) { RdsClient client = createClient(RdsClient.class); DbClusterResource current = (DbClusterResource) config; - software.amazon.awssdk.services.rds.model.ScalingConfiguration scalingConfiguration = getScalingConfiguration() != null - ? software.amazon.awssdk.services.rds.model.ScalingConfiguration.builder() + software.amazon.awssdk.services.rds.model.ScalingConfiguration scalingConfiguration = + getScalingConfiguration() != null + ? software.amazon.awssdk.services.rds.model.ScalingConfiguration.builder() .autoPause(getScalingConfiguration().getAutoPause()) .maxCapacity(getScalingConfiguration().getMaxCapacity()) .minCapacity(getScalingConfiguration().getMinCapacity()) .secondsUntilAutoPause(getScalingConfiguration().getSecondsUntilAutoPause()) .build() - : null; + : null; - String clusterParameterGroupName = getDbClusterParameterGroup() != null ? getDbClusterParameterGroup().getName() : null; + String clusterParameterGroupName = + getDbClusterParameterGroup() != null ? getDbClusterParameterGroup().getName() : null; String optionGroupName = getOptionGroup() != null ? getOptionGroup().getName() : null; List vpcSecurityGroupIds = getVpcSecurityGroups() != null ? getVpcSecurityGroups() .stream() @@ -834,6 +966,14 @@ protected void doUpdate(Resource config, Set changedProperties) { .scalingConfiguration(scalingConfiguration) .vpcSecurityGroupIds(Objects.equals(getVpcSecurityGroups(), current.getVpcSecurityGroups()) ? null : vpcSecurityGroupIds) + .dbClusterInstanceClass(getDbClusterInstanceClass()) + .enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()) + .iops(getIops()) + .manageMasterUserPassword(getManageMasterUserPassword()) + .masterUserSecretKmsKeyId(getMasterUserSecretKmsKey().getId()) + .serverlessV2ScalingConfiguration(getServerlessV2ScalingConfiguration() != null ? + getServerlessV2ScalingConfiguration().toServerlessV2ScalingConfiguration() : null) + .storageType(getStorageType()) ); } catch (InvalidDbClusterStateException ex) { throw new GyroException(ex.getLocalizedMessage()); @@ -846,14 +986,14 @@ public void delete(GyroUI ui, State state) { if (getGlobalCluster() != null) { client.removeFromGlobalCluster( r -> r.dbClusterIdentifier(getArn()) - .globalClusterIdentifier(getGlobalCluster().getIdentifier()) + .globalClusterIdentifier(getGlobalCluster().getIdentifier()) ); } client.deleteDBCluster( r -> r.dbClusterIdentifier(getIdentifier()) - .finalDBSnapshotIdentifier(!getSkipFinalSnapshot() ? getFinalDbSnapshotIdentifier() : null) - .skipFinalSnapshot(getSkipFinalSnapshot()) + .finalDBSnapshotIdentifier(!getSkipFinalSnapshot() ? getFinalDbSnapshotIdentifier() : null) + .skipFinalSnapshot(getSkipFinalSnapshot()) ); Wait.atMost(5, TimeUnit.MINUTES) diff --git a/src/main/java/gyro/aws/rds/ServerlessV2ScalingConfig.java b/src/main/java/gyro/aws/rds/ServerlessV2ScalingConfig.java new file mode 100644 index 000000000..6ee86ce06 --- /dev/null +++ b/src/main/java/gyro/aws/rds/ServerlessV2ScalingConfig.java @@ -0,0 +1,64 @@ +package gyro.aws.rds; + +import gyro.aws.Copyable; +import gyro.core.resource.Diffable; +import software.amazon.awssdk.services.rds.model.ServerlessV2ScalingConfiguration; +import software.amazon.awssdk.services.rds.model.ServerlessV2ScalingConfigurationInfo; + +public class ServerlessV2ScalingConfig extends Diffable implements Copyable { + + private Double maxCapacity; + private Double minCapacity; + + /** + * The maximum number of Aurora capacity units (ACUs) for a DB instance in an Aurora Serverless v2 cluster. + */ + public Double getMaxCapacity() { + return maxCapacity; + } + + public void setMaxCapacity(Double maxCapacity) { + this.maxCapacity = maxCapacity; + } + + /** + * The minimum number of Aurora capacity units (ACUs) for a DB instance in an Aurora Serverless v2 cluster. + */ + public Double getMinCapacity() { + return minCapacity; + } + + public void setMinCapacity(Double minCapacity) { + this.minCapacity = minCapacity; + } + + @Override + public void copyFrom(ServerlessV2ScalingConfiguration model) { + setMaxCapacity(model.maxCapacity()); + setMinCapacity(model.minCapacity()); + } + + public void copyFrom(ServerlessV2ScalingConfigurationInfo model) { + setMaxCapacity(model.maxCapacity()); + setMinCapacity(model.minCapacity()); + } + + @Override + public String primaryKey() { + return ""; + } + + public ServerlessV2ScalingConfiguration toServerlessV2ScalingConfiguration() { + ServerlessV2ScalingConfiguration.Builder builder = ServerlessV2ScalingConfiguration.builder(); + + if (getMaxCapacity() != null) { + builder.maxCapacity(getMaxCapacity()); + } + + if (getMinCapacity() != null) { + builder.minCapacity(getMinCapacity()); + } + + return builder.build(); + } +} From 1dac83cc430cc032f371da63465b427898d14ac5 Mon Sep 17 00:00:00 2001 From: Harshil Sanjay Jain Date: Fri, 12 Apr 2024 12:53:40 -0400 Subject: [PATCH 2/3] Add more fields --- .../java/gyro/aws/rds/DbClusterResource.java | 53 +++- .../java/gyro/aws/rds/DbInstanceResource.java | 284 ++++++++++++------ 2 files changed, 239 insertions(+), 98 deletions(-) diff --git a/src/main/java/gyro/aws/rds/DbClusterResource.java b/src/main/java/gyro/aws/rds/DbClusterResource.java index 99dcc0e7c..a3fb5dff8 100644 --- a/src/main/java/gyro/aws/rds/DbClusterResource.java +++ b/src/main/java/gyro/aws/rds/DbClusterResource.java @@ -142,6 +142,9 @@ public class DbClusterResource extends RdsTaggableResource implements Copyable changedProperties) { .serverlessV2ScalingConfiguration(getServerlessV2ScalingConfiguration() != null ? getServerlessV2ScalingConfiguration().toServerlessV2ScalingConfiguration() : null) .storageType(getStorageType()) + .allocatedStorage(getAllocatedStorage()) + .autoMinorVersionUpgrade(getAutoMinorVersionUpgrade()) + .copyTagsToSnapshot(getCopyTagsToSnapshot()) ); } catch (InvalidDbClusterStateException ex) { throw new GyroException(ex.getLocalizedMessage()); diff --git a/src/main/java/gyro/aws/rds/DbInstanceResource.java b/src/main/java/gyro/aws/rds/DbInstanceResource.java index dc79700c1..aed00ac01 100644 --- a/src/main/java/gyro/aws/rds/DbInstanceResource.java +++ b/src/main/java/gyro/aws/rds/DbInstanceResource.java @@ -16,19 +16,25 @@ package gyro.aws.rds; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import com.psddev.dari.util.ObjectUtils; import gyro.aws.Copyable; import gyro.aws.ec2.SecurityGroupResource; import gyro.aws.kms.KmsKeyResource; import gyro.core.GyroException; import gyro.core.GyroUI; import gyro.core.TimeoutSettings; +import gyro.core.Type; import gyro.core.Wait; import gyro.core.resource.Id; -import gyro.core.resource.Updatable; -import gyro.core.Type; import gyro.core.resource.Output; import gyro.core.resource.Resource; -import com.psddev.dari.util.ObjectUtils; +import gyro.core.resource.Updatable; import gyro.core.scope.State; import gyro.core.validation.Min; import gyro.core.validation.Range; @@ -44,12 +50,6 @@ import software.amazon.awssdk.services.rds.model.DomainMembership; import software.amazon.awssdk.services.rds.model.InvalidDbInstanceStateException; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - /** * Create a db instance. * @@ -125,6 +125,9 @@ public class DbInstanceResource extends RdsTaggableResource implements Copyable< private String tdeCredentialPassword; private String timezone; private List vpcSecurityGroups; + private String backupTarget; + private Boolean dedicatedLogVolume; + private String domainAuthSecretArn; private String endpointAddress; /** @@ -395,7 +398,12 @@ public void setEnablePerformanceInsights(Boolean enablePerformanceInsights) { /** * The name of the database engine to use for this DB Instance. */ - @ValidStrings({"aurora", "aurora-mysql", "aurora-postgresql", "mariadb", "mysql", "oracle-ee", "oracle-se2", "oracle-se1", "oracle-se", "postgres", "sqlserver-ee", "sqlserver-se", "sqlserver-ex", "sqlserver-we"}) + @ValidStrings({ + "aurora-mysql", "aurora-postgresql", "custom-oracle-ee", "custom-oracle-ee-cdb", "custom-sqlserver-ee", + "custom-sqlserver-se", "custom-sqlserver-web", "db2-ae", "db2-se", "mariadb", "mysql", "oracle-ee", + "oracle-ee-cdb", "oracle-se2", "oracle-se2-cdb", "postgres", "sqlserver-ee", "sqlserver-se", "sqlserver-ex", + "sqlserver-web" + }) public String getEngine() { return engine; } @@ -491,7 +499,7 @@ public void setMasterUsername(String masterUsername) { * Enhanced Monitoring metrics collecting interval in seconds. The default is 0 (disable collection). */ @Updatable - @ValidNumbers({0,1,5,10,15,30,60}) + @ValidNumbers({0, 1, 5, 10, 15, 30, 60}) public Integer getMonitoringInterval() { return monitoringInterval; } @@ -552,7 +560,7 @@ public void setPerformanceInsightsKmsKey(KmsKeyResource performanceInsightsKmsKe * How many days to retain Performance Insights data. */ @Updatable - @ValidNumbers({7,731}) + @ValidNumbers({7, 731}) public Integer getPerformanceInsightsRetentionPeriod() { return performanceInsightsRetentionPeriod; } @@ -704,6 +712,42 @@ public void setVpcSecurityGroups(List vpcSecurityGroups) this.vpcSecurityGroups = vpcSecurityGroups; } + /** + * The location for storing automated backups and manual snapshots. + */ + @ValidStrings({"outposts", "region"}) + public String getBackupTarget() { + return backupTarget; + } + + public void setBackupTarget(String backupTarget) { + this.backupTarget = backupTarget; + } + + /** + * When set to ``true`` the DB instance has a dedicated log volume (DLV) enabled. + */ + @Updatable + public Boolean getDedicatedLogVolume() { + return dedicatedLogVolume; + } + + public void setDedicatedLogVolume(Boolean dedicatedLogVolume) { + this.dedicatedLogVolume = dedicatedLogVolume; + } + + /** + * The ARN for the Secrets Manager secret with the credentials for the user joining the domain. + */ + @Updatable + public String getDomainAuthSecretArn() { + return domainAuthSecretArn; + } + + public void setDomainAuthSecretArn(String domainAuthSecretArn) { + this.domainAuthSecretArn = domainAuthSecretArn; + } + /** * DNS hostname to access this database at. */ @@ -724,7 +768,9 @@ public void copyFrom(DBInstance instance) { setBackupRetentionPeriod(instance.backupRetentionPeriod()); setCharacterSetName(instance.characterSetName()); setCopyTagsToSnapshot(instance.copyTagsToSnapshot()); - setDbCluster(instance.dbClusterIdentifier() != null ? findById(DbClusterResource.class, instance.dbClusterIdentifier()) : null); + setDbCluster( + instance.dbClusterIdentifier() != null ? findById(DbClusterResource.class, instance.dbClusterIdentifier()) : + null); setDbInstanceClass(instance.dbInstanceClass()); setIdentifier(instance.dbInstanceIdentifier()); setDbName(instance.dbName()); @@ -737,7 +783,8 @@ public void copyFrom(DBInstance instance) { .map(DBSecurityGroupMembership::dbSecurityGroupName) .collect(Collectors.toList())); - setDbSubnetGroup(instance.dbSubnetGroup() != null ? findById(DbSubnetGroupResource.class, instance.dbSubnetGroup().dbSubnetGroupName()) : null); + setDbSubnetGroup(instance.dbSubnetGroup() != null ? + findById(DbSubnetGroupResource.class, instance.dbSubnetGroup().dbSubnetGroupName()) : null); setDeletionProtection(instance.deletionProtection()); setDomain(instance.domainMemberships().stream() @@ -772,7 +819,8 @@ public void copyFrom(DBInstance instance) { .findFirst().map(s -> findById(DbOptionGroupResource.class, s.optionGroupName())) .orElse(null)); - setPerformanceInsightsKmsKey(instance.performanceInsightsKMSKeyId() != null ? findById(KmsKeyResource.class, instance.performanceInsightsKMSKeyId()) : null); + setPerformanceInsightsKmsKey(instance.performanceInsightsKMSKeyId() != null ? + findById(KmsKeyResource.class, instance.performanceInsightsKMSKeyId()) : null); setPerformanceInsightsRetentionPeriod(instance.performanceInsightsRetentionPeriod()); setPort(instance.dbInstancePort()); setPreferredBackupWindow(instance.preferredBackupWindow()); @@ -787,6 +835,11 @@ public void copyFrom(DBInstance instance) { .map(s -> findById(SecurityGroupResource.class, s.vpcSecurityGroupId())) .collect(Collectors.toList())); setArn(instance.dbInstanceArn()); + setBackupTarget(instance.backupTarget()); + setDedicatedLogVolume(instance.dedicatedLogVolume()); + setDomainAuthSecretArn(instance.domainMemberships().stream() + .findFirst().map(DomainMembership::authSecretArn) + .orElse(null)); if (instance.endpoint() != null) { setEndpointAddress(instance.endpoint().address()); @@ -820,51 +873,55 @@ public void doCreate(GyroUI ui, State state) { RdsClient client = createClient(RdsClient.class); CreateDbInstanceResponse response = client.createDBInstance( r -> r.allocatedStorage(getAllocatedStorage()) - .autoMinorVersionUpgrade(getAutoMinorVersionUpgrade()) - .availabilityZone(getAvailabilityZone()) - .backupRetentionPeriod(getBackupRetentionPeriod()) - .characterSetName(getCharacterSetName()) - .copyTagsToSnapshot(getCopyTagsToSnapshot()) - .dbClusterIdentifier(getDbCluster() != null ? getDbCluster().getIdentifier() : null) - .dbInstanceClass(getDbInstanceClass()) - .dbInstanceIdentifier(getIdentifier()) - .dbName(getDbName()) - .dbParameterGroupName(getDbParameterGroup() != null ? getDbParameterGroup().getName() : null) - .dbSecurityGroups(getDbSecurityGroups()) - .dbSubnetGroupName(getDbSubnetGroup() != null ? getDbSubnetGroup().getName() : null) - .deletionProtection(getDeletionProtection()) - .domain(getDomain()) - .domainIAMRoleName(getDomainIamRoleName()) - .enableCloudwatchLogsExports(getEnableCloudwatchLogsExports()) - .enableIAMDatabaseAuthentication(getEnableIamDatabaseAuthentication()) - .enablePerformanceInsights(getEnablePerformanceInsights()) - .engine(getEngine()) - .engineVersion(getEngineVersion()) - .iops(getIops()) - .kmsKeyId(getKmsKey() != null ? getKmsKey().getArn() : null) - .licenseModel(getLicenseModel()) - .masterUsername(getMasterUsername()) - .masterUserPassword(getMasterUserPassword()) - .monitoringInterval(getMonitoringInterval()) - .monitoringRoleArn(getMonitoringRoleArn()) - .multiAZ(getMultiAz()) - .optionGroupName(getOptionGroup() != null ? getOptionGroup().getName() : null) - .performanceInsightsKMSKeyId(getPerformanceInsightsKmsKey() != null ? getPerformanceInsightsKmsKey().getArn() : null) - .performanceInsightsRetentionPeriod(getPerformanceInsightsRetentionPeriod()) - .port(getPort()) - .preferredBackupWindow(getPreferredBackupWindow()) - .preferredMaintenanceWindow(getPreferredMaintenanceWindow()) - .promotionTier(getPromotionTier()) - .publiclyAccessible(getPubliclyAccessible()) - .storageEncrypted(getStorageEncrypted()) - .storageType(getStorageType()) - .tdeCredentialArn(getTdeCredentialArn()) - .tdeCredentialPassword(getTdeCredentialPassword()) - .timezone(getTimezone()) - .vpcSecurityGroupIds(getVpcSecurityGroups() != null ? getVpcSecurityGroups() - .stream() - .map(SecurityGroupResource::getId) - .collect(Collectors.toList()) : null) + .autoMinorVersionUpgrade(getAutoMinorVersionUpgrade()) + .availabilityZone(getAvailabilityZone()) + .backupRetentionPeriod(getBackupRetentionPeriod()) + .characterSetName(getCharacterSetName()) + .copyTagsToSnapshot(getCopyTagsToSnapshot()) + .dbClusterIdentifier(getDbCluster() != null ? getDbCluster().getIdentifier() : null) + .dbInstanceClass(getDbInstanceClass()) + .dbInstanceIdentifier(getIdentifier()) + .dbName(getDbName()) + .dbParameterGroupName(getDbParameterGroup() != null ? getDbParameterGroup().getName() : null) + .dbSecurityGroups(getDbSecurityGroups()) + .dbSubnetGroupName(getDbSubnetGroup() != null ? getDbSubnetGroup().getName() : null) + .deletionProtection(getDeletionProtection()) + .domain(getDomain()) + .domainIAMRoleName(getDomainIamRoleName()) + .enableCloudwatchLogsExports(getEnableCloudwatchLogsExports()) + .enableIAMDatabaseAuthentication(getEnableIamDatabaseAuthentication()) + .enablePerformanceInsights(getEnablePerformanceInsights()) + .engine(getEngine()) + .engineVersion(getEngineVersion()) + .iops(getIops()) + .kmsKeyId(getKmsKey() != null ? getKmsKey().getArn() : null) + .licenseModel(getLicenseModel()) + .masterUsername(getMasterUsername()) + .masterUserPassword(getMasterUserPassword()) + .monitoringInterval(getMonitoringInterval()) + .monitoringRoleArn(getMonitoringRoleArn()) + .multiAZ(getMultiAz()) + .optionGroupName(getOptionGroup() != null ? getOptionGroup().getName() : null) + .performanceInsightsKMSKeyId( + getPerformanceInsightsKmsKey() != null ? getPerformanceInsightsKmsKey().getArn() : null) + .performanceInsightsRetentionPeriod(getPerformanceInsightsRetentionPeriod()) + .port(getPort()) + .preferredBackupWindow(getPreferredBackupWindow()) + .preferredMaintenanceWindow(getPreferredMaintenanceWindow()) + .promotionTier(getPromotionTier()) + .publiclyAccessible(getPubliclyAccessible()) + .storageEncrypted(getStorageEncrypted()) + .storageType(getStorageType()) + .tdeCredentialArn(getTdeCredentialArn()) + .tdeCredentialPassword(getTdeCredentialPassword()) + .timezone(getTimezone()) + .vpcSecurityGroupIds(getVpcSecurityGroups() != null ? getVpcSecurityGroups() + .stream() + .map(SecurityGroupResource::getId) + .collect(Collectors.toList()) : null) + .backupTarget(getBackupTarget()) + .dedicatedLogVolume(getDedicatedLogVolume()) + .domainAuthSecretArn(getDomainAuthSecretArn()) ); setArn(response.dbInstance().dbInstanceArn()); @@ -904,7 +961,8 @@ public void doUpdate(Resource config, Set changedProperties) { String parameterGroupName = getDbParameterGroup() != null ? getDbParameterGroup().getName() : null; String subnetGroupName = getDbSubnetGroup() != null ? getDbSubnetGroup().getName() : null; String optionGroupName = getOptionGroup() != null ? getOptionGroup().getName() : null; - String performanceInsightsKmsKeyId = getPerformanceInsightsKmsKey() != null ? getPerformanceInsightsKmsKey().getArn() : null; + String performanceInsightsKmsKeyId = + getPerformanceInsightsKmsKey() != null ? getPerformanceInsightsKmsKey().getArn() : null; List vpcSecurityGroupIds = getVpcSecurityGroups() != null ? getVpcSecurityGroups() .stream() .map(SecurityGroupResource::getId) @@ -912,55 +970,87 @@ public void doUpdate(Resource config, Set changedProperties) { try { client.modifyDBInstance( - r -> r.allocatedStorage(Objects.equals(getAllocatedStorage(), current.getAllocatedStorage()) ? null : getAllocatedStorage()) - .applyImmediately(Objects.equals(getApplyImmediately(), current.getApplyImmediately()) ? null : getApplyImmediately()) - .allowMajorVersionUpgrade(Objects.equals(getAllowMajorVersionUpgrade(), current.getAllowMajorVersionUpgrade()) - ? null : getAllowMajorVersionUpgrade()) - .autoMinorVersionUpgrade(Objects.equals(getAutoMinorVersionUpgrade(), current.getAutoMinorVersionUpgrade()) - ? null : getAutoMinorVersionUpgrade()) - .backupRetentionPeriod(Objects.equals(getBackupRetentionPeriod(), current.getBackupRetentionPeriod()) - ? null : getBackupRetentionPeriod()) + r -> r.allocatedStorage( + Objects.equals(getAllocatedStorage(), current.getAllocatedStorage()) ? null : getAllocatedStorage()) + .applyImmediately(Objects.equals(getApplyImmediately(), current.getApplyImmediately()) ? null : + getApplyImmediately()) + .allowMajorVersionUpgrade( + Objects.equals(getAllowMajorVersionUpgrade(), current.getAllowMajorVersionUpgrade()) + ? null : getAllowMajorVersionUpgrade()) + .autoMinorVersionUpgrade( + Objects.equals(getAutoMinorVersionUpgrade(), current.getAutoMinorVersionUpgrade()) + ? null : getAutoMinorVersionUpgrade()) + .backupRetentionPeriod( + Objects.equals(getBackupRetentionPeriod(), current.getBackupRetentionPeriod()) + ? null : getBackupRetentionPeriod()) .cloudwatchLogsExportConfiguration(c -> c.enableLogTypes(getEnableCloudwatchLogsExports())) - .copyTagsToSnapshot(Objects.equals(getCopyTagsToSnapshot(), current.getCopyTagsToSnapshot()) ? null : getCopyTagsToSnapshot()) - .dbInstanceClass(Objects.equals(getDbInstanceClass(), current.getDbInstanceClass()) ? null : getDbInstanceClass()) + .copyTagsToSnapshot( + Objects.equals(getCopyTagsToSnapshot(), current.getCopyTagsToSnapshot()) ? null : + getCopyTagsToSnapshot()) + .dbInstanceClass(Objects.equals(getDbInstanceClass(), current.getDbInstanceClass()) ? null : + getDbInstanceClass()) .dbInstanceIdentifier(getIdentifier()) .dbParameterGroupName(Objects.equals(getDbParameterGroup(), current.getDbParameterGroup()) ? null : parameterGroupName) - .dbSecurityGroups(Objects.equals(getDbSecurityGroups(), current.getDbSecurityGroups()) ? null : getDbSecurityGroups()) - .dbSubnetGroupName(Objects.equals(getDbSubnetGroup(), current.getDbSubnetGroup()) ? null : subnetGroupName) - .deletionProtection(Objects.equals(getDeletionProtection(), current.getDeletionProtection()) ? null : getDeletionProtection()) + .dbSecurityGroups(Objects.equals(getDbSecurityGroups(), current.getDbSecurityGroups()) ? null : + getDbSecurityGroups()) + .dbSubnetGroupName( + Objects.equals(getDbSubnetGroup(), current.getDbSubnetGroup()) ? null : subnetGroupName) + .deletionProtection( + Objects.equals(getDeletionProtection(), current.getDeletionProtection()) ? null : + getDeletionProtection()) .domain(Objects.equals(getDomain(), current.getDomain()) ? null : getDomain()) - .domainIAMRoleName(Objects.equals(getDomainIamRoleName(), current.getDomainIamRoleName()) ? null : getDomainIamRoleName()) + .domainIAMRoleName(Objects.equals(getDomainIamRoleName(), current.getDomainIamRoleName()) ? null : + getDomainIamRoleName()) .enableIAMDatabaseAuthentication(Objects.equals( getEnableIamDatabaseAuthentication(), current.getEnableIamDatabaseAuthentication()) ? null : getEnableIamDatabaseAuthentication()) - .enablePerformanceInsights(Objects.equals(getEnablePerformanceInsights(), current.getEnablePerformanceInsights()) - ? null : getEnablePerformanceInsights()) - .engineVersion(Objects.equals(getEngineVersion(), current.getEngineVersion()) ? null : getEngineVersion()) + .enablePerformanceInsights( + Objects.equals(getEnablePerformanceInsights(), current.getEnablePerformanceInsights()) + ? null : getEnablePerformanceInsights()) + .engineVersion( + Objects.equals(getEngineVersion(), current.getEngineVersion()) ? null : getEngineVersion()) .iops(Objects.equals(getIops(), current.getIops()) ? null : getIops()) - .licenseModel(Objects.equals(getLicenseModel(), current.getLicenseModel()) ? null : getLicenseModel()) - .masterUserPassword(Objects.equals(getMasterUserPassword(), current.getMasterUserPassword()) ? null : getMasterUserPassword()) - .monitoringInterval(Objects.equals(getMonitoringInterval(), current.getMonitoringInterval()) ? null : getMonitoringInterval()) - .monitoringRoleArn(Objects.equals(getMonitoringRoleArn(), current.getMonitoringRoleArn()) ? null : getMonitoringRoleArn()) + .licenseModel( + Objects.equals(getLicenseModel(), current.getLicenseModel()) ? null : getLicenseModel()) + .masterUserPassword( + Objects.equals(getMasterUserPassword(), current.getMasterUserPassword()) ? null : + getMasterUserPassword()) + .monitoringInterval( + Objects.equals(getMonitoringInterval(), current.getMonitoringInterval()) ? null : + getMonitoringInterval()) + .monitoringRoleArn(Objects.equals(getMonitoringRoleArn(), current.getMonitoringRoleArn()) ? null : + getMonitoringRoleArn()) .multiAZ(Objects.equals(getMultiAz(), current.getMultiAz()) ? null : getMultiAz()) - .optionGroupName(Objects.equals(getOptionGroup(), current.getOptionGroup()) ? null : optionGroupName) - .performanceInsightsKMSKeyId(Objects.equals(getPerformanceInsightsKmsKey(), current.getPerformanceInsightsKmsKey()) - ? null : performanceInsightsKmsKeyId) + .optionGroupName( + Objects.equals(getOptionGroup(), current.getOptionGroup()) ? null : optionGroupName) + .performanceInsightsKMSKeyId( + Objects.equals(getPerformanceInsightsKmsKey(), current.getPerformanceInsightsKmsKey()) + ? null : performanceInsightsKmsKeyId) .performanceInsightsRetentionPeriod(Objects.equals( getPerformanceInsightsRetentionPeriod(), current.getPerformanceInsightsRetentionPeriod()) ? null : getPerformanceInsightsRetentionPeriod()) - .preferredBackupWindow(Objects.equals(getPreferredBackupWindow(), current.getPreferredBackupWindow()) - ? null : getPreferredBackupWindow()) - .preferredMaintenanceWindow(Objects.equals(getPreferredMaintenanceWindow(), current.getPreferredMaintenanceWindow()) - ? null : getPreferredMaintenanceWindow()) - .promotionTier(Objects.equals(getPromotionTier(), current.getPromotionTier()) ? null : getPromotionTier()) - .publiclyAccessible(Objects.equals(getPubliclyAccessible(), current.getPubliclyAccessible()) ? null : getPubliclyAccessible()) + .preferredBackupWindow( + Objects.equals(getPreferredBackupWindow(), current.getPreferredBackupWindow()) + ? null : getPreferredBackupWindow()) + .preferredMaintenanceWindow( + Objects.equals(getPreferredMaintenanceWindow(), current.getPreferredMaintenanceWindow()) + ? null : getPreferredMaintenanceWindow()) + .promotionTier( + Objects.equals(getPromotionTier(), current.getPromotionTier()) ? null : getPromotionTier()) + .publiclyAccessible( + Objects.equals(getPubliclyAccessible(), current.getPubliclyAccessible()) ? null : + getPubliclyAccessible()) .storageType(Objects.equals(getStorageType(), current.getStorageType()) ? null : getStorageType()) - .tdeCredentialArn(Objects.equals(getTdeCredentialArn(), current.getTdeCredentialArn()) ? null : getTdeCredentialArn()) - .tdeCredentialPassword(Objects.equals(getTdeCredentialPassword(), current.getTdeCredentialPassword()) - ? null : getTdeCredentialPassword()) + .tdeCredentialArn(Objects.equals(getTdeCredentialArn(), current.getTdeCredentialArn()) ? null : + getTdeCredentialArn()) + .tdeCredentialPassword( + Objects.equals(getTdeCredentialPassword(), current.getTdeCredentialPassword()) + ? null : getTdeCredentialPassword()) .vpcSecurityGroupIds(Objects.equals(getVpcSecurityGroups(), current.getVpcSecurityGroups()) ? null : vpcSecurityGroupIds) + .dedicatedLogVolume(getDedicatedLogVolume()) + .domainAuthSecretArn(getDomainAuthSecretArn()) ); } catch (InvalidDbInstanceStateException ex) { throw new GyroException(ex.getLocalizedMessage()); @@ -972,9 +1062,9 @@ public void delete(GyroUI ui, State state) { RdsClient client = createClient(RdsClient.class); client.deleteDBInstance( r -> r.dbInstanceIdentifier(getIdentifier()) - .finalDBSnapshotIdentifier(getFinalDbSnapshotIdentifier()) - .skipFinalSnapshot(getSkipFinalSnapshot()) - .deleteAutomatedBackups(getDeleteAutomatedBackups()) + .finalDBSnapshotIdentifier(getFinalDbSnapshotIdentifier()) + .skipFinalSnapshot(getSkipFinalSnapshot()) + .deleteAutomatedBackups(getDeleteAutomatedBackups()) ); Wait.atMost(5, TimeUnit.MINUTES) From 321141f6d94431c680215e0552e20cd571b776ec Mon Sep 17 00:00:00 2001 From: Harshil Sanjay Jain Date: Tue, 27 Aug 2024 00:06:12 -0400 Subject: [PATCH 3/3] add examples and local write forwarding --- examples/rds/db-cluster-serverless.gyro | 135 ++++++++++++++++++ .../java/gyro/aws/rds/DbClusterResource.java | 52 ++++++- 2 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 examples/rds/db-cluster-serverless.gyro diff --git a/examples/rds/db-cluster-serverless.gyro b/examples/rds/db-cluster-serverless.gyro new file mode 100644 index 000000000..134e5673a --- /dev/null +++ b/examples/rds/db-cluster-serverless.gyro @@ -0,0 +1,135 @@ +aws::vpc vpc + cidr-block: "10.0.0.0/16" + + tags: { + Name: "vpc-example" + } +end + +aws::subnet subnet-us-east-2a + vpc: $(aws::vpc vpc) + availability-zone: "us-east-2a" + cidr-block: "10.0.0.0/24" + tags: { + Name: "subnet-us-east-2a-example" + } +end + +aws::subnet subnet-us-east-2b + vpc: $(aws::vpc vpc) + availability-zone: "us-east-2b" + cidr-block: "10.0.1.0/24" + tags: { + Name: "subnet-us-east-2b-example" + } +end + +aws::subnet subnet-us-east-2c + vpc: $(aws::vpc vpc) + availability-zone: "us-east-2c" + cidr-block: "10.0.2.0/24" + tags: { + Name: "subnet-us-east-2c-example" + } +end + +aws::db-subnet-group db-subnet-group + name: "db-subnet-group-example" + description: "db subnet group description" + subnets: [ + $(aws::subnet subnet-us-east-2a), + $(aws::subnet subnet-us-east-2b), + $(aws::subnet subnet-us-east-2c) + ] + + tags: { + Name: "db-subnet-group-example" + } +end + +aws::security-group security-group-example + name: "example-db-cluster-security-group" + vpc: $(aws::vpc vpc) + description: "Allow web traffic only" +end + +aws::security-group-rules security-group-example-rules + security-group: $(aws::security-group security-group-example) + keep-default-egress-rules: true + + @for port, type -in [80, 'http', 443, 'https', 999, 'foo'] + ingress + description: "allow inbound $(type) traffic, ipv4 only" + cidr-block: "0.0.0.0/0" + protocol: "TCP" + from-port: $(port) + to-port: $(port) + end + @end + + egress + description: "allow outbound http traffic, ipv6 only" + ipv6-cidr-block: "::/0" + protocol: "TCP" + from-port: 80 + to-port: 80 + end +end + +aws::kms-key aurora-master-password-encryption + description: "KMS key used to encrypt aurora database master password" + + aliases: ["alias/example/aurora-master-password-encryption"] + enabled: "true" +end + +aws::db-cluster db-cluster-example + identifier: "aurora-mysql-cluster" + engine: "aurora-mysql" + engine-version: "8.0.mysql_aurora.3.06.0" + availability-zones: ["us-east-2a", "us-east-2b", "us-east-2c"] + db-name: "clusterexample" + + master-username: "root" + manage-master-user-password: true + master-user-secret-kms-key: $(aws::kms-key aurora-master-password-encryption) + + storage-type: "aurora" + + engine-mode: "provisioned" + serverless-v2-scaling-configuration + min-capacity: 1 + max-capacity: 3 + end + + db-subnet-group: $(aws::db-subnet-group db-subnet-group) + vpc-security-groups: [ + $(aws::security-group security-group-example) + ] + backup-retention-period: 5 + preferred-backup-window: "07:00-09:00" + preferred-maintenance-window: "tue:15:00-tue:17:00" + skip-final-snapshot: true + enable-local-write-forwarding: false + + tags: { + Name: "aurora-mysql-cluster" + } + copy-tags-to-snapshot: true + storage-encrypted: true + kms-key: $(aws::kms-key aurora-master-password-encryption) + back-track-window: 0 + auto-minor-version-upgrade: true + deletion-protection: false +end + +aws::db-instance db-instance-example + identifier: "aurora-mysql-cluster-instance" + db-instance-class: "db.serverless" + db-cluster: $(aws::db-cluster db-cluster-example) + engine: "aurora-mysql" + apply-immediately: true + tags: { + Name: "aurora-mysql-cluster-instance" + } +end diff --git a/src/main/java/gyro/aws/rds/DbClusterResource.java b/src/main/java/gyro/aws/rds/DbClusterResource.java index a3fb5dff8..b74eb068a 100644 --- a/src/main/java/gyro/aws/rds/DbClusterResource.java +++ b/src/main/java/gyro/aws/rds/DbClusterResource.java @@ -41,11 +41,14 @@ import gyro.core.validation.Required; import gyro.core.validation.ValidStrings; import software.amazon.awssdk.services.rds.RdsClient; +import software.amazon.awssdk.services.rds.model.CreateDbClusterRequest; import software.amazon.awssdk.services.rds.model.CreateDbClusterResponse; import software.amazon.awssdk.services.rds.model.DBCluster; import software.amazon.awssdk.services.rds.model.DbClusterNotFoundException; import software.amazon.awssdk.services.rds.model.DescribeDbClustersResponse; import software.amazon.awssdk.services.rds.model.InvalidDbClusterStateException; +import software.amazon.awssdk.services.rds.model.LocalWriteForwardingStatus; +import software.amazon.awssdk.services.rds.model.ModifyDbClusterRequest; import software.amazon.awssdk.services.rds.model.RestoreDbClusterFromS3Response; import software.amazon.awssdk.services.rds.model.RestoreDbClusterFromSnapshotResponse; @@ -145,6 +148,7 @@ public class DbClusterResource extends RdsTaggableResource implements Copyable availabilityZones) { */ @Updatable public Long getBackTrackWindow() { + if (backTrackWindow == null) { + backTrackWindow = 0L; + } + return backTrackWindow; } @@ -659,6 +667,23 @@ public void setCopyTagsToSnapshot(Boolean copyTagsToSnapshot) { this.copyTagsToSnapshot = copyTagsToSnapshot; } + /** + * When set to ``true``, read replicas can forward write operations to the writer DB instance in the DB cluster. + * By default, write operations aren't allowed on reader DB instances. + */ + @Updatable + public Boolean getEnableLocalWriteForwarding() { + if (enableGlobalWriteForwarding == null) { + enableGlobalWriteForwarding = false; + } + + return enableLocalWriteForwarding; + } + + public void setEnableLocalWriteForwarding(Boolean enableLocalWriteForwarding) { + this.enableLocalWriteForwarding = enableLocalWriteForwarding; + } + /** * DNS hostname to access the primary instance of the cluster. */ @@ -753,6 +778,12 @@ public void copyFrom(DBCluster cluster) { config.copyFrom(cluster.serverlessV2ScalingConfiguration()); setServerlessV2ScalingConfiguration(config); } + + setEnableLocalWriteForwarding( + LocalWriteForwardingStatus.REQUESTED.equals(cluster.localWriteForwardingStatus()) || + LocalWriteForwardingStatus.ENABLING.equals(cluster.localWriteForwardingStatus()) || + LocalWriteForwardingStatus.ENABLED.equals(cluster.localWriteForwardingStatus()) + ); } @Override @@ -892,8 +923,13 @@ protected void doCreate(GyroUI ui, State state) { } } else { + CreateDbClusterRequest.Builder builder = CreateDbClusterRequest.builder(); + if (Boolean.TRUE.equals(getEnableGlobalWriteForwarding())) { + builder.enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()); + } + CreateDbClusterResponse response = client.createDBCluster( - r -> r.availabilityZones(getAvailabilityZones()) + builder.availabilityZones(getAvailabilityZones()) .backtrackWindow(getBackTrackWindow()) .backupRetentionPeriod(getBackupRetentionPeriod()) .characterSetName(getCharacterSetName()) @@ -925,7 +961,6 @@ protected void doCreate(GyroUI ui, State state) { .map(SecurityGroupResource::getId) .collect(Collectors.toList()) : null) .dbClusterInstanceClass(getDbClusterInstanceClass()) - .enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()) .iops(getIops()) .manageMasterUserPassword(getManageMasterUserPassword()) .masterUserSecretKmsKeyId(getMasterUserSecretKmsKey().getId()) @@ -935,6 +970,8 @@ protected void doCreate(GyroUI ui, State state) { .allocatedStorage(getAllocatedStorage()) .autoMinorVersionUpgrade(getAutoMinorVersionUpgrade()) .copyTagsToSnapshot(getCopyTagsToSnapshot()) + .enableLocalWriteForwarding(getEnableLocalWriteForwarding()) + .build() ); setArn(response.dbCluster().dbClusterArn()); @@ -981,8 +1018,13 @@ protected void doUpdate(Resource config, Set changedProperties) { .collect(Collectors.toList()) : null; try { + ModifyDbClusterRequest.Builder builder = ModifyDbClusterRequest.builder(); + if (!Objects.equals(getEnableGlobalWriteForwarding(), current.getEnableGlobalWriteForwarding())) { + builder.enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()); + } + client.modifyDBCluster( - r -> r.applyImmediately(getApplyImmediately()) + builder.applyImmediately(getApplyImmediately()) .backtrackWindow(Objects.equals(getBackTrackWindow(), current.getBackTrackWindow()) ? null : getBackTrackWindow()) .backupRetentionPeriod(Objects.equals( @@ -1015,7 +1057,6 @@ protected void doUpdate(Resource config, Set changedProperties) { .vpcSecurityGroupIds(Objects.equals(getVpcSecurityGroups(), current.getVpcSecurityGroups()) ? null : vpcSecurityGroupIds) .dbClusterInstanceClass(getDbClusterInstanceClass()) - .enableGlobalWriteForwarding(getEnableGlobalWriteForwarding()) .iops(getIops()) .manageMasterUserPassword(getManageMasterUserPassword()) .masterUserSecretKmsKeyId(getMasterUserSecretKmsKey().getId()) @@ -1025,6 +1066,9 @@ protected void doUpdate(Resource config, Set changedProperties) { .allocatedStorage(getAllocatedStorage()) .autoMinorVersionUpgrade(getAutoMinorVersionUpgrade()) .copyTagsToSnapshot(getCopyTagsToSnapshot()) + .enableLocalWriteForwarding(Objects.equals(getEnableLocalWriteForwarding(), + current.getEnableLocalWriteForwarding()) ? null : getEnableLocalWriteForwarding()) + .build() ); } catch (InvalidDbClusterStateException ex) { throw new GyroException(ex.getLocalizedMessage());