Skip to content

Commit

Permalink
Merge branch 'master' into ignore-failed-domain-on-delete
Browse files Browse the repository at this point in the history
  • Loading branch information
khebul authored Oct 31, 2023
2 parents 6494107 + 82e98c7 commit a8fde71
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 23 deletions.
4 changes: 4 additions & 0 deletions aws-rds-dbcluster/aws-rds-dbcluster.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
"type": "string"
}
},
"EnableGlobalWriteForwarding": {
"description": "Specifies whether to enable this DB cluster to forward write operations to the primary cluster of a global cluster (Aurora global database). By default, write operations are not allowed on Aurora DB clusters that are secondary clusters in an Aurora global database.",
"type": "boolean"
},
"EnableHttpEndpoint": {
"description": "A value that indicates whether to enable the HTTP endpoint for an Aurora Serverless DB cluster. By default, the HTTP endpoint is disabled.",
"type": "boolean"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.function.Predicate;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
Expand Down Expand Up @@ -56,6 +57,7 @@
import software.amazon.awssdk.services.rds.model.StorageTypeNotAvailableException;
import software.amazon.awssdk.services.rds.model.StorageTypeNotSupportedException;
import software.amazon.awssdk.services.rds.model.Tag;
import software.amazon.awssdk.services.rds.model.WriteForwardingStatus;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.cloudformation.exceptions.CfnNotStabilizedException;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
Expand Down Expand Up @@ -305,17 +307,25 @@ protected boolean isDBClusterStabilized(

return isDBClusterAvailable(dbCluster) &&
isNoPendingChanges(dbCluster) &&
isMasterUserSecretStabilized(dbCluster);
isMasterUserSecretStabilized(dbCluster) &&
isGlobalWriteForwardingStabilized(dbCluster);
}

private static boolean isMasterUserSecretStabilized(DBCluster dbCluster) {
protected static boolean isMasterUserSecretStabilized(DBCluster dbCluster) {
if (dbCluster.masterUserSecret() == null ||
CollectionUtils.isEmpty(dbCluster.dbClusterMembers())) {
return true;
}
return MASTER_USER_SECRET_ACTIVE.equalsIgnoreCase(dbCluster.masterUserSecret().secretStatus());
}

protected static boolean isGlobalWriteForwardingStabilized(DBCluster dbCluster) {
return BooleanUtils.isNotTrue(dbCluster.globalWriteForwardingRequested()) ||
// Even if GWF is requested the WF will not start until a replica is created by customers
(dbCluster.globalWriteForwardingStatus() != WriteForwardingStatus.ENABLING &&
dbCluster.globalWriteForwardingStatus() != WriteForwardingStatus.DISABLING);
}

protected boolean isClusterRemovedFromGlobalCluster(
final ProxyClient<RdsClient> proxyClient,
final String previousGlobalClusterIdentifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ static CreateDbClusterRequest createDbClusterRequest(
.domain(model.getDomain())
.domainIAMRoleName(model.getDomainIAMRoleName())
.enableCloudwatchLogsExports(model.getEnableCloudwatchLogsExports())
.enableGlobalWriteForwarding(model.getEnableGlobalWriteForwarding())
.enableHttpEndpoint(model.getEnableHttpEndpoint())
.enableIAMDatabaseAuthentication(model.getEnableIAMDatabaseAuthentication())
.enablePerformanceInsights(model.getPerformanceInsightsEnabled())
Expand Down Expand Up @@ -212,6 +213,7 @@ static ModifyDbClusterRequest modifyDbClusterAfterCreateRequest(final ResourceMo
.deletionProtection(desiredModel.getDeletionProtection())
.domain(desiredModel.getDomain())
.domainIAMRoleName(desiredModel.getDomainIAMRoleName())
.enableGlobalWriteForwarding(desiredModel.getEnableGlobalWriteForwarding())
.enableHttpEndpoint(desiredModel.getEnableHttpEndpoint())
.enablePerformanceInsights(desiredModel.getPerformanceInsightsEnabled())
.iops(desiredModel.getIops())
Expand Down Expand Up @@ -262,6 +264,7 @@ static ModifyDbClusterRequest modifyDbClusterRequest(
.deletionProtection(desiredModel.getDeletionProtection())
.domain(desiredModel.getDomain())
.domainIAMRoleName(desiredModel.getDomainIAMRoleName())
.enableGlobalWriteForwarding(desiredModel.getEnableGlobalWriteForwarding())
.enableHttpEndpoint(desiredModel.getEnableHttpEndpoint())
.enableIAMDatabaseAuthentication(diff(previousModel.getEnableIAMDatabaseAuthentication(), desiredModel.getEnableIAMDatabaseAuthentication()))
.enablePerformanceInsights(desiredModel.getPerformanceInsightsEnabled())
Expand Down Expand Up @@ -506,6 +509,7 @@ public static ResourceModel translateDbClusterFromSdk(
.domain(domain)
.domainIAMRoleName(domainIAMRoleName)
.enableCloudwatchLogsExports(dbCluster.enabledCloudwatchLogsExports())
.enableGlobalWriteForwarding(dbCluster.globalWriteForwardingRequested())
.enableHttpEndpoint(dbCluster.httpEndpointEnabled())
.enableIAMDatabaseAuthentication(dbCluster.iamDatabaseAuthenticationEnabled())
.endpoint(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package software.amazon.rds.dbcluster;

import java.time.Instant;
import java.util.Collections;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.apache.commons.collections.CollectionUtils;

import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.rds.RdsClient;
import software.amazon.awssdk.services.rds.model.DBCluster;
import software.amazon.awssdk.services.rds.model.MasterUserSecret;
import software.amazon.awssdk.services.rds.model.WriteForwardingStatus;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.ProgressEvent;
import software.amazon.cloudformation.proxy.ProxyClient;
import software.amazon.cloudformation.proxy.ResourceHandlerRequest;
import software.amazon.rds.common.handler.HandlerConfig;
import software.amazon.rds.common.logging.RequestLogger;
import software.amazon.rds.common.request.RequestValidationException;
import software.amazon.rds.common.request.ValidatedRequest;

class BaseHandlerStdTest {

static class TestBaseHandlerStd extends BaseHandlerStd {

public TestBaseHandlerStd(HandlerConfig config) {
super(config);
}

@Override
protected ProgressEvent<ResourceModel, CallbackContext> handleRequest(
final AmazonWebServicesClientProxy proxy,
final ValidatedRequest<ResourceModel> request,
final CallbackContext callbackContext,
final ProxyClient<RdsClient> rdsProxyClient,
final ProxyClient<Ec2Client> ec2ProxyClient,
final RequestLogger logger
) {
return null;
}
}

private TestBaseHandlerStd handler;

@BeforeEach
public void setUp() {
handler = new TestBaseHandlerStd(null);
}

@Test
void isMasterUserSecretStabilized_masterUserSecretIsNull() {
Assertions.assertThat(handler.isMasterUserSecretStabilized(
DBCluster.builder()
.build()
)).isTrue();
}

@Test
void isMasterUserSecretStabilized_masterUserSecretStatusActive() {
Assertions.assertThat(handler.isMasterUserSecretStabilized(
DBCluster.builder()
.masterUserSecret(MasterUserSecret.builder()
.secretStatus("Active")
.build())
.build()
)).isTrue();
}

@Test
void isMasterUserSecretStabilized_masterUserSecretStatusCreatingWithEmptyMembers() {
DBCluster dbCluster = DBCluster.builder()
.masterUserSecret(MasterUserSecret.builder()
.secretStatus("Creating")
.build())
.build();
Assertions.assertThat(CollectionUtils.isEmpty(dbCluster.dbClusterMembers()));
Assertions.assertThat(handler.isMasterUserSecretStabilized(dbCluster)).isTrue();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingNotRequested() {
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.build()
)).isTrue();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingRequested() {
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.globalWriteForwardingRequested(true)
.build()
)).isTrue();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingEnabled() {
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.globalWriteForwardingRequested(true)
.globalWriteForwardingStatus(WriteForwardingStatus.ENABLED)
.build()
)).isTrue();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingEnabling() {
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.globalWriteForwardingRequested(true)
.globalWriteForwardingStatus(WriteForwardingStatus.ENABLING)
.build()
)).isFalse();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingDisabled() {
// GlobalWriteForwarding status will not enable until a replica is requested by customer
// This prevents customers from creating a stack with only primary and setting the property
// As WS does not validate this parameter the stack will wait on stabilization until timeout.
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.globalWriteForwardingRequested(true)
.globalWriteForwardingStatus(WriteForwardingStatus.DISABLED)
.build()
)).isTrue();
}

@Test
void isGlobalWriteForwardingStabilized_globalWriteForwardingDisabling() {
Assertions.assertThat(handler.isGlobalWriteForwardingStabilized(
DBCluster.builder()
.globalWriteForwardingRequested(true)
.globalWriteForwardingStatus(WriteForwardingStatus.DISABLING)
.build()
)).isFalse();
}


@Test
void validateRequest_BlankRegionIsAccepted() {
final ResourceHandlerRequest<ResourceModel> request = new ResourceHandlerRequest<>();
request.setDesiredResourceState(ResourceModel.builder()
.sourceRegion("")
.build());
Assertions.assertThatCode(() -> {
handler.validateRequest(request);
}).doesNotThrowAnyException();
}

@Test
void validateRequest_UnknownRegionIsRejected() {
final ResourceHandlerRequest<ResourceModel> request = new ResourceHandlerRequest<>();
request.setDesiredResourceState(ResourceModel.builder()
.sourceRegion("foo-bar-baz")
.build());
Assertions.assertThatExceptionOfType(RequestValidationException.class).isThrownBy(() -> {
handler.validateRequest(request);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.rds.RdsClient;
import software.amazon.awssdk.services.rds.model.DBCluster;
import software.amazon.awssdk.services.rds.model.CreateDbClusterRequest;
import software.amazon.awssdk.services.rds.model.DomainMembership;
import software.amazon.awssdk.services.rds.model.ModifyDbClusterRequest;
import software.amazon.awssdk.services.rds.model.RestoreDbClusterFromSnapshotRequest;
Expand All @@ -27,6 +28,14 @@ public class TranslatorTest extends AbstractHandlerTest {
private final static String STORAGE_TYPE_AURORA_IOPT1 = "aurora-opt1";


@Test
public void createDbClusterRequest_enableGlobalWriteForwarding() {
final ResourceModel model = RESOURCE_MODEL.toBuilder().enableGlobalWriteForwarding(true).build();

final CreateDbClusterRequest request = Translator.createDbClusterRequest(model, Tagging.TagSet.emptySet());
assertThat(request.enableGlobalWriteForwarding()).isEqualTo(Boolean.TRUE);
}

@Test
public void modifyDbClusterRequest_omitPreferredMaintenanceWindowIfUnchanged() {
final ResourceModel model = RESOURCE_MODEL.toBuilder().preferredMaintenanceWindow("old").build();
Expand Down Expand Up @@ -85,6 +94,16 @@ public void modifyDbClusterRequest_setEnableIAMDatabaseAuthentication() {
assertThat(request.enableIAMDatabaseAuthentication()).isEqualTo(Boolean.TRUE);
}

@Test
public void modifyDbClusterRequest_setEnableGlobalWriteForwarding() {
final ResourceModel previousModel = RESOURCE_MODEL.toBuilder().enableGlobalWriteForwarding(false).build();
final ResourceModel desiredModel = RESOURCE_MODEL.toBuilder().enableGlobalWriteForwarding(true).build();
final Boolean isRollback = false;

final ModifyDbClusterRequest request = Translator.modifyDbClusterRequest(previousModel, desiredModel, isRollback);
assertThat(request.enableGlobalWriteForwarding()).isEqualTo(Boolean.TRUE);
}

@Test
public void ModifyDbClusterRequest_dbInstanceParameterGroupNameIsNotSetWhenEngineVersionIsNotUpgrading() {
final ResourceModel previousModel = RESOURCE_MODEL.toBuilder().engineVersion("old-engine").dBInstanceParameterGroupName("old-pg").build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,7 +1105,7 @@ protected ProgressEvent<ResourceModel, CallbackContext> stopAutomaticBackupRepli
final ProxyClient<RdsClient> sourceRegionClient,
final String region
) {
final ProxyClient<RdsClient> rdsClient = proxy.newProxy(() -> new RdsClientProvider().getClientForRegion(region));
final ProxyClient<RdsClient> rdsClient = new LoggingProxyClient<>(logger, proxy.newProxy(() -> new RdsClientProvider().getClientForRegion(region)));

return proxy.initiate("rds::stop-db-instance-automatic-backup-replication", rdsClient, progress.getResourceModel(), progress.getCallbackContext())
.translateToServiceRequest(resourceModel -> Translator.stopDbInstanceAutomatedBackupsReplicationRequest(dbInstanceArn))
Expand All @@ -1131,7 +1131,7 @@ protected ProgressEvent<ResourceModel, CallbackContext> startAutomaticBackupRepl
final ProxyClient<RdsClient> sourceRegionClient,
final String region
) {
final ProxyClient<RdsClient> rdsClient = proxy.newProxy(() -> new RdsClientProvider().getClientForRegion(region));
final ProxyClient<RdsClient> rdsClient = new LoggingProxyClient<>(logger, proxy.newProxy(() -> new RdsClientProvider().getClientForRegion(region)));

return proxy.initiate("rds::start-db-instance-automatic-backup-replication", rdsClient, progress.getResourceModel(), progress.getCallbackContext())
.translateToServiceRequest(resourceModel -> Translator.startDbInstanceAutomatedBackupsReplicationRequest(dbInstanceArn))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class CallbackContext extends StdCallbackContext implements TaggingContex
private boolean automaticBackupReplicationStopped;
private boolean automaticBackupReplicationStarted;
private String dbInstanceArn;
private String currentRegion;

private TaggingContext taggingContext;
private Map<String, Long> timestamps;
Expand Down
Loading

0 comments on commit a8fde71

Please sign in to comment.