bug4811: Builder pattern for load balancer-related procedures; updated tests accordingly

This commit is contained in:
Axel Uhl
2020-11-05 18:25:50 +01:00
parent fbfeebd7a9
commit 0190dfec02
8 changed files with 334 additions and 111 deletions
@@ -21,9 +21,11 @@ import com.sap.sailing.landscape.procedures.StartSailingAnalyticsMaster;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.landscape.InboundReplicationConfiguration;
import com.sap.sse.landscape.aws.AwsInstance;
import com.sap.sse.landscape.aws.AwsLandscape;
import com.sap.sse.landscape.aws.Tags;
import com.sap.sse.landscape.aws.impl.AwsRegion;
import com.sap.sse.landscape.aws.orchestration.CreateDynamicLoadBalancerMapping;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.model.Instance;
@@ -103,6 +105,31 @@ public class TestProcedures {
assertFalse(envSh.isEmpty());
assertTrue("Couldn't find SERVER_NAME=\""+serverName+"\" in env.sh:\n"+envSh, envSh.contains("SERVER_NAME=\""+serverName+"\""));
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout));
// Now create an ALB mapping, assuming to create the dynamic ALB:
final String domain = "wiesen-weg.de";
final String hostname = serverName+"."+domain;
CreateDynamicLoadBalancerMapping.Builder<String, SailingAnalyticsMetrics,
SailingAnalyticsMaster<String>, SailingAnalyticsReplica<String>, AwsInstance<String, SailingAnalyticsMetrics>> createAlbProcedureBuilder = CreateDynamicLoadBalancerMapping.builder();
createAlbProcedureBuilder
.setProcess(process)
.setHostname(hostname)
.setTargetGroupNamePrefix("S-ded-") // TODO when we combine procedures for launching dedicated hosts (StartSailingAnlayticsHost and specializations) then "S-ded-" should be the default; for DeployProcessOnMultiServer, "S-shared-" should be the default
.setLandscape(landscape);
optionalTimeout.ifPresent(createAlbProcedureBuilder::setTimeout);
final CreateDynamicLoadBalancerMapping<String, SailingAnalyticsMetrics, SailingAnalyticsMaster<String>, SailingAnalyticsReplica<String>, AwsInstance<String, SailingAnalyticsMetrics>> createAlbProcedure =
createAlbProcedureBuilder.build();
try {
createAlbProcedure.run();
// A few validations:
// Is the process's host part of the public and master target groups?
assertNotNull(createAlbProcedure.getMasterTargetGroupCreated());
assertNotNull(createAlbProcedure.getPublicTargetGroupCreated());
assertTrue(createAlbProcedure.getMasterTargetGroupCreated().getRegisteredTargets().keySet().contains(process.getHost()));
assertTrue(createAlbProcedure.getPublicTargetGroupCreated().getRegisteredTargets().keySet().contains(process.getHost()));
} finally {
landscape.deleteLoadBalancer(createAlbProcedure.getLoadBalancerUsed());
landscape.removeDNSRecord(landscape.getDNSHostedZoneId(domain), hostname, "*."+domain);
}
} finally {
landscape.terminate(host);
landscape.deleteKeyPair(region, keyName);
@@ -40,7 +40,6 @@ import com.sap.sse.landscape.application.impl.ApplicationProcessImpl;
import com.sap.sse.landscape.aws.impl.AmazonMachineImage;
import com.sap.sse.landscape.aws.impl.AwsRegion;
import com.sap.sse.landscape.aws.orchestration.CreateDNSBasedLoadBalancerMapping;
import com.sap.sse.landscape.aws.orchestration.CreateLoadBalancerMapping;
import com.sap.sse.landscape.impl.ReleaseRepositoryImpl;
import com.sap.sse.landscape.ssh.SSHKeyPair;
import com.sap.sse.landscape.ssh.SshCommandChannel;
@@ -115,9 +114,15 @@ public class ConnectivityTest {
assertEquals(14888, process.getTelnetPortToOSGiConsole(optionalTimeout));
@SuppressWarnings("unchecked")
final AwsLandscape<String, ApplicationProcessMetrics, MasterT, ReplicaT> castLandscape = (AwsLandscape<String, ApplicationProcessMetrics, MasterT, ReplicaT>) landscape;
final CreateLoadBalancerMapping<String, ApplicationProcessMetrics, MasterT, ReplicaT, AwsInstance<String, ApplicationProcessMetrics>> createDNSBasedLoadBalancerMappingProcedure =
new CreateDNSBasedLoadBalancerMapping<String, ApplicationProcessMetrics, MasterT, ReplicaT, AwsInstance<String, ApplicationProcessMetrics>>(
process, hostname, TARGET_GROUP_NAME_PREFIX, castLandscape, optionalTimeout);
final CreateDNSBasedLoadBalancerMapping.Builder<String, ApplicationProcessMetrics, MasterT, ReplicaT, AwsInstance<String, ApplicationProcessMetrics>> builder = CreateDNSBasedLoadBalancerMapping.builder();
builder
.setProcess(process)
.setHostname(hostname)
.setTargetGroupNamePrefix(TARGET_GROUP_NAME_PREFIX)
.setLandscape(castLandscape);
optionalTimeout.ifPresent(builder::setTimeout);
final CreateDNSBasedLoadBalancerMapping<String, ApplicationProcessMetrics, MasterT, ReplicaT, AwsInstance<String, ApplicationProcessMetrics>> createDNSBasedLoadBalancerMappingProcedure =
builder.build();
final String wiesenWegId = landscape.getDNSHostedZoneId(hostedZoneName);
try {
createDNSBasedLoadBalancerMappingProcedure.run();
@@ -207,6 +207,8 @@ extends Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancer(String loadBalancerArn, Region region);
ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancerByName(String name, Region region);
/**
* Creates an application load balancer with the name and in the region specified. The method returns once the request
* has been responded to. The load balancer may still be in a pre-ready state. Use {@link #getApplicationLoadBalancerStatus(ApplicationLoadBalancer)}
@@ -103,6 +103,7 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.DescribeTarg
import software.amazon.awssdk.services.elasticloadbalancingv2.model.Listener;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancer;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancerAttribute;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancerNotFoundException;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancerState;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.ModifyTargetGroupAttributesRequest;
import software.amazon.awssdk.services.elasticloadbalancingv2.model.ProtocolEnum;
@@ -130,6 +131,7 @@ import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
public class AwsLandscapeImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>> implements AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
private static final String DEFAULT_TARGET_GROUP_PREFIX = "D";
private static final Logger logger = Logger.getLogger(AwsLandscapeImpl.class.getName());
private static final long DEFAULT_DNS_TTL_MILLIS = 60000l;
// TODO <config> the sapsailing.com certificate's ARN where the certificate is valid until 2021-05-07; we need a certifiate per region
@@ -137,7 +139,7 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
// TODO <config> the "Java Application with Reverse Proxy" security group in eu-west-2 for experimenting; we need this security group per region
private static final String DEFAULT_APPLICATION_SERVER_SECURITY_GROUP_ID_EU_WEST_1 = "sg-eaf31e85";
private static final String DEFAULT_APPLICATION_SERVER_SECURITY_GROUP_ID_EU_WEST_2 = "sg-0b2afd48960251280";
private static final String DEFAULT_NON_DNS_MAPPED_ALB_NAME = "DefaultDynamicALB";
private static final String DEFAULT_NON_DNS_MAPPED_ALB_NAME = "DefDyn";
private static final String MONGO_REPLICA_SET_TAG_NAME = "mongo-replica-set";
private static final String MONGO_DEFAULT_REPLICA_SET_NAME = "live";
private static final String RABBITMQ_TAG_NAME = "RabbitMQEndpoint";
@@ -252,7 +254,7 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
private Listener createLoadBalancerListener(ApplicationLoadBalancer<ShardingKey, MetricsT> alb, ProtocolEnum protocol) {
final int port = protocol==ProtocolEnum.HTTP?80:443;
final ReverseProxyCluster<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, RotatingFileBasedLog> reverseProxy = getCentralReverseProxy(alb.getRegion());
final TargetGroup<ShardingKey, MetricsT> defaultTargetGroup = createTargetGroup(alb.getRegion(), "DefTG-"+alb.getName()+"-"+protocol.name(),
final TargetGroup<ShardingKey, MetricsT> defaultTargetGroup = createTargetGroup(alb.getRegion(), DEFAULT_TARGET_GROUP_PREFIX+alb.getName()+"-"+protocol.name(),
port, reverseProxy.getHealthCheckPath(), /* healthCheckPort */ port);
defaultTargetGroup.addTargets(reverseProxy.getHosts());
return getLoadBalancingClient(
@@ -360,6 +362,16 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
}
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancerByName(String name, com.sap.sse.landscape.Region region) {
try {
final DescribeLoadBalancersResponse response = getLoadBalancingClient(getRegion(region)).describeLoadBalancers(b->b.names(name));
return response.hasLoadBalancers() ? new ApplicationLoadBalancerImpl<>(region, response.loadBalancers().iterator().next(), this) : null;
} catch (LoadBalancerNotFoundException e) {
return null;
}
}
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancer(String loadBalancerArn, com.sap.sse.landscape.Region region) {
final LoadBalancer loadBalancer = getLoadBalancingClient(getRegion(region))
@@ -735,8 +747,7 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getNonDNSMappedLoadBalancer(
com.sap.sse.landscape.Region region, String wildcardDomain) {
final DescribeLoadBalancersResponse response = getLoadBalancingClient(getRegion(region)).describeLoadBalancers(b->b.names(getNonDNSMappedLoadBalancerName(wildcardDomain)));
return response.hasLoadBalancers() ? new ApplicationLoadBalancerImpl<>(region, response.loadBalancers().iterator().next(), this) : null;
return getLoadBalancerByName(getNonDNSMappedLoadBalancerName(wildcardDomain), region);
}
@Override
@@ -746,13 +757,13 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
}
private String getNonDNSMappedLoadBalancerName(String wildcardDomain) {
return DEFAULT_NON_DNS_MAPPED_ALB_NAME + "-" + wildcardDomain.replaceAll(".", "-");
return DEFAULT_NON_DNS_MAPPED_ALB_NAME + wildcardDomain.replaceAll("\\.", "-");
}
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getDNSMappedLoadBalancerFor(
com.sap.sse.landscape.Region region, String hostname) {
final DescribeLoadBalancersResponse response = getLoadBalancingClient(getRegion(region)).describeLoadBalancers(b->b.names(DEFAULT_NON_DNS_MAPPED_ALB_NAME));
final DescribeLoadBalancersResponse response = getLoadBalancingClient(getRegion(region)).describeLoadBalancers();
for (final LoadBalancer lb : response.loadBalancers()) {
final ApplicationLoadBalancer<ShardingKey, MetricsT> alb = new ApplicationLoadBalancerImpl<>(region, lb, this);
for (final Rule rule : alb.getRules()) {
@@ -2,7 +2,6 @@ package com.sap.sse.landscape.aws.orchestration;
import java.io.IOException;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -10,7 +9,7 @@ import java.util.stream.IntStream;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.landscape.Region;
import com.sap.sse.landscape.application.ApplicationMasterProcess;
@@ -36,65 +35,96 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.LoadBalancer
*/
public class CreateDNSBasedLoadBalancerMapping<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, AwsInstance<ShardingKey, MetricsT>>
implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
private static final String DNS_MAPPED_ALB_NAME_PREFIX = "DNSMapped-";
private static final Pattern ALB_NAME_PATTERN = Pattern.compile(DNS_MAPPED_ALB_NAME_PREFIX+"(.*)$");
public CreateDNSBasedLoadBalancerMapping(
ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process, String hostname,
String targetGroupNamePrefix,
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape,
Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException, SftpException {
super(process, getOrCreateDNSMappedLoadBalancer(landscape, process.getHost().getRegion()), hostname,
targetGroupNamePrefix, landscape, optionalTimeout);
}
/**
* Finds or creates a {@link ApplicationLoadBalander} load balancer in the {@code region} that is DNS-mapped and still has
* at least the length of {@link #createRules()} additional rules available.
* @throws InterruptedException
*/
private static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
public static interface Builder<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
ApplicationLoadBalancer<ShardingKey, MetricsT> getOrCreateDNSMappedLoadBalancer(
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape, Region region) throws InterruptedException {
ApplicationLoadBalancer<ShardingKey, MetricsT> result = null;
final Set<String> loadBalancerNames = new HashSet<>();
for (final ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancer : landscape.getLoadBalancers(region)) {
if (ALB_NAME_PATTERN.matcher(loadBalancer.getName()).matches()) {
loadBalancerNames.add(loadBalancer.getName());
if (Util.size(loadBalancer.getRules()) <= MAX_RULES_PER_ALB - NUMBER_OF_RULES_PER_REPLICA_SET) {
result = loadBalancer;
break;
extends CreateLoadBalancerMapping.Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
CreateDNSBasedLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> build() throws JSchException, IOException, InterruptedException, SftpException;
}
protected static class BuilderImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping.BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancerUsed() throws InterruptedException {
final ApplicationLoadBalancer<ShardingKey, MetricsT> result;
if (super.getLoadBalancerUsed() != null) {
result = super.getLoadBalancerUsed();
} else {
result = getOrCreateDNSMappedLoadBalancer(getLandscape(), getProcess().getHost().getRegion());
}
return result;
}
/**
* Finds or creates a {@link ApplicationLoadBalander} load balancer in the {@code region} that is DNS-mapped and still has
* at least the length of {@link #createRules()} additional rules available.
* @throws InterruptedException
*/
private ApplicationLoadBalancer<ShardingKey, MetricsT> getOrCreateDNSMappedLoadBalancer(
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape, Region region) throws InterruptedException {
ApplicationLoadBalancer<ShardingKey, MetricsT> result = null;
final Set<String> loadBalancerNames = new HashSet<>();
for (final ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancer : landscape.getLoadBalancers(region)) {
if (ALB_NAME_PATTERN.matcher(loadBalancer.getName()).matches()) {
loadBalancerNames.add(loadBalancer.getName());
if (Util.size(loadBalancer.getRules()) <= MAX_RULES_PER_ALB - NUMBER_OF_RULES_PER_REPLICA_SET) {
result = loadBalancer;
break;
}
}
}
}
if (result == null) {
result = landscape.createLoadBalancer(getAvailableDNSMappedAlbName(loadBalancerNames), region);
while (landscape.getApplicationLoadBalancerStatus(result).code() == LoadBalancerStateEnum.PROVISIONING) {
Thread.sleep(1000); // wait until the ALB has been provisioned or failed
if (result == null) {
result = landscape.createLoadBalancer(getAvailableDNSMappedAlbName(loadBalancerNames), region);
final TimePoint startingToPollForReady = TimePoint.now();
while (landscape.getApplicationLoadBalancerStatus(result).code() == LoadBalancerStateEnum.PROVISIONING
&& (!getOptionalTimeout().isPresent() || startingToPollForReady.until(TimePoint.now()).compareTo(getOptionalTimeout().get()) <= 0)) {
Thread.sleep(1000); // wait until the ALB has been provisioned or failed
}
}
return result;
}
return result;
/**
* Picks a new load balancer name following the pattern {@link #DNS_MAPPED_ALB_NAME_PREFIX}{@code [0-9]+} that is not
* part of {@code loadBalancerNames} and has the least number.
*/
private String getAvailableDNSMappedAlbName(Set<String> loadBalancerNames) {
final Set<Integer> numbersTaken = new HashSet<>();
for (final String loadBalancerName : loadBalancerNames) {
final Matcher matcher = ALB_NAME_PATTERN.matcher(loadBalancerName);
if (matcher.find()) {
numbersTaken.add(Integer.parseInt(matcher.group(1)));
}
}
return DNS_MAPPED_ALB_NAME_PREFIX + IntStream.range(0, MAX_ALBS_PER_REGION).filter(i->!numbersTaken.contains(i)).min().getAsInt();
}
@Override
public CreateDNSBasedLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> build() throws JSchException, IOException, InterruptedException, SftpException {
return new CreateDNSBasedLoadBalancerMapping<>(this);
}
}
public static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder() {
return new BuilderImpl<>();
}
/**
* Picks a new load balancer name following the pattern {@link #DNS_MAPPED_ALB_NAME_PREFIX}{@code [0-9]+} that is not
* part of {@code loadBalancerNames} and has the least number.
*/
private static String getAvailableDNSMappedAlbName(Set<String> loadBalancerNames) {
final Set<Integer> numbersTaken = new HashSet<>();
for (final String loadBalancerName : loadBalancerNames) {
final Matcher matcher = ALB_NAME_PATTERN.matcher(loadBalancerName);
if (matcher.find()) {
numbersTaken.add(Integer.parseInt(matcher.group(1)));
}
}
return DNS_MAPPED_ALB_NAME_PREFIX + IntStream.range(0, MAX_ALBS_PER_REGION).filter(i->!numbersTaken.contains(i)).min().getAsInt();
protected CreateDNSBasedLoadBalancerMapping(BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) throws JSchException, IOException, InterruptedException, SftpException {
super(builder);
}
@Override
@@ -1,11 +1,9 @@
package com.sap.sse.landscape.aws.orchestration;
import java.io.IOException;
import java.util.Optional;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import com.sap.sse.common.Duration;
import com.sap.sse.landscape.Region;
import com.sap.sse.landscape.application.ApplicationMasterProcess;
import com.sap.sse.landscape.application.ApplicationProcess;
@@ -24,43 +22,66 @@ import com.sap.sse.landscape.orchestration.Procedure;
public class CreateDynamicLoadBalancerMapping<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, AwsInstance<ShardingKey, MetricsT>>
implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
public CreateDynamicLoadBalancerMapping(
ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process, String hostname,
String targetGroupNamePrefix,
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape,
Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException, SftpException {
super(process, getOrCreateNonDNSMappedLoadBalancer(process.getHost().getRegion(), hostname, landscape), hostname,
targetGroupNamePrefix, landscape, optionalTimeout);
}
protected static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
extends CreateLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
public static interface Builder<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>>
ApplicationLoadBalancer<ShardingKey, MetricsT> getOrCreateNonDNSMappedLoadBalancer(
Region region, String hostname,
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape) {
final String domainName = getHostedZoneName(hostname);
final ApplicationLoadBalancer<ShardingKey, MetricsT> existingLoadBalancer = landscape.getNonDNSMappedLoadBalancer(region, domainName);
final ApplicationLoadBalancer<ShardingKey, MetricsT> result;
if (existingLoadBalancer != null) {
result = existingLoadBalancer;
} else {
result = landscape.createNonDNSMappedLoadBalancer(region, domainName);
createWildcardRoute53Mapping(landscape, result, domainName);
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping.Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
CreateDynamicLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> build() throws JSchException, IOException, InterruptedException, SftpException;
}
protected static class BuilderImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
extends CreateLoadBalancerMapping.BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
@Override
public ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancerUsed() throws InterruptedException {
final ApplicationLoadBalancer<ShardingKey, MetricsT> result;
if (super.getLoadBalancerUsed() != null) {
result = super.getLoadBalancerUsed();
} else {
result = getOrCreateNonDNSMappedLoadBalancer(getProcess().getHost().getRegion(), getHostname(), getLandscape());
}
return result;
}
protected ApplicationLoadBalancer<ShardingKey, MetricsT> getOrCreateNonDNSMappedLoadBalancer(
Region region, String hostname, AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape) {
final String domainName = getHostedZoneName(hostname);
final ApplicationLoadBalancer<ShardingKey, MetricsT> existingLoadBalancer = landscape.getNonDNSMappedLoadBalancer(region, domainName);
final ApplicationLoadBalancer<ShardingKey, MetricsT> result;
if (existingLoadBalancer != null) {
result = existingLoadBalancer;
} else {
result = landscape.createNonDNSMappedLoadBalancer(region, domainName);
createWildcardRoute53Mapping(landscape, result, domainName);
}
return result;
}
private void createWildcardRoute53Mapping(
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape,
ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancer, String domainName) {
final String hostname = "*." + domainName;
landscape.setDNSRecordToApplicationLoadBalancer(landscape.getDNSHostedZoneId(domainName), hostname, loadBalancer);
}
@Override
public CreateDynamicLoadBalancerMapping<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> build() throws JSchException, IOException, InterruptedException, SftpException {
return new CreateDynamicLoadBalancerMapping<>(this);
}
return result;
}
private static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
public static <ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>>
void createWildcardRoute53Mapping(
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape,
ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancer,
String domainName) {
final String hostname = "*." + domainName;
landscape.setDNSRecordToApplicationLoadBalancer(landscape.getDNSHostedZoneId(domainName), hostname, loadBalancer);
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder() {
return new BuilderImpl<>();
}
protected CreateDynamicLoadBalancerMapping(BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) throws JSchException, IOException, InterruptedException, SftpException {
super(builder);
}
}
@@ -81,15 +81,81 @@ extends ProcedureWithTargetGroup<ShardingKey, MetricsT, MasterProcessT, ReplicaP
private TargetGroup<ShardingKey, MetricsT> masterTargetGroupCreated;
private TargetGroup<ShardingKey, MetricsT> publicTargetGroupCreated;
private Iterable<Rule> rulesAdded;
/**
* Default rules implemented by this builder:
* <ul>
* <li>The timeout for looking up the process's server name defaults to no timeout.</li>
* </ul>
*
* @author Axel Uhl (D043530)
*/
public static interface Builder<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends ProcedureWithTargetGroup.Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setProcess(ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process);
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setHostname(String hostname);
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setTimeout(Duration timeout);
}
protected static class BuilderImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
extends ProcedureWithTargetGroup.BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
private String hostname;
private ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process;
private Optional<Duration> optionalTimeout = Optional.empty();
public CreateLoadBalancerMapping(ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process,
ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed, String hostname,
String targetGroupNamePrefix,
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape,
Optional<Duration> optionalTimeout) throws JSchException, IOException, InterruptedException, SftpException {
super(loadBalancerUsed, targetGroupNamePrefix, landscape, process.getServerName(optionalTimeout));
this.process = process;
this.hostname = hostname;
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setProcess(
ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> process) {
this.process = process;
return this;
}
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setHostname(String hostname) {
this.hostname = hostname;
return this;
}
public String getHostname() {
return hostname;
}
public ApplicationProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getProcess() {
return process;
}
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setTimeout(Duration timeout) {
this.optionalTimeout = Optional.of(timeout);
return this;
}
public Optional<Duration> getOptionalTimeout() {
return optionalTimeout;
}
@Override
public String getServerName() throws JSchException, IOException, InterruptedException, SftpException {
final String result;
if (super.getServerName() != null) {
result = super.getServerName();
} else {
result = getProcess().getServerName(getOptionalTimeout());
}
return result;
}
}
protected CreateLoadBalancerMapping(BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) throws JSchException, IOException, InterruptedException, SftpException {
super(builder);
this.process = builder.getProcess();
this.hostname = builder.getHostname();
}
@Override
@@ -31,15 +31,76 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
private static final String MASTER_TARGET_GROUP_SUFFIX = "-m";
private final ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed;
private final String targetGroupNamePrefix;
private final String servername;
private final String serverName;
private Iterable<Rule> rulesAdded;
public static interface Builder<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>> {
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLoadBalancerUsed(ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed);
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setTargetGroupNamePrefix(String targetGroupNamePrefix);
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setServerName(String serverName);
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLandscape(AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape);
}
protected static class BuilderImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>, HostT extends AwsInstance<ShardingKey, MetricsT>>
implements Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
private ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed;
private String targetGroupNamePrefix;
private String serverName;
private AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape;
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLoadBalancerUsed(
ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed) {
this.loadBalancerUsed = loadBalancerUsed;
return this;
}
public ProcedureWithTargetGroup(ApplicationLoadBalancer<ShardingKey, MetricsT> loadBalancerUsed, String targetGroupNamePrefix,
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape, String servername) throws JSchException, IOException, InterruptedException, SftpException {
super(landscape);
this.loadBalancerUsed = loadBalancerUsed;
this.targetGroupNamePrefix = targetGroupNamePrefix;
this.servername = servername;
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setTargetGroupNamePrefix(
String targetGroupNamePrefix) {
this.targetGroupNamePrefix = targetGroupNamePrefix;
return this;
}
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setServerName(String serverName) {
this.serverName = serverName;
return this;
}
public ApplicationLoadBalancer<ShardingKey, MetricsT> getLoadBalancerUsed() throws InterruptedException {
return loadBalancerUsed;
}
public String getTargetGroupNamePrefix() {
return targetGroupNamePrefix;
}
public String getServerName() throws JSchException, IOException, InterruptedException, SftpException {
return serverName;
}
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLandscape(
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape) {
this.landscape = landscape;
return this;
}
public AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape() {
return landscape;
}
}
protected ProcedureWithTargetGroup(BuilderImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) throws JSchException, IOException, InterruptedException, SftpException {
super(builder.getLandscape());
this.loadBalancerUsed = builder.getLoadBalancerUsed();
this.targetGroupNamePrefix = builder.getTargetGroupNamePrefix();
this.serverName = builder.getServerName();
}
protected TargetGroup<ShardingKey, MetricsT> createTargetGroup(Region region, String targetGroupName,
@@ -63,7 +124,7 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
}
protected String getPublicTargetGroupName() {
return targetGroupNamePrefix+servername;
return targetGroupNamePrefix+serverName;
}
protected TargetGroup<ShardingKey, MetricsT> getPublicTargetGroup() throws JSchException, IOException, InterruptedException, SftpException {