bug4811: refactored UpgradeAmi procedure and continued with its snapshot handling

This commit is contained in:
Axel Uhl
2020-11-06 18:46:49 +01:00
parent 92afc436a0
commit 8e8d51c64e
13 changed files with 303 additions and 178 deletions
@@ -24,6 +24,7 @@ import com.sap.sailing.landscape.SailingAnalyticsMetrics;
import com.sap.sailing.landscape.SailingAnalyticsReplica;
import com.sap.sailing.landscape.impl.BearerTokenReplicationCredentials;
import com.sap.sailing.landscape.procedures.StartSailingAnalyticsMaster;
import com.sap.sailing.landscape.procedures.UpgradeAmi;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.landscape.InboundReplicationConfiguration;
@@ -67,6 +68,20 @@ public class TestProcedures {
securityServiceReplicationBearerToken = System.getProperty(SECURITY_SERVICE_REPLICATION_BEARER_TOKEN);
}
@Test
public void testImageUpgrade() throws Exception {
final com.sap.sailing.landscape.procedures.UpgradeAmi.Builder<String, SailingAnalyticsMaster<String>, SailingAnalyticsReplica<String>> imageUpgradeProcedureBuilder = UpgradeAmi.builder();
final UpgradeAmi<String, SailingAnalyticsMaster<String>, SailingAnalyticsReplica<String>> imageUpgradeProcedure =
imageUpgradeProcedureBuilder.build();
try {
imageUpgradeProcedure.run();
int continueHere;
// TODO add test verdicts
} finally {
imageUpgradeProcedure.getUpgradedAmi().delete();
}
}
@Test
public void testMongoReplica() throws Exception {
final Builder<String, ApplicationProcessMetrics, ?, ?> startMongoDBServerProcedureBuilder = StartMongoDBServer.builder();
@@ -21,7 +21,8 @@ import com.sap.sse.landscape.orchestration.Procedure;
* @param <ShardingKey>
* @param <HostT>
*/
public abstract class StartEmptyServer<ShardingKey, MetricsT extends ApplicationProcessMetrics,
public abstract class StartEmptyServer<T extends StartEmptyServer<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>,
ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
@@ -43,42 +44,40 @@ implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
* </ul>
* @author Axel Uhl (D043530)
*/
public static interface Builder<ShardingKey, MetricsT extends ApplicationProcessMetrics,
public static interface Builder<T extends StartEmptyServer<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>,
ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartAwsHost.Builder<StartEmptyServer<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
boolean isNoShutdown();
Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setNoShutdown(boolean noShutdown);
extends StartAwsHost.Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setNoShutdown(boolean noShutdown);
}
protected abstract static class BuilderImpl<ShardingKey, MetricsT extends ApplicationProcessMetrics,
protected abstract static class BuilderImpl<T extends StartEmptyServer<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>,
ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartAwsHost.BuilderImpl<StartEmptyServer<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
extends StartAwsHost.BuilderImpl<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
private boolean noShutdown = true;
@Override
public boolean isNoShutdown() {
protected boolean isNoShutdown() {
return noShutdown;
}
@Override
public Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setNoShutdown(boolean noShutdown) {
public Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setNoShutdown(boolean noShutdown) {
this.noShutdown = noShutdown;
return this;
}
@Override
public String getInstanceName() {
protected String getInstanceName() {
return super.getInstanceName() == null ? IMAGE_UPGRADE_USER_DATA+" for "+getMachineImage().getId() : super.getInstanceName();
}
}
public StartEmptyServer(Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
public StartEmptyServer(BuilderImpl<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
super(builder);
addUserData(Collections.singleton(IMAGE_UPGRADE_USER_DATA));
if (builder.isNoShutdown()) {
@@ -6,12 +6,11 @@ import java.util.logging.Logger;
import com.sap.sailing.landscape.SailingAnalyticsHost;
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
import com.sap.sailing.landscape.impl.SailingAnalyticsHostImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.landscape.application.ApplicationMasterProcess;
import com.sap.sse.landscape.application.ApplicationProcessMetrics;
import com.sap.sse.landscape.application.ApplicationReplicaProcess;
import com.sap.sse.landscape.aws.AwsInstance;
import com.sap.sse.landscape.aws.AwsAvailabilityZone;
import com.sap.sse.landscape.aws.AwsLandscape;
import com.sap.sse.landscape.aws.HostSupplier;
import com.sap.sse.landscape.ssh.SshCommandChannel;
@@ -30,13 +29,12 @@ import software.amazon.awssdk.services.ec2.model.InstanceType;
* @author Axel Uhl (D043530)
*
* @param <ShardingKey>
* @param <HostT>
* @param <SailingAnalyticsHost<ShardingKey>>
*/
public class StartMultiServer<ShardingKey, MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartEmptyServer<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
public class StartMultiServer<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends StartEmptyServer<StartMultiServer<ShardingKey, MasterProcessT, ReplicaProcessT>, ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> {
private static final Logger logger = Logger.getLogger(StartMultiServer.class.getName());
private Optional<Duration> optionalTimeout;
@@ -46,41 +44,48 @@ extends StartEmptyServer<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT,
*
* @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 StartEmptyServer.Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
@Override
default boolean isNoShutdown() {
return true;
}
public static interface Builder<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends StartEmptyServer.Builder<StartMultiServer<ShardingKey, MasterProcessT, ReplicaProcessT>, ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> {
}
protected static class BuilderImpl<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends StartEmptyServer.BuilderImpl<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
implements Builder<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> {
extends StartEmptyServer.BuilderImpl<StartMultiServer<ShardingKey, MasterProcessT, ReplicaProcessT>,
ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
implements Builder<ShardingKey, MasterProcessT, ReplicaProcessT> {
@Override
public StartMultiServer<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> build() {
public StartMultiServer<ShardingKey, MasterProcessT, ReplicaProcessT> build() {
return new StartMultiServer<>(this);
}
@Override
protected boolean isNoShutdown() {
return true;
}
@Override
public HostSupplier<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> getHostSupplier() {
return SailingAnalyticsHostImpl::new;
return new HostSupplier<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>() {
@Override
public SailingAnalyticsHost<ShardingKey> supply(String instanceId, AwsAvailabilityZone az,
AwsLandscape<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT> landscape) {
return null;
}
};
}
}
public static <ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, SailingAnalyticsMetrics>> Builder<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> builder() {
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
Builder<ShardingKey, MasterProcessT, ReplicaProcessT> builder() {
return new BuilderImpl<>();
}
protected StartMultiServer(Builder<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
protected StartMultiServer(BuilderImpl<ShardingKey, MasterProcessT, ReplicaProcessT> builder) {
super(builder);
this.optionalTimeout = builder.getOptionalTimeout();
}
@@ -147,7 +147,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, SailingAnalyticsMaste
}
}
protected StartSailingAnalyticsHost(Builder<? extends StartSailingAnalyticsHost<ShardingKey, ProcessT>, ShardingKey, ProcessT> builder) {
protected StartSailingAnalyticsHost(BuilderImpl<? extends StartSailingAnalyticsHost<ShardingKey, ProcessT>, ShardingKey, ProcessT> builder) {
super(builder);
// remember the port we need in order to hand out the process
this.port = builder.getPort();
@@ -46,7 +46,7 @@ public class StartSailingAnalyticsMaster<ShardingKey>
return new BuilderImpl<>();
}
protected StartSailingAnalyticsMaster(Builder<ShardingKey> builder) {
protected StartSailingAnalyticsMaster(BuilderImpl<ShardingKey> builder) {
super(builder);
addUserData(ProcessConfigurationVariable.USE_ENVIRONMENT, "live-master-server"); // TODO maybe this should be handled by this procedure adding the correct defaults, e.g., for replicating security/sharedsailing?
}
@@ -47,7 +47,7 @@ public class StartSailingAnalyticsReplica<ShardingKey>
return new BuilderImpl<>();
}
protected StartSailingAnalyticsReplica(Builder<ShardingKey> builder) {
protected StartSailingAnalyticsReplica(BuilderImpl<ShardingKey> builder) {
super(builder);
addUserData(ProcessConfigurationVariable.USE_ENVIRONMENT, "live-replica-server"); // TODO maybe this should be handled by this procedure adding the correct defaults, e.g., for replicating security/sharedsailing?
}
@@ -1,22 +1,29 @@
package com.sap.sailing.landscape.procedures;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sap.sailing.landscape.SailingAnalyticsHost;
import com.sap.sailing.landscape.SailingAnalyticsMetrics;
import com.sap.sailing.landscape.impl.SailingAnalyticsHostImpl;
import com.sap.sailing.landscape.procedures.UpgradeAmi.Builder;
import com.sap.sailing.landscape.procedures.UpgradeAmi.Builder.VersionPart;
import com.sap.sse.landscape.MachineImage;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.landscape.application.ApplicationMasterProcess;
import com.sap.sse.landscape.application.ApplicationReplicaProcess;
import com.sap.sse.landscape.aws.AmazonMachineImage;
import com.sap.sse.landscape.aws.AwsInstance;
import com.sap.sse.landscape.aws.HostSupplier;
import com.sap.sse.landscape.aws.orchestration.StartAwsHost;
import com.sap.sse.landscape.orchestration.Procedure;
import software.amazon.awssdk.services.ec2.model.BlockDeviceMapping;
import software.amazon.awssdk.services.ec2.model.Instance;
import software.amazon.awssdk.services.ec2.model.InstanceStateName;
/**
* Upgrades an existing Amazon Machine Image that is expected to be prepared for such an upgrade, by
* invoking it with very specific user data that trigger the automatic upgrade. The resulting AMI can
@@ -41,14 +48,18 @@ import com.sap.sse.landscape.orchestration.Procedure;
public class UpgradeAmi<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends StartEmptyServer<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
extends StartEmptyServer<UpgradeAmi<ShardingKey, MasterProcessT, ReplicaProcessT>, ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT> {
private static final Logger logger = Logger.getLogger(UpgradeAmi.class.getName());
private static final String IMAGE_UPGRADE_USER_DATA = "image-upgrade";
private static final String NO_SHUTDOWN_USER_DATA = "no-shutdown";
private static final Pattern imageNamePattern = Pattern.compile("^(.*) ([0-9]+)\\.([0-9]+)(\\.([0-9]+))?$");
private final String upgradedImageName;
private MachineImage upgradedAmi;
private final Duration timeout;
private final boolean waitForShutdown; // no need to wait if no shutdown was requested
private final Map<String, String> deviceNamesToSnapshotBaseNames;
private AmazonMachineImage<ShardingKey, SailingAnalyticsMetrics> upgradedAmi;
/**
* Additional default rules in addition to what the {@link StartAwsHost.Builder parent builder} defines:
@@ -71,8 +82,7 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
public static interface Builder<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends
StartEmptyServer.Builder<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> {
extends StartEmptyServer.Builder<UpgradeAmi<ShardingKey, MasterProcessT, ReplicaProcessT>, ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> {
enum VersionPart {
MAJOR, MINOR, MICRO
}
@@ -80,18 +90,35 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setUpgradedImageName(String upgradedImageName);
Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setVersionPartToIncrement(VersionPart versionPartToIncrement);
/**
* An optional timeout when waiting for the upgraded instance to shut down for image creation.
*/
Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setTimeout(Duration timeout);
/**
* It is possible to assign base names for snapshots based on their device name in the AMI. For example, "/dev/sdc" may
* be the "Swap" device, and "/dev/xvda" may be the "System" partition. The full name for the snapshot is then assembled from
* the AMI's name including its version and this base name. If no such basename is provided for a device name for which
* a block device mapping to a snapshot exists, the snapshot will only be named after the AMI's name, so when multiple snapshots
* are connected to the AMI then their names will not be discernible.
*/
Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setSnapshotBaseName(String deviceName, String snapshotBaseName);
}
protected static class BuilderImpl<ShardingKey,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT>>
extends StartEmptyServer.BuilderImpl<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
extends StartEmptyServer.BuilderImpl<UpgradeAmi<ShardingKey, MasterProcessT, ReplicaProcessT>, ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>>
implements Builder<ShardingKey, MasterProcessT, ReplicaProcessT> {
private String upgradedImageName;
private VersionPart versionPartToIncrement;
private Duration timeout;
private final Map<String, String> deviceNamesToSnapshotBaseNames;
private BuilderImpl() {
super();
deviceNamesToSnapshotBaseNames = new HashMap<>();
setNoShutdown(false);
}
@@ -101,22 +128,36 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
return this;
}
@Override
public Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setTimeout(Duration timeout) {
this.timeout = timeout;
return this;
}
private String increaseVersionNumber(String imageName) {
final String result;
final Matcher versionNumberMatcher = imageNamePattern.matcher(imageName);
if (versionNumberMatcher.matches()) {
final String imageBaseName = versionNumberMatcher.group(1);
final String majorVersion = versionNumberMatcher.group(2);
final String minorVersion = versionNumberMatcher.group(3);
final String microVersion = versionNumberMatcher.group(5);
final Integer oldMajorVersion = Integer.valueOf(versionNumberMatcher.group(2));
final Integer oldMinorVersion = Integer.valueOf(versionNumberMatcher.group(3));
final Integer oldMicroVersion = versionNumberMatcher.group(5) == null ? null : Integer.valueOf(versionNumberMatcher.group(5));
final VersionPart partToEffectivelyIncrement = versionPartToIncrement == null
? oldMicroVersion == null ? VersionPart.MINOR : VersionPart.MICRO
: versionPartToIncrement;
final Integer newMajorVersion = partToEffectivelyIncrement == VersionPart.MAJOR ? oldMajorVersion + 1 : oldMajorVersion;
final Integer newMinorVersion = partToEffectivelyIncrement == VersionPart.MINOR ? oldMinorVersion + 1 : oldMinorVersion;
final Integer newMicroVersion = oldMajorVersion == null ?
partToEffectivelyIncrement == VersionPart.MICRO ? 0 : null :
partToEffectivelyIncrement == VersionPart.MICRO ? oldMajorVersion + 1 : oldMajorVersion;
final StringBuilder sb = new StringBuilder(imageBaseName);
sb.append(' ');
sb.append(majorVersion);
sb.append(newMajorVersion);
sb.append('.');
sb.append(minorVersion);
if (microVersion != null || versionPartToIncrement == VersionPart.MICRO) {
sb.append(newMinorVersion);
if (newMicroVersion != null) {
sb.append('.');
sb.append(microVersion);
sb.append(newMicroVersion);
}
result = sb.toString();
} else {
@@ -131,6 +172,12 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
return this;
}
@Override
public Builder<ShardingKey, MasterProcessT, ReplicaProcessT> setSnapshotBaseName(String deviceName, String snapshotBaseName) {
deviceNamesToSnapshotBaseNames.put(deviceName, snapshotBaseName);
return this;
}
@Override
public HostSupplier<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, ReplicaProcessT, SailingAnalyticsHost<ShardingKey>> getHostSupplier() {
return SailingAnalyticsHostImpl::new;
@@ -139,6 +186,17 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
protected String getUpgradedImageName() {
return upgradedImageName;
}
/**
* @return {@code null} means wait forever
*/
protected Duration getTimeout() {
return timeout;
}
protected Map<String, String> getDeviceNamesToSnapshotBaseNames() {
return Collections.unmodifiableMap(deviceNamesToSnapshotBaseNames);
}
@Override
public UpgradeAmi<ShardingKey, MasterProcessT, ReplicaProcessT> build() {
@@ -159,6 +217,9 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
protected UpgradeAmi(BuilderImpl<ShardingKey, MasterProcessT, ReplicaProcessT> builder) {
super(builder);
upgradedImageName = builder.getUpgradedImageName();
timeout = builder.getTimeout();
waitForShutdown = !builder.isNoShutdown();
deviceNamesToSnapshotBaseNames = builder.getDeviceNamesToSnapshotBaseNames();
addUserData(Collections.singleton(IMAGE_UPGRADE_USER_DATA));
if (builder.isNoShutdown()) {
addUserData(Collections.singleton(NO_SHUTDOWN_USER_DATA));
@@ -168,16 +229,45 @@ implements Procedure<ShardingKey, SailingAnalyticsMetrics, MasterProcessT, Repli
@Override
public void run() throws Exception {
super.run(); // launches the machine in upgrade mode and shuts it down again, preparing for AMI creation
final Instance instance = getLandscape().getInstance(getHost().getInstanceId(), getHost().getRegion());
if (waitForShutdown) {
logger.info("Waiting for shutdown of instance "+instance.instanceId());
// wait for the instance to shut down
final TimePoint startedWaiting = TimePoint.now();
while (instance.state().name() != InstanceStateName.STOPPED && (timeout == null || startedWaiting.until(TimePoint.now()).compareTo(timeout) < 0)) {
logger.info("Instance " + instance.instanceId() + " still in state " + instance.state().name()
+ ". Waiting " + (timeout == null ? "forever"
: ("for " + timeout.minus(startedWaiting.until(TimePoint.now())))));
Thread.sleep(5000);
}
}
upgradedAmi = getLandscape().createImage(getHost(), upgradedImageName);
// TODO now comes the waiting for the shutdown and initiating the creation of an AMI for the instance
// TODO then comes the tagging of the volume snapshots created
// TODO then tag the resulting AMI according to the original image's tags, except for the name where automatic version number increment should be implemented
for (final BlockDeviceMapping blockDeviceMapping : upgradedAmi.getBlockDeviceMappings()) {
if (blockDeviceMapping.ebs() != null) {
final String snapshotId = blockDeviceMapping.ebs().snapshotId();
final String deviceName = blockDeviceMapping.deviceName();
final String snapshotName = getSnapshotName(deviceName);
getLandscape().setSnapshotName(getHost().getRegion(), snapshotId, snapshotName);
}
}
}
private String getSnapshotName(String deviceName) {
final StringBuilder result = new StringBuilder();
result.append(upgradedImageName);
final String baseName = deviceNamesToSnapshotBaseNames.get(deviceName);
if (baseName != null) {
result.append(" (");
result.append(baseName);
result.append(")");
}
return result.toString();
}
/**
* @return the resulting AMI that has the upgraded version of everything
*/
public MachineImage getUpgradedAmi() {
public AmazonMachineImage<ShardingKey, SailingAnalyticsMetrics> getUpgradedAmi() {
return upgradedAmi;
}
}
@@ -122,6 +122,10 @@ extends Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
AmazonMachineImage<ShardingKey, MetricsT> getLatestImageWithTag(Region region, String tagName, String tagValue);
void setSnapshotName(Region region, String snapshotId, String snapshotName);
void deleteSnapshot(Region region, String snapshotId);
/**
* Finds EC2 instances in the {@code region} that have a tag named {@code tagName} with value {@code tagValue}.
*/
@@ -61,6 +61,12 @@ public class AmazonMachineImageImpl<ShardingKey, MetricsT extends ApplicationPro
@Override
public void delete() {
for (final BlockDeviceMapping blockDeviceMapping : getBlockDeviceMappings()) {
if (blockDeviceMapping.ebs() != null) {
// TODO landscape.deleteSnapshot(blockDeviceMapping.ebs().snapshotId())
}
}
int forceWarningTODO;
// TODO implement AmazonMachineImageImpl.delete(); we probably want landscape here...
}
@@ -455,6 +455,7 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
@Override
public AmazonMachineImage<ShardingKey, MetricsT> createImage(AwsInstance<ShardingKey, MetricsT> instance, String imageName) {
logger.info("Creating Amazon Machine Image (AMI) named "+imageName+" for instance "+instance.getInstanceId());
final Ec2Client client = getEc2Client(getRegion(instance.getRegion()));
final String imageId = client.createImage(b->b
.instanceId(instance.getInstanceId())
@@ -469,6 +470,16 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
return new AmazonMachineImageImpl<>(response.images().stream().max(getMachineImageCreationDateComparator()).get(), region);
}
@Override
public void setSnapshotName(com.sap.sse.landscape.Region region, String snapshotId, String snapshotName) {
getEc2Client(getRegion(region)).createTags(b->b.tags(Tag.builder().key("Name").value(snapshotName).build()));
}
@Override
public void deleteSnapshot(com.sap.sse.landscape.Region region, String snapshotId) {
getEc2Client(getRegion(region)).deleteSnapshot(b->b.snapshotId(snapshotId));
}
@Override
public Iterable<AwsInstance<ShardingKey, MetricsT>> getHostsWithTagValue(com.sap.sse.landscape.Region region,
String tagName, String tagValue) {
@@ -48,17 +48,7 @@ public abstract class StartAwsHost<ShardingKey,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
/**
* The {@link AwsLandscape#getLatestImageWithTag(com.sap.sse.landscape.Region, String, String)} method is
* used to obtain default images for specific AWS host starting procedures that subclass this class. The
* Amazon Machine Images (AMIs) for this are then expected to be tagged with a tag named as specified by this
* constant ("image-type"). The tag value then must match what the subclass wants.
*
* @see #getLatestImageOfType(String)
*/
private final static String IMAGE_TYPE_TAG_NAME = "image-type";
private static final String NAME_TAG_NAME = "Name";
protected static final String NAME_TAG_NAME = "Name";
private final List<String> userData;
private final InstanceType instanceType;
@@ -68,7 +58,6 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
private final Optional<Tags> tags;
private final HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> hostSupplier;
private HostT host;
/**
* A builder that helps building an instance of type {@link StartAwsHost} or any subclass thereof (then using
@@ -110,110 +99,58 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartHost.Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
@Override
AmazonMachineImage<ShardingKey, MetricsT> getMachineImage();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setMachineImage(AmazonMachineImage<ShardingKey, MetricsT> machineImage);
/**
* When not {@code null}, the newest {@link AmazonMachineImage} tagged with a tag named as specified by the constant {@link #IMAGE_TYPE_TAG_NAME}
* with the value provided by the result of this method will be searched and will be used as the default for {@link #getMachineImage()}.
*/
String getImageType();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setImageType(String imageType);
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setImageType(String imageType);
/**
* By default, the release pre-deployed in the image will be used, represented by an empty {@link Optional}
* returned by this default method implementation.
*/
Optional<Release> getRelease();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setRelease(Optional<Release> release);
@Override
AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLandscape(AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape);
InstanceType getInstanceType();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setInstanceType(InstanceType instanceType);
AwsAvailabilityZone getAvailabilityZone();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setAvailabilityZone(AwsAvailabilityZone availabilityZone);
String getKeyName();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setKeyName(String keyName);
Iterable<SecurityGroup> getSecurityGroups();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setSecurityGroups(Iterable<SecurityGroup> securityGroups);
Optional<Tags> getTags();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setTags(Optional<Tags> tags);
Iterable<String> getUserData();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setUserData(String[] userData);
AwsRegion getRegion();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setRegion(AwsRegion region);
String getInstanceName();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setInstanceName(String name);
String getServerName();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setServerName(String serverName);
String getDatabaseName();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setDatabaseName(String databaseName);
Optional<InboundReplicationConfiguration> getInboundReplicationConfiguration();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setReplicationConfiguration(
InboundReplicationConfiguration replicationConfiguration);
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setOutboundReplicationConfiguration(OutboundReplicationConfiguration outboundReplicationConfiguration);
RabbitMQEndpoint getRabbitConfiguration();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setRabbitConfiguration(RabbitMQEndpoint rabbitConfiguration);
Database getDatabaseConfiguration();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setDatabaseConfiguration(Database databaseConfiguration);
String getCommaSeparatedEmailAddressesToNotifyOfStartup();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setCommaSeparatedEmailAddressesToNotifyOfStartup(
String commaSeparatedEmailAddressesToNotifyOfStartup);
/**
* A timeout for interacting with the instance, such as when creating an SSH / SFTP connection or waiting for its
* public IP address.
*/
Optional<Duration> getOptionalTimeout();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setOptionalTimeout(Optional<Duration> optionalTimeout);
HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> getHostSupplier();
OutboundReplicationConfiguration getOutboundReplicationConfiguration();
Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setHostSupplier(HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> hostSupplier);
}
protected abstract static class BuilderImpl<T extends StartAwsHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, 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<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
HostT extends AwsInstance<ShardingKey, MetricsT>>
extends StartHost.BuilderImpl<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
implements Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
private AmazonMachineImage<ShardingKey, MetricsT> machineImage;
private String imageType;
private Optional<Release> release = Optional.empty();
@@ -234,10 +171,10 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
private OutboundReplicationConfiguration outboundReplicationConfiguration;
private String commaSeparatedEmailAddressesToNotifyOfStartup;
private Optional<Duration> optionalTimeout;
private HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> hostSupplier;
@Override
public AmazonMachineImage<ShardingKey, MetricsT> getMachineImage() {
return machineImage == null ? getLatestImageOfType(getImageType(), getLandscape(), getRegion()) : machineImage;
protected AmazonMachineImage<ShardingKey, MetricsT> getMachineImage() {
return machineImage == null ? getLandscape().getLatestImageWithTag((Region) getRegion(), IMAGE_TYPE_TAG_NAME, getImageType()) : machineImage;
}
@Override
@@ -247,8 +184,11 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getImageType() {
/**
* When not {@code null}, the newest {@link AmazonMachineImage} tagged with a tag named as specified by the constant {@link #IMAGE_TYPE_TAG_NAME}
* with the value provided by the result of this method will be searched and will be used as the default for {@link #getMachineImage()}.
*/
protected String getImageType() {
return imageType;
}
@@ -259,8 +199,11 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Optional<Release> getRelease() {
/**
* By default, the release pre-deployed in the image will be used, represented by an empty {@link Optional}
* returned by this default method implementation.
*/
protected Optional<Release> getRelease() {
return release;
}
@@ -270,8 +213,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape() {
protected AwsLandscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape() {
return landscape;
}
@@ -282,8 +224,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public InstanceType getInstanceType() {
protected InstanceType getInstanceType() {
return instanceType;
}
@@ -294,8 +235,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public AwsAvailabilityZone getAvailabilityZone() {
protected AwsAvailabilityZone getAvailabilityZone() {
return availabilityZone == null ? getRandomAvailabilityZone(getRegion(), getLandscape()) : availabilityZone;
}
@@ -306,8 +246,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getKeyName() {
protected String getKeyName() {
return keyName;
}
@@ -317,8 +256,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Iterable<SecurityGroup> getSecurityGroups() {
protected Iterable<SecurityGroup> getSecurityGroups() {
return securityGroups == null ? Collections.singleton(getLandscape().getDefaultSecurityGroupForApplicationHosts(getRegion())) : securityGroups;
}
@@ -329,8 +267,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Optional<Tags> getTags() {
protected Optional<Tags> getTags() {
return tags;
}
@@ -340,8 +277,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Iterable<String> getUserData() {
protected Iterable<String> getUserData() {
return Collections.unmodifiableList(userData);
}
@@ -355,8 +291,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public AwsRegion getRegion() {
protected AwsRegion getRegion() {
return region == null ? getAvailabilityZone().getRegion() : region;
}
@@ -366,8 +301,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getInstanceName() {
protected String getInstanceName() {
return instanceName;
}
@@ -381,8 +315,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getServerName() {
protected String getServerName() {
return serverName;
}
@@ -392,8 +325,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getDatabaseName() {
protected String getDatabaseName() {
return databaseName == null ? getServerName() : databaseName;
}
@@ -403,8 +335,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Database getDatabaseConfiguration() {
protected Database getDatabaseConfiguration() {
return databaseConfiguration == null ? getLandscape().getDatabase(getRegion(), getDatabaseName()) : databaseConfiguration;
}
@@ -414,8 +345,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public RabbitMQEndpoint getRabbitConfiguration() {
protected RabbitMQEndpoint getRabbitConfiguration() {
return rabbitConfiguration;
}
@@ -438,8 +368,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return outboundReplicationConfiguration != null && outboundReplicationConfiguration.getOutboundRabbitMQEndpoint() != null;
}
@Override
public OutboundReplicationConfiguration getOutboundReplicationConfiguration() {
protected OutboundReplicationConfiguration getOutboundReplicationConfiguration() {
final OutboundReplicationConfiguration.Builder resultBuilder;
if (outboundReplicationConfiguration != null) {
resultBuilder = OutboundReplicationConfiguration.copy(outboundReplicationConfiguration);
@@ -461,8 +390,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public Optional<InboundReplicationConfiguration> getInboundReplicationConfiguration() {
protected Optional<InboundReplicationConfiguration> getInboundReplicationConfiguration() {
final InboundReplicationConfiguration.Builder resultBuilder;
if (inboundReplicationConfiguration == null || !inboundReplicationConfiguration.isPresent()) {
resultBuilder = InboundReplicationConfiguration.builder();
@@ -482,8 +410,7 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
public String getCommaSeparatedEmailAddressesToNotifyOfStartup() {
protected String getCommaSeparatedEmailAddressesToNotifyOfStartup() {
return commaSeparatedEmailAddressesToNotifyOfStartup;
}
@@ -493,7 +420,10 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
return this;
}
@Override
/**
* A timeout for interacting with the instance, such as when creating an SSH / SFTP connection or waiting for its
* public IP address.
*/
public Optional<Duration> getOptionalTimeout() {
return optionalTimeout == null ? Optional.empty() : optionalTimeout;
}
@@ -504,9 +434,19 @@ extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>
this.optionalTimeout = optionalTimeout;
return this;
}
protected HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> getHostSupplier() {
return hostSupplier;
}
@Override
public Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setHostSupplier(HostSupplier<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> hostSupplier) {
this.hostSupplier = hostSupplier;
return this;
}
}
protected StartAwsHost(Builder<? extends StartAwsHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
protected StartAwsHost(BuilderImpl<? extends StartAwsHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
super(builder);
this.userData = new ArrayList<>();
for (final String ud : builder.getUserData()) {
@@ -41,5 +41,5 @@ ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterP
*/
Iterable<Region> getRegions();
MachineImage getLatestImageWithTag(Region region, String tagName, String tagValue);
}
@@ -3,6 +3,7 @@ package com.sap.sse.landscape.orchestration;
import com.sap.sse.landscape.Host;
import com.sap.sse.landscape.Landscape;
import com.sap.sse.landscape.MachineImage;
import com.sap.sse.landscape.Region;
import com.sap.sse.landscape.application.ApplicationMasterProcess;
import com.sap.sse.landscape.application.ApplicationProcessMetrics;
import com.sap.sse.landscape.application.ApplicationReplicaProcess;
@@ -14,6 +15,16 @@ public abstract class StartHost<ShardingKey,
HostT extends Host>
extends AbstractProcedureImpl<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>
implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
/**
* The {@link AwsLandscape#getLatestImageWithTag(com.sap.sse.landscape.Region, String, String)} method is
* used to obtain default images for specific AWS host starting procedures that subclass this class. The
* Amazon Machine Images (AMIs) for this are then expected to be tagged with a tag named as specified by this
* constant ("image-type"). The tag value then must match what the subclass wants.
*
* @see #getLatestImageOfType(String)
*/
protected final static String IMAGE_TYPE_TAG_NAME = "image-type";
private final MachineImage machineImage;
/**
@@ -28,13 +39,57 @@ implements Procedure<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> {
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends Host> {
T build();
MachineImage getMachineImage();
Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape();
}
protected StartHost(Builder<? extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
protected abstract static class BuilderImpl<T extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey,
MetricsT extends ApplicationProcessMetrics,
MasterProcessT extends ApplicationMasterProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
ReplicaProcessT extends ApplicationReplicaProcess<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT>,
HostT extends Host>
implements Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> {
private MachineImage machineImage;
private Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape;
private Region region;
private String imageType;
protected MachineImage getMachineImage() {
return machineImage == null ? getLandscape().getLatestImageWithTag(getRegion(), IMAGE_TYPE_TAG_NAME, getImageType()) : machineImage;
}
protected Region getRegion() {
return region;
}
protected Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setRegion(Region region) {
this.region = region;
return this;
}
protected Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setMachineImage(MachineImage machineImage) {
this.machineImage = machineImage;
return this;
}
protected String getImageType() {
return imageType;
}
protected Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setImageType(String imageType) {
this.imageType = imageType;
return this;
}
protected Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> getLandscape() {
return landscape;
}
protected Builder<T, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> setLandscape(Landscape<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT> landscape) {
this.landscape = landscape;
return this;
}
}
protected StartHost(BuilderImpl<? extends StartHost<ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT>, ShardingKey, MetricsT, MasterProcessT, ReplicaProcessT, HostT> builder) {
super(builder.getLandscape());
this.machineImage = builder.getMachineImage();
}