From d795e8773f1e454d394a08ca52958a62fb94e224 Mon Sep 17 00:00:00 2001 From: Keynan Pratt Date: Tue, 5 Nov 2024 05:01:09 +0000 Subject: [PATCH 1/2] Stamping the target. --- .../rds/dbinstance/DBInstancePredicates.java | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java new file mode 100644 index 000000000..3583ea129 --- /dev/null +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java @@ -0,0 +1,261 @@ +package software.amazon.rds.dbinstance; + +import com.amazonaws.arn.Arn; +import com.amazonaws.util.CollectionUtils; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; +import software.amazon.awssdk.services.rds.model.DBCluster; +import software.amazon.awssdk.services.rds.model.DBInstance; +import software.amazon.awssdk.services.rds.model.DomainMembership; +import software.amazon.awssdk.services.rds.model.OptionGroupMembership; +import software.amazon.awssdk.services.rds.model.PendingModifiedValues; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.cloudformation.exceptions.CfnNotStabilizedException; +import software.amazon.rds.common.logging.RequestLogger; +import software.amazon.rds.dbinstance.status.DBInstanceStatus; +import software.amazon.rds.dbinstance.status.DBParameterGroupStatus; +import software.amazon.rds.dbinstance.status.DomainMembershipStatus; +import software.amazon.rds.dbinstance.status.OptionGroupStatus; +import software.amazon.rds.dbinstance.status.ReadReplicaStatus; +import software.amazon.rds.dbinstance.status.VPCSecurityGroupStatus; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DBInstancePredicates { + + private static final String SECRET_STATUS_ACTIVE = "active"; + private static final List RDS_CUSTOM_ORACLE_ENGINES = ImmutableList.of( + "custom-oracle-ee", + "custom-oracle-ee-cdb" + ); + private static final String READ_REPLICA_STATUS_TYPE = "read replication"; + + public static void assertNoDBInstanceTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { + final DBInstanceStatus status = DBInstanceStatus.fromString(dbInstance.dbInstanceStatus()); + if (status != null && status.isTerminal()) { + throw new CfnNotStabilizedException(new Exception("DB Instance is in state: " + status.toString())); + } + } + + public static void assertNoOptionGroupTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { + final List termOptionGroups = Optional.ofNullable(dbInstance.optionGroupMemberships()).orElse(Collections.emptyList()) + .stream() + .filter(optionGroup -> { + final OptionGroupStatus status = OptionGroupStatus.fromString(optionGroup.status()); + return status != null && status.isTerminal(); + }) + .collect(Collectors.toList()); + + if (!termOptionGroups.isEmpty()) { + throw new CfnNotStabilizedException(new Exception( + String.format("OptionGroup %s is in a terminal state", + termOptionGroups.get(0).optionGroupName()))); + } + } + + public static void assertNoDomainMembershipTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { + final List terminalDomainMemberships = Optional.ofNullable(dbInstance.domainMemberships()).orElse(Collections.emptyList()) + .stream() + .filter(domainMembership -> { + final DomainMembershipStatus status = DomainMembershipStatus.fromString(domainMembership.status()); + return status != null && status.isTerminal(); + }) + .collect(Collectors.toList()); + + if (!terminalDomainMemberships.isEmpty()) { + throw new CfnNotStabilizedException(new Exception(String.format("Domain %s is in a terminal state", + terminalDomainMemberships.get(0).domain()))); + } + } + + public static void assertNoTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { + assertNoDBInstanceTerminalStatus(dbInstance); + assertNoOptionGroupTerminalStatus(dbInstance); + assertNoDomainMembershipTerminalStatus(dbInstance); + } + + public static boolean isInstanceStabilizedAfterReplicationStop( + final DBInstance dbInstance, + final ResourceModel model + ) { + assertNoTerminalStatus(dbInstance); + return isDBInstanceAvailable(dbInstance) + && !dbInstance.hasDbInstanceAutomatedBackupsReplications(); + } + + public static boolean isDBInstanceAvailable(final DBInstance dbInstance) { + return DBInstanceStatus.Available.equalsString(dbInstance.dbInstanceStatus()); + } + + public static boolean isDomainMembershipsJoined(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.domainMemberships()).orElse(Collections.emptyList()) + .stream() + .allMatch(membership -> DomainMembershipStatus.Joined.equalsString(membership.status()) || + DomainMembershipStatus.KerberosEnabled.equalsString(membership.status())); + } + + public static boolean isVpcSecurityGroupsActive(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.vpcSecurityGroups()).orElse(Collections.emptyList()) + .stream() + .allMatch(group -> VPCSecurityGroupStatus.Active.equalsString(group.status())); + } + + public static boolean isNoPendingChanges(final DBInstance dbInstance) { + final PendingModifiedValues pending = dbInstance.pendingModifiedValues(); + return (pending == null) || (pending.dbInstanceClass() == null && + pending.allocatedStorage() == null && + pending.automationMode() == null && + pending.backupRetentionPeriod() == null && + pending.dbInstanceIdentifier() == null && + pending.dbSubnetGroupName() == null && + pending.engine() == null && + pending.engineVersion() == null && + pending.iamDatabaseAuthenticationEnabled() == null && + pending.iops() == null && + pending.licenseModel() == null && + pending.masterUserPassword() == null && + pending.multiAZ() == null && + pending.pendingCloudwatchLogsExports() == null && + pending.port() == null && + CollectionUtils.isNullOrEmpty(pending.processorFeatures()) && + pending.resumeFullAutomationModeTime() == null && + pending.storageThroughput() == null && + pending.storageType() == null + ); + } + + public static boolean isCaCertificateChangesApplied(final DBInstance dbInstance, final ResourceModel model) { + final PendingModifiedValues pending = dbInstance.pendingModifiedValues(); + return pending == null || + pending.caCertificateIdentifier() == null || + BooleanUtils.isNotTrue(model.getCertificateRotationRestart()); + } + + public static boolean isDBParameterGroupNotApplying(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.dbParameterGroups()).orElse(Collections.emptyList()) + .stream() + .noneMatch(group -> DBParameterGroupStatus.Applying.equalsString(group.parameterApplyStatus())); + } + + public static boolean isReplicationComplete(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.statusInfos()).orElse(Collections.emptyList()) + .stream() + .filter(statusInfo -> READ_REPLICA_STATUS_TYPE.equals(statusInfo.statusType())) + .allMatch(statusInfo -> ReadReplicaStatus.Replicating.equalsString(statusInfo.status())); + } + + public static boolean isDBClusterParameterGroupInSync(final ResourceModel model, final DBCluster dbCluster) { + return Optional.ofNullable(dbCluster.dbClusterMembers()).orElse(Collections.emptyList()) + .stream() + .filter(member -> model.getDBInstanceIdentifier().equalsIgnoreCase(member.dbInstanceIdentifier())) + .anyMatch(member -> DBParameterGroupStatus.InSync.equalsString(member.dbClusterParameterGroupStatus())); + } + + public static boolean isDBClusterMember(final ResourceModel model) { + return StringUtils.isNotBlank(model.getDBClusterIdentifier()); + } + + public static boolean isRdsCustomOracleInstance(final ResourceModel model) { + return RDS_CUSTOM_ORACLE_ENGINES.contains(model.getEngine()); + } + + public static boolean isOptionGroupInSync(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.optionGroupMemberships()).orElse(Collections.emptyList()) + .stream() + .allMatch(optionGroup -> OptionGroupStatus.InSync.equalsString(optionGroup.status())); + } + + public static boolean isDBParameterGroupInSync(final DBInstance dbInstance) { + return Optional.ofNullable(dbInstance.dbParameterGroups()).orElse(Collections.emptyList()) + .stream() + .allMatch(parameterGroup -> DBParameterGroupStatus.InSync.equalsString(parameterGroup.parameterApplyStatus())); + } + + public static boolean isMasterUserSecretStabilized(final DBInstance instance) { + if (instance.masterUserSecret() == null) { + return true; + } + return SECRET_STATUS_ACTIVE.equalsIgnoreCase(instance.masterUserSecret().secretStatus()); + } + + public static boolean isDBInstanceStabilizedAfterMutate( + final DBInstance dbInstance, + final ResourceModel model, + final CallbackContext context, + final RequestLogger requestLogger + ) { + assertNoTerminalStatus(dbInstance); + + final boolean isDBInstanceStabilizedAfterMutateResult = isDBInstanceAvailable(dbInstance) && + isReplicationComplete(dbInstance) && + isDBParameterGroupNotApplying(dbInstance) && + isNoPendingChanges(dbInstance) && + isCaCertificateChangesApplied(dbInstance, model) && + isVpcSecurityGroupsActive(dbInstance) && + isDomainMembershipsJoined(dbInstance) && + isMasterUserSecretStabilized(dbInstance); + + requestLogger.log(String.format("isDBInstanceStabilizedAfterMutate: %b", isDBInstanceStabilizedAfterMutateResult), + ImmutableMap.of("isDBInstanceAvailable", isDBInstanceAvailable(dbInstance), + "isReplicationComplete", isReplicationComplete(dbInstance), + "isDBParameterGroupNotApplying", isDBParameterGroupNotApplying(dbInstance), + "isNoPendingChanges", isNoPendingChanges(dbInstance), + "isCaCertificateChangesApplied", isCaCertificateChangesApplied(dbInstance, model), + "isVpcSecurityGroupsActive", isVpcSecurityGroupsActive(dbInstance), + "isDomainMembershipsJoined", isDomainMembershipsJoined(dbInstance), + "isMasterUserSecretStabilized", isMasterUserSecretStabilized(dbInstance)), + ImmutableMap.of("Description", "isDBInstanceStabilizedAfterMutate method will be repeatedly" + + " called with a backoff mechanism after the modify call until it returns true. This" + + " process will continue until all included flags are true.")); + + return isDBInstanceStabilizedAfterMutateResult; + } + + public static boolean isDBInstanceStabilizedAfterReboot( + final DBInstance dbInstance, + final Optional maybeDbCluster, + final ResourceModel model, + final RequestLogger requestLogger + ) { + assertNoTerminalStatus(dbInstance); + + final boolean isDBClusterParameterGroupStabilized = maybeDbCluster + .map(dbCluster -> isDBClusterParameterGroupInSync(model, dbCluster)) + .orElseGet(() -> !DBInstancePredicates.isDBClusterMember(model)); + final boolean isDBInstanceStabilizedAfterReboot = isDBInstanceAvailable(dbInstance) && + isDBParameterGroupInSync(dbInstance) && + isOptionGroupInSync(dbInstance) && + isDBClusterParameterGroupStabilized; + + requestLogger.log(String.format("isDBInstanceStabilizedAfterReboot: %b", isDBInstanceStabilizedAfterReboot), + ImmutableMap.of("isDBInstanceAvailable", isDBInstanceAvailable(dbInstance), + "isDBParameterGroupInSync", isDBParameterGroupInSync(dbInstance), + "isOptionGroupInSync", isOptionGroupInSync(dbInstance), + "isDBClusterParameterGroupStabilized", isDBClusterParameterGroupStabilized), + ImmutableMap.of("Description", "isDBInstanceStabilizedAfterReboot method will be repeatedly" + + " called with a backoff mechanism after the reboot call until it returns true. This" + + " process will continue until all included flags are true.")); + + return isDBInstanceStabilizedAfterReboot; + } + + public static boolean isInstanceStabilizedAfterReplicationStart( + final DBInstance dbInstance, + final ResourceModel model + ) { + assertNoTerminalStatus(dbInstance); + return isDBInstanceAvailable(dbInstance) + && dbInstance.hasDbInstanceAutomatedBackupsReplications() && + !dbInstance.dbInstanceAutomatedBackupsReplications().isEmpty() && + model.getAutomaticBackupReplicationRegion() + .equalsIgnoreCase( + Arn.fromString(dbInstance.dbInstanceAutomatedBackupsReplications().get(0).dbInstanceAutomatedBackupsArn()).getRegion()); + } +} From 15a16437e230bc31087f830d3c4268de3d71abf7 Mon Sep 17 00:00:00 2001 From: Keynan Pratt Date: Tue, 5 Nov 2024 05:51:47 +0000 Subject: [PATCH 2/2] [Refactoring] Decomposing DBInstance BaseHandlerStd in advance of expanding the DBInstance type. --- .../amazon/rds/dbinstance/BaseHandlerStd.java | 328 ++---------------- .../amazon/rds/dbinstance/CreateHandler.java | 2 +- .../rds/dbinstance/DBInstancePredicates.java | 24 +- .../amazon/rds/dbinstance/DeleteHandler.java | 2 +- .../amazon/rds/dbinstance/UpdateHandler.java | 63 ++-- .../rds/dbinstance/BaseHandlerStdTest.java | 80 ++--- 6 files changed, 121 insertions(+), 378 deletions(-) diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/BaseHandlerStd.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/BaseHandlerStd.java index 7d8b55c0d..08b87da05 100644 --- a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/BaseHandlerStd.java +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/BaseHandlerStd.java @@ -1,133 +1,46 @@ package software.amazon.rds.dbinstance; -import java.time.Duration; -import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.BooleanUtils; - import com.amazonaws.arn.Arn; -import com.amazonaws.util.CollectionUtils; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.BooleanUtils; import software.amazon.awssdk.services.ec2.Ec2Client; import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsResponse; import software.amazon.awssdk.services.ec2.model.SecurityGroup; import software.amazon.awssdk.services.rds.RdsClient; -import software.amazon.awssdk.services.rds.model.AuthorizationNotFoundException; -import software.amazon.awssdk.services.rds.model.CertificateNotFoundException; -import software.amazon.awssdk.services.rds.model.DBCluster; -import software.amazon.awssdk.services.rds.model.DBClusterSnapshot; -import software.amazon.awssdk.services.rds.model.DBInstance; -import software.amazon.awssdk.services.rds.model.DBInstanceAutomatedBackup; -import software.amazon.awssdk.services.rds.model.DBSnapshot; -import software.amazon.awssdk.services.rds.model.DbClusterNotFoundException; -import software.amazon.awssdk.services.rds.model.DbClusterSnapshotNotFoundException; -import software.amazon.awssdk.services.rds.model.DbInstanceAlreadyExistsException; -import software.amazon.awssdk.services.rds.model.DbInstanceAutomatedBackupQuotaExceededException; -import software.amazon.awssdk.services.rds.model.DbInstanceNotFoundException; -import software.amazon.awssdk.services.rds.model.DbInstanceRoleAlreadyExistsException; -import software.amazon.awssdk.services.rds.model.DbInstanceRoleNotFoundException; -import software.amazon.awssdk.services.rds.model.DbParameterGroupNotFoundException; -import software.amazon.awssdk.services.rds.model.DbSecurityGroupNotFoundException; -import software.amazon.awssdk.services.rds.model.DbSnapshotAlreadyExistsException; -import software.amazon.awssdk.services.rds.model.DbSnapshotNotFoundException; -import software.amazon.awssdk.services.rds.model.DbSubnetGroupDoesNotCoverEnoughAZsException; -import software.amazon.awssdk.services.rds.model.DbSubnetGroupNotFoundException; -import software.amazon.awssdk.services.rds.model.DbUpgradeDependencyFailureException; -import software.amazon.awssdk.services.rds.model.DescribeDbClusterSnapshotsResponse; -import software.amazon.awssdk.services.rds.model.DescribeDbClustersResponse; -import software.amazon.awssdk.services.rds.model.DescribeDbInstanceAutomatedBackupsResponse; -import software.amazon.awssdk.services.rds.model.DescribeDbInstancesResponse; -import software.amazon.awssdk.services.rds.model.DescribeDbSnapshotsResponse; -import software.amazon.awssdk.services.rds.model.DomainMembership; -import software.amazon.awssdk.services.rds.model.DomainNotFoundException; -import software.amazon.awssdk.services.rds.model.Event; -import software.amazon.awssdk.services.rds.model.InstanceQuotaExceededException; -import software.amazon.awssdk.services.rds.model.InsufficientDbInstanceCapacityException; -import software.amazon.awssdk.services.rds.model.InvalidDbClusterStateException; -import software.amazon.awssdk.services.rds.model.InvalidDbInstanceAutomatedBackupStateException; -import software.amazon.awssdk.services.rds.model.InvalidDbInstanceStateException; -import software.amazon.awssdk.services.rds.model.InvalidDbSecurityGroupStateException; -import software.amazon.awssdk.services.rds.model.InvalidDbSnapshotStateException; -import software.amazon.awssdk.services.rds.model.InvalidRestoreException; -import software.amazon.awssdk.services.rds.model.InvalidSubnetException; -import software.amazon.awssdk.services.rds.model.InvalidVpcNetworkStateException; -import software.amazon.awssdk.services.rds.model.KmsKeyNotAccessibleException; -import software.amazon.awssdk.services.rds.model.NetworkTypeNotSupportedException; -import software.amazon.awssdk.services.rds.model.OptionGroupMembership; -import software.amazon.awssdk.services.rds.model.OptionGroupNotFoundException; -import software.amazon.awssdk.services.rds.model.PendingModifiedValues; -import software.amazon.awssdk.services.rds.model.ProvisionedIopsNotAvailableInAzException; -import software.amazon.awssdk.services.rds.model.SnapshotQuotaExceededException; -import software.amazon.awssdk.services.rds.model.StorageQuotaExceededException; -import software.amazon.awssdk.services.rds.model.StorageTypeNotSupportedException; import software.amazon.awssdk.services.rds.model.Tag; +import software.amazon.awssdk.services.rds.model.*; import software.amazon.awssdk.utils.StringUtils; import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; -import software.amazon.cloudformation.exceptions.CfnNotStabilizedException; -import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; -import software.amazon.cloudformation.proxy.HandlerErrorCode; -import software.amazon.cloudformation.proxy.Logger; -import software.amazon.cloudformation.proxy.OperationStatus; -import software.amazon.cloudformation.proxy.ProgressEvent; -import software.amazon.cloudformation.proxy.ProxyClient; -import software.amazon.cloudformation.proxy.ResourceHandlerRequest; +import software.amazon.cloudformation.proxy.*; import software.amazon.cloudformation.proxy.delay.Constant; import software.amazon.cloudformation.resource.ResourceTypeSchema; import software.amazon.rds.common.error.ErrorCode; import software.amazon.rds.common.error.ErrorRuleSet; import software.amazon.rds.common.error.ErrorStatus; -import software.amazon.rds.common.handler.Commons; -import software.amazon.rds.common.handler.Events; -import software.amazon.rds.common.handler.HandlerConfig; -import software.amazon.rds.common.handler.HandlerMethod; -import software.amazon.rds.common.handler.Tagging; +import software.amazon.rds.common.handler.*; import software.amazon.rds.common.logging.LoggingProxyClient; import software.amazon.rds.common.logging.RequestLogger; import software.amazon.rds.common.printer.FilteredJsonPrinter; import software.amazon.rds.common.request.RequestValidationException; import software.amazon.rds.common.request.ValidatedRequest; import software.amazon.rds.common.request.Validations; -import software.amazon.rds.dbinstance.client.ApiVersion; -import software.amazon.rds.dbinstance.client.ApiVersionDispatcher; -import software.amazon.rds.dbinstance.client.Ec2ClientProvider; -import software.amazon.rds.dbinstance.client.RdsClientProvider; -import software.amazon.rds.dbinstance.client.VersionedProxyClient; -import software.amazon.rds.dbinstance.status.DBInstanceStatus; -import software.amazon.rds.dbinstance.status.DBParameterGroupStatus; -import software.amazon.rds.dbinstance.status.DomainMembershipStatus; -import software.amazon.rds.dbinstance.status.OptionGroupStatus; -import software.amazon.rds.dbinstance.status.ReadReplicaStatus; -import software.amazon.rds.dbinstance.status.VPCSecurityGroupStatus; +import software.amazon.rds.dbinstance.client.*; + +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; public abstract class BaseHandlerStd extends BaseHandler { - public static final String SECRET_STATUS_ACTIVE = "active"; public static final String RESOURCE_IDENTIFIER = "dbinstance"; public static final String STACK_NAME = "rds"; public static final String API_VERSION_V12 = "2012-09-17"; - static final String READ_REPLICA_STATUS_TYPE = "read replication"; - - protected static final List RDS_CUSTOM_ORACLE_ENGINES = ImmutableList.of( - "custom-oracle-ee", - "custom-oracle-ee-cdb" - ); - protected static final int RESOURCE_ID_MAX_LENGTH = 63; protected final static HandlerConfig DEFAULT_DB_INSTANCE_HANDLER_CONFIG = HandlerConfig.builder() @@ -496,14 +409,6 @@ protected ProgressEvent updateDbInstance( .progress(); } - protected boolean isDBClusterMember(final ResourceModel model) { - return StringUtils.isNotBlank(model.getDBClusterIdentifier()); - } - - protected boolean isRdsCustomOracleInstance(final ResourceModel model) { - return RDS_CUSTOM_ORACLE_ENGINES.contains(model.getEngine()); - } - protected boolean isFailureEvent(final Event event) { return EVENT_FAIL_CHECKERS.stream().anyMatch(p -> p.test(event)); } @@ -627,50 +532,6 @@ protected boolean isDbInstanceDeleted( return false; } - private void assertNoDBInstanceTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { - final DBInstanceStatus status = DBInstanceStatus.fromString(dbInstance.dbInstanceStatus()); - if (status != null && status.isTerminal()) { - throw new CfnNotStabilizedException(new Exception("DB Instance is in state: " + status.toString())); - } - } - - private void assertNoOptionGroupTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { - final List termOptionGroups = Optional.ofNullable(dbInstance.optionGroupMemberships()).orElse(Collections.emptyList()) - .stream() - .filter(optionGroup -> { - final OptionGroupStatus status = OptionGroupStatus.fromString(optionGroup.status()); - return status != null && status.isTerminal(); - }) - .collect(Collectors.toList()); - - if (!termOptionGroups.isEmpty()) { - throw new CfnNotStabilizedException(new Exception( - String.format("OptionGroup %s is in a terminal state", - termOptionGroups.get(0).optionGroupName()))); - } - } - - private void assertNoDomainMembershipTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { - final List terminalDomainMemberships = Optional.ofNullable(dbInstance.domainMemberships()).orElse(Collections.emptyList()) - .stream() - .filter(domainMembership -> { - final DomainMembershipStatus status = DomainMembershipStatus.fromString(domainMembership.status()); - return status != null && status.isTerminal(); - }) - .collect(Collectors.toList()); - - if (!terminalDomainMemberships.isEmpty()) { - throw new CfnNotStabilizedException(new Exception(String.format("Domain %s is in a terminal state", - terminalDomainMemberships.get(0).domain()))); - } - } - - private void assertNoTerminalStatus(final DBInstance dbInstance) throws CfnNotStabilizedException { - assertNoDBInstanceTerminalStatus(dbInstance); - assertNoOptionGroupTerminalStatus(dbInstance); - assertNoDomainMembershipTerminalStatus(dbInstance); - } - protected boolean isDBInstanceStabilizedAfterMutate( final ProxyClient rdsProxyClient, final ResourceModel model, @@ -678,32 +539,7 @@ protected boolean isDBInstanceStabilizedAfterMutate( ) { final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - assertNoTerminalStatus(dbInstance); - - - final boolean isDBInstanceStabilizedAfterMutateResult = isDBInstanceAvailable(dbInstance) && - isReplicationComplete(dbInstance) && - isDBParameterGroupNotApplying(dbInstance) && - isNoPendingChanges(dbInstance) && - isCaCertificateChangesApplied(dbInstance, model) && - isVpcSecurityGroupsActive(dbInstance) && - isDomainMembershipsJoined(dbInstance) && - isMasterUserSecretStabilized(dbInstance); - - requestLogger.log(String.format("isDBInstanceStabilizedAfterMutate: %b", isDBInstanceStabilizedAfterMutateResult), - ImmutableMap.of("isDBInstanceAvailable", isDBInstanceAvailable(dbInstance), - "isReplicationComplete", isReplicationComplete(dbInstance), - "isDBParameterGroupNotApplying", isDBParameterGroupNotApplying(dbInstance), - "isNoPendingChanges", isNoPendingChanges(dbInstance), - "isCaCertificateChangesApplied", isCaCertificateChangesApplied(dbInstance, model), - "isVpcSecurityGroupsActive", isVpcSecurityGroupsActive(dbInstance), - "isDomainMembershipsJoined", isDomainMembershipsJoined(dbInstance), - "isMasterUserSecretStabilized", isMasterUserSecretStabilized(dbInstance)), - ImmutableMap.of("Description", "isDBInstanceStabilizedAfterMutate method will be repeatedly" + - " called with a backoff mechanism after the modify call until it returns true. This" + - " process will continue until all included flags are true.")); - - return isDBInstanceStabilizedAfterMutateResult; + return DBInstancePredicates.isDBInstanceStabilizedAfterMutate(dbInstance, model, context, requestLogger); } private void resourceStabilizationTime(final CallbackContext context) { @@ -712,26 +548,20 @@ private void resourceStabilizationTime(final CallbackContext context) { context.calculateTimeDeltaInMinutes(DB_INSTANCE_STABILIZATION_TIME, context.getTimestamp(DB_INSTANCE_REQUEST_IN_PROGRESS_AT), context.getTimestamp(DB_INSTANCE_REQUEST_STARTED_AT)); } - protected boolean isInstanceStabilizedAfterReplicationStop(final ProxyClient rdsProxyClient, - final ResourceModel model) { + protected boolean isInstanceStabilizedAfterReplicationStop( + final ProxyClient rdsProxyClient, + final ResourceModel model + ) { final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - assertNoTerminalStatus(dbInstance); - return isDBInstanceAvailable(dbInstance) - && !dbInstance.hasDbInstanceAutomatedBackupsReplications(); + return DBInstancePredicates.isInstanceStabilizedAfterReplicationStop(dbInstance, model); } protected boolean isInstanceStabilizedAfterReplicationStart(final ProxyClient rdsProxyClient, final ResourceModel model) { final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - assertNoTerminalStatus(dbInstance); - return isDBInstanceAvailable(dbInstance) - && dbInstance.hasDbInstanceAutomatedBackupsReplications() && - !dbInstance.dbInstanceAutomatedBackupsReplications().isEmpty() && - model.getAutomaticBackupReplicationRegion() - .equalsIgnoreCase( - Arn.fromString(dbInstance.dbInstanceAutomatedBackupsReplications().get(0).dbInstanceAutomatedBackupsArn()).getRegion()); + return DBInstancePredicates.isInstanceStabilizedAfterReplicationStart(dbInstance, model); } protected boolean isDBInstanceStabilizedAfterReboot( @@ -739,126 +569,39 @@ protected boolean isDBInstanceStabilizedAfterReboot( final ResourceModel model ) { final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - - assertNoTerminalStatus(dbInstance); - - final boolean isDBClusterParameterGroupStabilized = !isDBClusterMember(model) || isDBClusterParameterGroupStabilized(rdsProxyClient, model); - final boolean isDBInstanceStabilizedAfterReboot = isDBInstanceAvailable(dbInstance) && - isDBParameterGroupInSync(dbInstance) && - isOptionGroupInSync(dbInstance) && - isDBClusterParameterGroupStabilized; - - requestLogger.log(String.format("isDBInstanceStabilizedAfterReboot: %b", isDBInstanceStabilizedAfterReboot), - ImmutableMap.of("isDBInstanceAvailable", isDBInstanceAvailable(dbInstance), - "isDBParameterGroupInSync", isDBParameterGroupInSync(dbInstance), - "isOptionGroupInSync", isOptionGroupInSync(dbInstance), - "isDBClusterParameterGroupStabilized", isDBClusterParameterGroupStabilized), - ImmutableMap.of("Description", "isDBInstanceStabilizedAfterReboot method will be repeatedly" + - " called with a backoff mechanism after the reboot call until it returns true. This" + - " process will continue until all included flags are true.")); - - return isDBInstanceStabilizedAfterReboot; - } - - boolean isDBInstanceAvailable(final DBInstance dbInstance) { - return DBInstanceStatus.Available.equalsString(dbInstance.dbInstanceStatus()); - } - - boolean isDomainMembershipsJoined(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.domainMemberships()).orElse(Collections.emptyList()) - .stream() - .allMatch(membership -> DomainMembershipStatus.Joined.equalsString(membership.status()) || - DomainMembershipStatus.KerberosEnabled.equalsString(membership.status())); - } - - boolean isVpcSecurityGroupsActive(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.vpcSecurityGroups()).orElse(Collections.emptyList()) - .stream() - .allMatch(group -> VPCSecurityGroupStatus.Active.equalsString(group.status())); - } - - boolean isNoPendingChanges(final DBInstance dbInstance) { - final PendingModifiedValues pending = dbInstance.pendingModifiedValues(); - return (pending == null) || (pending.dbInstanceClass() == null && - pending.allocatedStorage() == null && - pending.automationMode() == null && - pending.backupRetentionPeriod() == null && - pending.dbInstanceIdentifier() == null && - pending.dbSubnetGroupName() == null && - pending.engine() == null && - pending.engineVersion() == null && - pending.iamDatabaseAuthenticationEnabled() == null && - pending.iops() == null && - pending.licenseModel() == null && - pending.masterUserPassword() == null && - pending.multiAZ() == null && - pending.pendingCloudwatchLogsExports() == null && - pending.port() == null && - CollectionUtils.isNullOrEmpty(pending.processorFeatures()) && - pending.resumeFullAutomationModeTime() == null && - pending.storageThroughput() == null && - pending.storageType() == null - ); - } - - boolean isCaCertificateChangesApplied(final DBInstance dbInstance, final ResourceModel model) { - final PendingModifiedValues pending = dbInstance.pendingModifiedValues(); - return pending == null || - pending.caCertificateIdentifier() == null || - BooleanUtils.isNotTrue(model.getCertificateRotationRestart()); - } - - boolean isDBParameterGroupNotApplying(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.dbParameterGroups()).orElse(Collections.emptyList()) - .stream() - .noneMatch(group -> DBParameterGroupStatus.Applying.equalsString(group.parameterApplyStatus())); - } - - boolean isReplicationComplete(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.statusInfos()).orElse(Collections.emptyList()) - .stream() - .filter(statusInfo -> READ_REPLICA_STATUS_TYPE.equals(statusInfo.statusType())) - .allMatch(statusInfo -> ReadReplicaStatus.Replicating.equalsString(statusInfo.status())); + if (DBInstancePredicates.isDBClusterMember(model)) { + final DBCluster dbCluster = fetchDBCluster(rdsProxyClient, model); + return DBInstancePredicates.isDBInstanceStabilizedAfterReboot(dbInstance, dbCluster, model, requestLogger); + } else { + return DBInstancePredicates.isDBInstanceStabilizedAfterReboot(dbInstance, requestLogger); + } } protected boolean isOptionGroupStabilized( final ProxyClient rdsProxyClient, final ResourceModel model ) { - return isOptionGroupInSync(fetchDBInstance(rdsProxyClient, model)); - } + final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - protected boolean isOptionGroupInSync(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.optionGroupMemberships()).orElse(Collections.emptyList()) - .stream() - .allMatch(optionGroup -> OptionGroupStatus.InSync.equalsString(optionGroup.status())); + return DBInstancePredicates.isOptionGroupInSync(dbInstance); } protected boolean isDBParameterGroupStabilized( final ProxyClient rdsProxyClient, final ResourceModel model ) { - return isDBParameterGroupInSync(fetchDBInstance(rdsProxyClient, model)); - } + final DBInstance dbInstance = fetchDBInstance(rdsProxyClient, model); - protected boolean isDBParameterGroupInSync(final DBInstance dbInstance) { - return Optional.ofNullable(dbInstance.dbParameterGroups()).orElse(Collections.emptyList()) - .stream() - .allMatch(parameterGroup -> DBParameterGroupStatus.InSync.equalsString(parameterGroup.parameterApplyStatus())); + return DBInstancePredicates.isDBParameterGroupInSync(dbInstance); } protected boolean isDBClusterParameterGroupStabilized( final ProxyClient rdsProxyClient, final ResourceModel model ) { - return isDBClusterParameterGroupInSync(model, fetchDBCluster(rdsProxyClient, model)); - } + final DBCluster dbCluster = fetchDBCluster(rdsProxyClient, model); - protected boolean isDBClusterParameterGroupInSync(final ResourceModel model, final DBCluster dbCluster) { - return Optional.ofNullable(dbCluster.dbClusterMembers()).orElse(Collections.emptyList()) - .stream() - .filter(member -> model.getDBInstanceIdentifier().equalsIgnoreCase(member.dbInstanceIdentifier())) - .anyMatch(member -> DBParameterGroupStatus.InSync.equalsString(member.dbClusterParameterGroupStatus())); + return DBInstancePredicates.isDBClusterParameterGroupInSync(model, dbCluster); } protected boolean isDBInstanceRoleStabilized( @@ -897,13 +640,6 @@ protected boolean isDBInstanceRoleRemovalStabilized( ); } - protected boolean isMasterUserSecretStabilized(final DBInstance instance) { - if (instance.masterUserSecret() == null) { - return true; - } - return SECRET_STATUS_ACTIVE.equalsIgnoreCase(instance.masterUserSecret().secretStatus()); - } - protected ProgressEvent updateAssociatedRoles( final AmazonWebServicesClientProxy proxy, final ProxyClient rdsProxyClient, diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/CreateHandler.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/CreateHandler.java index e6bf30b94..61cf2d74f 100644 --- a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/CreateHandler.java +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/CreateHandler.java @@ -57,7 +57,7 @@ protected void validateRequest(final ResourceHandlerRequest reque } private void validateDeletionPolicyForClusterInstance(final ResourceHandlerRequest request) throws RequestValidationException { - if (isDBClusterMember(request.getDesiredResourceState()) && BooleanUtils.isTrue(request.getSnapshotRequested())) { + if (DBInstancePredicates.isDBClusterMember(request.getDesiredResourceState()) && BooleanUtils.isTrue(request.getSnapshotRequested())) { throw new RequestValidationException(ILLEGAL_DELETION_POLICY_ERROR); } } diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java index 3583ea129..26fbb8f48 100644 --- a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DBInstancePredicates.java @@ -218,17 +218,33 @@ public static boolean isDBInstanceStabilizedAfterMutate( return isDBInstanceStabilizedAfterMutateResult; } + public static boolean isDBInstanceStabilizedAfterReboot( + final DBInstance dbInstance, + final RequestLogger requestLogger + ) { + assertNoTerminalStatus(dbInstance); + + final boolean isDBClusterParameterGroupStabilized = true; + return isDBInstanceStabilizedAfterReboot(dbInstance, isDBClusterParameterGroupStabilized, requestLogger); + } + public static boolean isDBInstanceStabilizedAfterReboot( final DBInstance dbInstance, - final Optional maybeDbCluster, + final DBCluster dbCluster, final ResourceModel model, final RequestLogger requestLogger ) { assertNoTerminalStatus(dbInstance); - final boolean isDBClusterParameterGroupStabilized = maybeDbCluster - .map(dbCluster -> isDBClusterParameterGroupInSync(model, dbCluster)) - .orElseGet(() -> !DBInstancePredicates.isDBClusterMember(model)); + final boolean isDBClusterParameterGroupStabilized = isDBClusterParameterGroupInSync(model, dbCluster); + return isDBInstanceStabilizedAfterReboot(dbInstance, isDBClusterParameterGroupStabilized, requestLogger); + } + + private static boolean isDBInstanceStabilizedAfterReboot( + final DBInstance dbInstance, + final boolean isDBClusterParameterGroupStabilized, + final RequestLogger requestLogger + ) { final boolean isDBInstanceStabilizedAfterReboot = isDBInstanceAvailable(dbInstance) && isDBParameterGroupInSync(dbInstance) && isOptionGroupInSync(dbInstance) && diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DeleteHandler.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DeleteHandler.java index 2d6dc816b..b129428e7 100644 --- a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DeleteHandler.java +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/DeleteHandler.java @@ -49,7 +49,7 @@ protected ProgressEvent handleRequest( // Final snapshots are not allowed for read replicas and cluster instances. if (BooleanUtils.isNotFalse(request.getSnapshotRequested()) && !ResourceModelHelper.isReadReplica(resourceModel) && - !isDBClusterMember(resourceModel)) { + !DBInstancePredicates.isDBClusterMember(resourceModel)) { snapshotIdentifier = snapshotIdentifierFactory.newIdentifier() .withStackId(request.getStackId()) .withResourceId(StringUtils.prependIfMissing(request.getLogicalResourceIdentifier(), SNAPSHOT_PREFIX)) diff --git a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/UpdateHandler.java b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/UpdateHandler.java index d57bf9cab..47d17f6f7 100644 --- a/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/UpdateHandler.java +++ b/aws-rds-dbinstance/src/main/java/software/amazon/rds/dbinstance/UpdateHandler.java @@ -1,35 +1,16 @@ package software.amazon.rds.dbinstance; -import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; - -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.ObjectUtils; - import com.amazonaws.util.CollectionUtils; import com.amazonaws.util.StringUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import software.amazon.awssdk.services.ec2.Ec2Client; import software.amazon.awssdk.services.ec2.model.SecurityGroup; import software.amazon.awssdk.services.rds.RdsClient; -import software.amazon.awssdk.services.rds.model.DBCluster; -import software.amazon.awssdk.services.rds.model.DBClusterMember; -import software.amazon.awssdk.services.rds.model.DBInstance; -import software.amazon.awssdk.services.rds.model.DBParameterGroup; -import software.amazon.awssdk.services.rds.model.DbInstanceNotFoundException; -import software.amazon.awssdk.services.rds.model.DescribeDbEngineVersionsResponse; -import software.amazon.awssdk.services.rds.model.DescribeDbParameterGroupsResponse; -import software.amazon.awssdk.services.rds.model.SourceType; +import software.amazon.awssdk.services.rds.model.*; import software.amazon.awssdk.utils.ImmutableMap; import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; -import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; -import software.amazon.cloudformation.proxy.HandlerErrorCode; -import software.amazon.cloudformation.proxy.ProgressEvent; -import software.amazon.cloudformation.proxy.ProxyClient; -import software.amazon.cloudformation.proxy.ResourceHandlerRequest; +import software.amazon.cloudformation.proxy.*; import software.amazon.rds.common.handler.Commons; import software.amazon.rds.common.handler.Events; import software.amazon.rds.common.handler.HandlerConfig; @@ -41,6 +22,14 @@ import software.amazon.rds.dbinstance.status.DBParameterGroupStatus; import software.amazon.rds.dbinstance.util.ImmutabilityHelper; import software.amazon.rds.dbinstance.util.ResourceModelHelper; + +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + public class UpdateHandler extends BaseHandlerStd { public UpdateHandler() { @@ -104,7 +93,7 @@ protected ProgressEvent handleRequest( return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) .then(progress -> { try { - if(!Objects.equals(request.getDesiredResourceState().getEngineLifecycleSupport(), + if (!Objects.equals(request.getDesiredResourceState().getEngineLifecycleSupport(), request.getPreviousResourceState().getEngineLifecycleSupport()) && !request.getRollback()) { throw new CfnInvalidRequestException("EngineLifecycleSupport cannot be modified."); @@ -188,23 +177,25 @@ protected ProgressEvent handleRequest( } } return progress; - }, (m) -> !StringUtils.isNullOrEmpty(callbackContext.getDbInstanceArn()), (v, c) -> {})) + }, (m) -> !StringUtils.isNullOrEmpty(callbackContext.getDbInstanceArn()), (v, c) -> { + })) .then(progress -> Commons.execOnce(progress, () -> { if (ResourceModelHelper.shouldStopAutomaticBackupReplication(request.getPreviousResourceState(), request.getDesiredResourceState())) { return stopAutomaticBackupReplicationInRegion(callbackContext.getDbInstanceArn(), proxy, progress, rdsProxyClient.defaultClient(), ResourceModelHelper.getAutomaticBackupReplicationRegion(request.getPreviousResourceState())); } - return progress;}, + return progress; + }, CallbackContext::isAutomaticBackupReplicationStopped, CallbackContext::setAutomaticBackupReplicationStopped)) .then(progress -> Commons.execOnce(progress, () -> { if (ResourceModelHelper.shouldStartAutomaticBackupReplication(request.getPreviousResourceState(), request.getDesiredResourceState())) { return startAutomaticBackupReplicationInRegion( - callbackContext.getDbInstanceArn(), - progress.getResourceModel().getAutomaticBackupReplicationKmsKeyId(), - proxy, - progress, - rdsProxyClient.defaultClient(), - ResourceModelHelper.getAutomaticBackupReplicationRegion(request.getDesiredResourceState()) + callbackContext.getDbInstanceArn(), + progress.getResourceModel().getAutomaticBackupReplicationKmsKeyId(), + proxy, + progress, + rdsProxyClient.defaultClient(), + ResourceModelHelper.getAutomaticBackupReplicationRegion(request.getDesiredResourceState()) ); } return progress; @@ -233,7 +224,7 @@ private ProgressEvent handleResourceDrift( return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext) .then(progress -> { if (shouldReboot(rdsProxyClient.defaultClient(), progress) || - (isDBClusterMember(progress.getResourceModel()) && shouldRebootCluster(rdsProxyClient.defaultClient(), progress))) { + (DBInstancePredicates.isDBClusterMember(progress.getResourceModel()) && shouldRebootCluster(rdsProxyClient.defaultClient(), progress))) { return rebootAwait(proxy, rdsProxyClient.defaultClient(), progress); } return progress; @@ -241,7 +232,7 @@ private ProgressEvent handleResourceDrift( .then(progress -> awaitDBParameterGroupInSyncStatus(proxy, rdsProxyClient.defaultClient(), progress)) .then(progress -> awaitOptionGroupInSyncStatus(proxy, rdsProxyClient.defaultClient(), progress)) .then(progress -> { - if (isDBClusterMember(progress.getResourceModel())) { + if (DBInstancePredicates.isDBClusterMember(progress.getResourceModel())) { return awaitDBClusterParameterGroup(proxy, rdsProxyClient.defaultClient(), progress); } return progress; @@ -369,8 +360,8 @@ private ProgressEvent setParameterGroupName( private boolean shouldSetDefaultVpcId(final ResourceHandlerRequest request) { // DBCluster member instances inherit default vpc security groups from the corresponding umbrella cluster - return !isDBClusterMember(request.getDesiredResourceState()) && - !isRdsCustomOracleInstance(request.getDesiredResourceState()) && + return !DBInstancePredicates.isDBClusterMember(request.getDesiredResourceState()) && + !DBInstancePredicates.isRdsCustomOracleInstance(request.getDesiredResourceState()) && CollectionUtils.isNullOrEmpty(request.getDesiredResourceState().getVPCSecurityGroups()); } diff --git a/aws-rds-dbinstance/src/test/java/software/amazon/rds/dbinstance/BaseHandlerStdTest.java b/aws-rds-dbinstance/src/test/java/software/amazon/rds/dbinstance/BaseHandlerStdTest.java index 05afa0f78..51ae24eae 100644 --- a/aws-rds-dbinstance/src/test/java/software/amazon/rds/dbinstance/BaseHandlerStdTest.java +++ b/aws-rds-dbinstance/src/test/java/software/amazon/rds/dbinstance/BaseHandlerStdTest.java @@ -55,14 +55,14 @@ public void setUp() { @Test void isDomainMembershipsJoined_NullDomainMembershipReturnsTrue() { - Assertions.assertThat(handler.isDomainMembershipsJoined( + Assertions.assertThat(DBInstancePredicates.isDomainMembershipsJoined( DBInstance.builder().build() )).isTrue(); } @Test void isDomainMembershipsJoined_EmptyDomainMembershipReturnsTrue() { - Assertions.assertThat(handler.isDomainMembershipsJoined( + Assertions.assertThat(DBInstancePredicates.isDomainMembershipsJoined( DBInstance.builder() .domainMemberships(Collections.emptyList()) .build() @@ -71,7 +71,7 @@ void isDomainMembershipsJoined_EmptyDomainMembershipReturnsTrue() { @Test void isDomainMembershipsJoined_NonEmptyListJoinedAndKerberosReturnsTrue() { - Assertions.assertThat(handler.isDomainMembershipsJoined( + Assertions.assertThat(DBInstancePredicates.isDomainMembershipsJoined( DBInstance.builder() .domainMemberships( DomainMembership.builder().status("joined").build(), @@ -83,7 +83,7 @@ void isDomainMembershipsJoined_NonEmptyListJoinedAndKerberosReturnsTrue() { @Test void isDomainMembershipsJoined_NonEmptyListJoinedAndKerberosAndAnythingElseReturnsFalse() { - Assertions.assertThat(handler.isDomainMembershipsJoined( + Assertions.assertThat(DBInstancePredicates.isDomainMembershipsJoined( DBInstance.builder() .domainMemberships( DomainMembership.builder().status("joined").build(), @@ -96,14 +96,14 @@ void isDomainMembershipsJoined_NonEmptyListJoinedAndKerberosAndAnythingElseRetur @Test void isVpcSecurityGroupsActive_NullVpcSecurityGroupsReturnsTrue() { - Assertions.assertThat(handler.isVpcSecurityGroupsActive( + Assertions.assertThat(DBInstancePredicates.isVpcSecurityGroupsActive( DBInstance.builder().build() )).isTrue(); } @Test void isVpcSecurityGroupsActive_EmptyVpcSecurityGroupsReturnsTrue() { - Assertions.assertThat(handler.isVpcSecurityGroupsActive( + Assertions.assertThat(DBInstancePredicates.isVpcSecurityGroupsActive( DBInstance.builder() .vpcSecurityGroups(Collections.emptyList()) .build() @@ -112,7 +112,7 @@ void isVpcSecurityGroupsActive_EmptyVpcSecurityGroupsReturnsTrue() { @Test void isVpcSecurityGroupsActive_NonEmptyVpcSecurityGroupsActiveReturnsTrue() { - Assertions.assertThat(handler.isVpcSecurityGroupsActive( + Assertions.assertThat(DBInstancePredicates.isVpcSecurityGroupsActive( DBInstance.builder() .vpcSecurityGroups( VpcSecurityGroupMembership.builder().status("active").build(), @@ -124,7 +124,7 @@ void isVpcSecurityGroupsActive_NonEmptyVpcSecurityGroupsActiveReturnsTrue() { @Test void isVpcSecurityGroupsActive_NonEmptyVpcSecurityGroupsNotActiveReturnsFalse() { - Assertions.assertThat(handler.isVpcSecurityGroupsActive( + Assertions.assertThat(DBInstancePredicates.isVpcSecurityGroupsActive( DBInstance.builder() .vpcSecurityGroups( VpcSecurityGroupMembership.builder().status("active").build(), @@ -136,14 +136,14 @@ void isVpcSecurityGroupsActive_NonEmptyVpcSecurityGroupsNotActiveReturnsFalse() @Test void isNoPendingChanges_NullPendingChangesReturnsTrue() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder().build() )).isTrue(); } @Test void isNoPendingChanges_EmptyPendingChangesReturnsTrue() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues(PendingModifiedValues.builder().build()) .build() @@ -152,7 +152,7 @@ void isNoPendingChanges_EmptyPendingChangesReturnsTrue() { @Test void isNoPendingChanges_NonEmptyAllocatedStorageReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -164,7 +164,7 @@ void isNoPendingChanges_NonEmptyAllocatedStorageReturnsFalse() { @Test void isCaCertificateChangesApplied_NonEmptyCACertificateIdentifierReturnsFalse_WhenCertificateRotationRestartIsTrue() { - Assertions.assertThat(handler.isCaCertificateChangesApplied( + Assertions.assertThat(DBInstancePredicates.isCaCertificateChangesApplied( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -177,7 +177,7 @@ void isCaCertificateChangesApplied_NonEmptyCACertificateIdentifierReturnsFalse_W @Test void isCaCertificateChangesApplied_NonEmptyCACertificateIdentifierReturnsTrue_WhenCertificateRotationRestartIsFalse() { - Assertions.assertThat(handler.isCaCertificateChangesApplied( + Assertions.assertThat(DBInstancePredicates.isCaCertificateChangesApplied( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -190,7 +190,7 @@ void isCaCertificateChangesApplied_NonEmptyCACertificateIdentifierReturnsTrue_Wh @Test void isNoPendingChanges_NonEmptyMasterUserPasswordReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -202,7 +202,7 @@ void isNoPendingChanges_NonEmptyMasterUserPasswordReturnsFalse() { @Test void isNoPendingChanges_NonEmptyBackupRetentionPeriodReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -214,7 +214,7 @@ void isNoPendingChanges_NonEmptyBackupRetentionPeriodReturnsFalse() { @Test void isNoPendingChanges_NonEmptyMultiAZReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -226,7 +226,7 @@ void isNoPendingChanges_NonEmptyMultiAZReturnsFalse() { @Test void isNoPendingChanges_NonEmptyEngineVersionReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -238,7 +238,7 @@ void isNoPendingChanges_NonEmptyEngineVersionReturnsFalse() { @Test void isNoPendingChanges_NonEmptyIopsReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -250,7 +250,7 @@ void isNoPendingChanges_NonEmptyIopsReturnsFalse() { @Test void isNoPendingChanges_NonEmptyDBInstanceIdentifierReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -262,7 +262,7 @@ void isNoPendingChanges_NonEmptyDBInstanceIdentifierReturnsFalse() { @Test void isNoPendingChanges_NonEmptyLicenseModelReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -274,7 +274,7 @@ void isNoPendingChanges_NonEmptyLicenseModelReturnsFalse() { @Test void isNoPendingChanges_NonEmptyStorageTypeReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -286,7 +286,7 @@ void isNoPendingChanges_NonEmptyStorageTypeReturnsFalse() { @Test void isNoPendingChanges_NonEmptyDBSubnetGroupNameReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -298,7 +298,7 @@ void isNoPendingChanges_NonEmptyDBSubnetGroupNameReturnsFalse() { @Test void isNoPendingChanges_NonEmptyPendingCloudWatchLogsExportsReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -310,7 +310,7 @@ void isNoPendingChanges_NonEmptyPendingCloudWatchLogsExportsReturnsFalse() { @Test void isNoPendingChanges_EmptyProcessorFeaturesReturnsTrue() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -322,7 +322,7 @@ void isNoPendingChanges_EmptyProcessorFeaturesReturnsTrue() { @Test void isNoPendingChanges_NonEmptyIamDatabaseAutenticationEnabledReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -334,7 +334,7 @@ void isNoPendingChanges_NonEmptyIamDatabaseAutenticationEnabledReturnsFalse() { @Test void isNoPendingChanges_NonEmptyAutomationModeReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -346,7 +346,7 @@ void isNoPendingChanges_NonEmptyAutomationModeReturnsFalse() { @Test void isNoPendingChanges_NonEmptyResumeFullAutomationModeTimeReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -358,7 +358,7 @@ void isNoPendingChanges_NonEmptyResumeFullAutomationModeTimeReturnsFalse() { @Test void isNoPendingChanges_NonEmptyReturnsFalse() { - Assertions.assertThat(handler.isNoPendingChanges( + Assertions.assertThat(DBInstancePredicates.isNoPendingChanges( DBInstance.builder() .pendingModifiedValues( PendingModifiedValues.builder() @@ -370,21 +370,21 @@ void isNoPendingChanges_NonEmptyReturnsFalse() { @Test void isDBParameterGroupSyncComplete_NullParameterGroupsReturnsTrue() { - Assertions.assertThat(handler.isDBParameterGroupNotApplying( + Assertions.assertThat(DBInstancePredicates.isDBParameterGroupNotApplying( DBInstance.builder().build() )).isTrue(); } @Test void isDBParameterGroupSyncComplete_EmptyParameterGroupsReturnsTrue() { - Assertions.assertThat(handler.isDBParameterGroupNotApplying( + Assertions.assertThat(DBInstancePredicates.isDBParameterGroupNotApplying( DBInstance.builder().dbParameterGroups(Collections.emptyList()).build() )).isTrue(); } @Test void isDBParameterGroupSyncComplete_NonEmptyParameterGroupsInSyncReturnsTrue() { - Assertions.assertThat(handler.isDBParameterGroupNotApplying( + Assertions.assertThat(DBInstancePredicates.isDBParameterGroupNotApplying( DBInstance.builder() .dbParameterGroups( DBParameterGroupStatus.builder().parameterApplyStatus("in-sync").build(), @@ -396,7 +396,7 @@ void isDBParameterGroupSyncComplete_NonEmptyParameterGroupsInSyncReturnsTrue() { @Test void isDBParameterGroupSyncComplete_NonEmptyParameterGroupsApplyingReturnsFalse() { - Assertions.assertThat(handler.isDBParameterGroupNotApplying( + Assertions.assertThat(DBInstancePredicates.isDBParameterGroupNotApplying( DBInstance.builder() .dbParameterGroups( DBParameterGroupStatus.builder().parameterApplyStatus("in-sync").build(), @@ -408,21 +408,21 @@ void isDBParameterGroupSyncComplete_NonEmptyParameterGroupsApplyingReturnsFalse( @Test void isReplicationComplete_NullStatusInfoReturnsTrue() { - Assertions.assertThat(handler.isReplicationComplete( + Assertions.assertThat(DBInstancePredicates.isReplicationComplete( DBInstance.builder().build() )).isTrue(); } @Test void isReplicationComplete_EmptyStatusInfoReturnsTrue() { - Assertions.assertThat(handler.isReplicationComplete( + Assertions.assertThat(DBInstancePredicates.isReplicationComplete( DBInstance.builder().statusInfos(Collections.emptyList()).build() )).isTrue(); } @Test void isReplicationComplete_NonEmptyListNoReadReplicaInfoReturnsTrue() { - Assertions.assertThat(handler.isReplicationComplete( + Assertions.assertThat(DBInstancePredicates.isReplicationComplete( DBInstance.builder() .statusInfos(DBInstanceStatusInfo.builder() .statusType("something else") @@ -434,7 +434,7 @@ void isReplicationComplete_NonEmptyListNoReadReplicaInfoReturnsTrue() { @Test void isReplicationComplete_NonEmptyListContainingReplicaInfoReplicatingReturnsTrue() { - Assertions.assertThat(handler.isReplicationComplete( + Assertions.assertThat(DBInstancePredicates.isReplicationComplete( DBInstance.builder() .statusInfos( DBInstanceStatusInfo.builder() @@ -452,7 +452,7 @@ void isReplicationComplete_NonEmptyListContainingReplicaInfoReplicatingReturnsTr @Test void isReplicationComplete_NonEmptyListContainingReplicaInfoNotReplicatingReturnsFalse() { - Assertions.assertThat(handler.isReplicationComplete( + Assertions.assertThat(DBInstancePredicates.isReplicationComplete( DBInstance.builder() .statusInfos( DBInstanceStatusInfo.builder() @@ -470,7 +470,7 @@ void isReplicationComplete_NonEmptyListContainingReplicaInfoNotReplicatingReturn @Test void isMasterUserSecretStabilized_masterUserSecretIsNull() { - Assertions.assertThat(handler.isMasterUserSecretStabilized( + Assertions.assertThat(DBInstancePredicates.isMasterUserSecretStabilized( DBInstance.builder() .build() )).isTrue(); @@ -478,7 +478,7 @@ void isMasterUserSecretStabilized_masterUserSecretIsNull() { @Test void isMasterUserSecretStabilized_masterUserSecretStatusActive() { - Assertions.assertThat(handler.isMasterUserSecretStabilized( + Assertions.assertThat(DBInstancePredicates.isMasterUserSecretStabilized( DBInstance.builder() .masterUserSecret(MasterUserSecret.builder() .secretStatus("Active") @@ -489,7 +489,7 @@ void isMasterUserSecretStabilized_masterUserSecretStatusActive() { @Test void isMasterUserSecretStabilized_masterUserSecretStatusCreating() { - Assertions.assertThat(handler.isMasterUserSecretStabilized( + Assertions.assertThat(DBInstancePredicates.isMasterUserSecretStabilized( DBInstance.builder() .masterUserSecret(MasterUserSecret.builder() .secretStatus("Creating")