3e1555b7cc189ad070b24d94afa95d6e9fe7921e
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/ApplicationReplicaSetsImagesBarCell.java
| ... | ... | @@ -14,6 +14,7 @@ import com.sap.sse.security.ui.client.UserService; |
| 14 | 14 | public class ApplicationReplicaSetsImagesBarCell extends ImagesBarCell { |
| 15 | 15 | static final String ACTION_REMOVE = DefaultActions.DELETE.name(); |
| 16 | 16 | static final String ACTION_UPGRADE = "UPGRADE"; |
| 17 | + static final String ACTION_ACTIVATE_ARCHIVE_CANDIDATE = "ACTIVATE_ARCHIVE_CANDIDATE"; |
|
| 17 | 18 | static final String ACTION_ARCHIVE = "ARCHIVE"; |
| 18 | 19 | static final String ACTION_DEFINE_LANDING_PAGE = "DEFINE_LANDING_PAGE"; |
| 19 | 20 | static final String ACTION_CREATE_LOAD_BALANCER_MAPPING = "CREATE_LOAD_BALANGER_MAPPING"; |
| ... | ... | @@ -63,6 +64,9 @@ public class ApplicationReplicaSetsImagesBarCell extends ImagesBarCell { |
| 63 | 64 | if (!applicationReplicaSet.isLocalReplicaSet(userService) && !applicationReplicaSet.isArchive()) { |
| 64 | 65 | result.add(new ImageSpec(ACTION_UPGRADE, stringMessages.upgrade(), IconResources.INSTANCE.refreshIcon())); |
| 65 | 66 | } |
| 67 | + if (applicationReplicaSet.isArchive()) { |
|
| 68 | + result.add(new ImageSpec(ACTION_ACTIVATE_ARCHIVE_CANDIDATE, stringMessages.activateArchiveCandidate(), IconResources.INSTANCE.check())); |
|
| 69 | + } |
|
| 66 | 70 | if (!applicationReplicaSet.isArchive()) { |
| 67 | 71 | result.add( |
| 68 | 72 | new ImageSpec(ACTION_ENSURE_ONE_REPLICA_THEN_STOP_REPLICATING_AND_REMOVE_MASTER_FROM_TARGET_GROUPS, |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LandscapeManagementPanel.java
| ... | ... | @@ -334,6 +334,9 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 334 | 334 | } |
| 335 | 335 | } |
| 336 | 336 | ); |
| 337 | + applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_ACTIVATE_ARCHIVE_CANDIDATE, |
|
| 338 | + applicationReplicaSetToActivateAsNewArchive -> makeCandidateArchiveServerGoLive(stringMessages, |
|
| 339 | + regionsTable.getSelectionModel().getSelectedObject(), applicationReplicaSetToActivateAsNewArchive)); |
|
| 337 | 340 | applicationReplicaSetsActionColumn.addAction(ApplicationReplicaSetsImagesBarCell.ACTION_DEFINE_LANDING_PAGE, |
| 338 | 341 | applicationReplicaSetForWhichToDefineLandingPage -> defineLandingPage(stringMessages, |
| 339 | 342 | regionsTable.getSelectionModel().getSelectedObject(), applicationReplicaSetForWhichToDefineLandingPage)); |
| ... | ... | @@ -1458,7 +1461,7 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 1458 | 1461 | } |
| 1459 | 1462 | |
| 1460 | 1463 | private void upgradeArchiveServer(StringMessages stringMessages, String regionId, |
| 1461 | - SailingApplicationReplicaSetDTO<String> replicaSet) { |
|
| 1464 | + SailingApplicationReplicaSetDTO<String> archiveReplicaSet) { |
|
| 1462 | 1465 | landscapeManagementService.getReleases(new AsyncCallback<ArrayList<ReleaseDTO>>() { |
| 1463 | 1466 | @Override |
| 1464 | 1467 | public void onFailure(Throwable caught) { |
| ... | ... | @@ -1471,7 +1474,7 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 1471 | 1474 | stringMessages, errorReporter, new DialogCallback<UpgradeArchiveServerDialog.UpgradeArchiveServerInstructions>() { |
| 1472 | 1475 | @Override |
| 1473 | 1476 | public void ok(UpgradeArchiveServerInstructions upgradeInstructions) { |
| 1474 | - landscapeManagementService.createArchiveReplicaSet(regionId, replicaSet, upgradeInstructions.getInstanceTypeOrNull(), |
|
| 1477 | + landscapeManagementService.createArchiveReplicaSet(regionId, archiveReplicaSet, upgradeInstructions.getInstanceTypeOrNull(), |
|
| 1475 | 1478 | upgradeInstructions.getReleaseNameOrNullForLatestMaster(), sshKeyManagementPanel.getSelectedKeyPair()==null?null:sshKeyManagementPanel.getSelectedKeyPair().getName(), |
| 1476 | 1479 | sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption() != null |
| 1477 | 1480 | ? sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption().getBytes() : null, |
| ... | ... | @@ -1486,7 +1489,7 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 1486 | 1489 | @Override |
| 1487 | 1490 | public void onSuccess(Void result) { |
| 1488 | 1491 | Notification.notify(stringMessages.successfullyLaunchedNewArchiveCandidate( |
| 1489 | - replicaSet.getName(), upgradeInstructions.getReleaseNameOrNullForLatestMaster()), |
|
| 1492 | + archiveReplicaSet.getName(), upgradeInstructions.getReleaseNameOrNullForLatestMaster()), |
|
| 1490 | 1493 | NotificationType.SUCCESS); |
| 1491 | 1494 | } |
| 1492 | 1495 | }); |
| ... | ... | @@ -1500,6 +1503,26 @@ public class LandscapeManagementPanel extends SimplePanel { |
| 1500 | 1503 | }); |
| 1501 | 1504 | } |
| 1502 | 1505 | |
| 1506 | + private void makeCandidateArchiveServerGoLive(StringMessages stringMessages, String regionId, SailingApplicationReplicaSetDTO<String> archiveReplicaSetToUpgrade) { |
|
| 1507 | + landscapeManagementService.makeCandidateArchiveServerGoLive(regionId, archiveReplicaSetToUpgrade, |
|
| 1508 | + sshKeyManagementPanel.getSelectedKeyPair() == null ? null |
|
| 1509 | + : sshKeyManagementPanel.getSelectedKeyPair().getName(), |
|
| 1510 | + sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption() != null |
|
| 1511 | + ? sshKeyManagementPanel.getPassphraseForPrivateKeyDecryption().getBytes() |
|
| 1512 | + : null, |
|
| 1513 | + new AsyncCallback<Void>() { |
|
| 1514 | + @Override |
|
| 1515 | + public void onFailure(Throwable caught) { |
|
| 1516 | + errorReporter.reportError(caught.getMessage()); |
|
| 1517 | + } |
|
| 1518 | + |
|
| 1519 | + @Override |
|
| 1520 | + public void onSuccess(Void result) { |
|
| 1521 | + Notification.notify(stringMessages.successfullySwitchedToNewArchiveCandidate(archiveReplicaSetToUpgrade.getName()), NotificationType.SUCCESS); |
|
| 1522 | + } |
|
| 1523 | + }); |
|
| 1524 | + } |
|
| 1525 | + |
|
| 1503 | 1526 | private void refreshRegionsTable(UserService userService) { |
| 1504 | 1527 | landscapeManagementService.getRegions(new AsyncCallback<ArrayList<String>>() { |
| 1505 | 1528 | @Override |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LandscapeManagementWriteService.java
| ... | ... | @@ -188,4 +188,8 @@ public interface LandscapeManagementWriteService extends RemoteService { |
| 188 | 188 | String optionalKeyName, byte[] privateKeyEncryptionPassphrase) throws Exception; |
| 189 | 189 | |
| 190 | 190 | boolean hasDNSResourceRecordsForReplicaSet(String replicaSetName, String optionalDomainName); |
| 191 | + |
|
| 192 | + void makeCandidateArchiveServerGoLive(String regionId, |
|
| 193 | + SailingApplicationReplicaSetDTO<String> archiveReplicaSetToUpgrade, String optionalKeyName, |
|
| 194 | + byte[] privateKeyEncryptionPassphrase) throws Exception; |
|
| 191 | 195 | } |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LandscapeManagementWriteServiceAsync.java
| ... | ... | @@ -195,6 +195,9 @@ public interface LandscapeManagementWriteServiceAsync { |
| 195 | 195 | byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken, String replicaReplicationBearerToken, |
| 196 | 196 | Integer optionalMemoryInMegabytesOrNull, Integer optionalMemoryTotalSizeFactorOrNull, AsyncCallback<Void> callback); |
| 197 | 197 | |
| 198 | + void makeCandidateArchiveServerGoLive(String regionId, |
|
| 199 | + SailingApplicationReplicaSetDTO<String> archiveReplicaSetToUpgrade, String optionalKeyName, |
|
| 200 | + byte[] privateKeyEncryptionPassphrase, AsyncCallback<Void> callback); |
|
| 198 | 201 | /** |
| 199 | 202 | * For the given replica set ensures there is at least one healthy replica, then stops replicating on all replicas and |
| 200 | 203 | * removes the master from the public and master target groups. This can be used as a preparatory action for upgrading |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.java
| ... | ... | @@ -181,4 +181,6 @@ com.sap.sse.gwt.adminconsole.StringMessages { |
| 181 | 181 | String igtimiRiotPort(); |
| 182 | 182 | String examplePort(int examplePort); |
| 183 | 183 | String successfullyLaunchedNewArchiveCandidate(String replicaSetName, String releaseName); |
| 184 | + String successfullySwitchedToNewArchiveCandidate(String replicaSetName); |
|
| 185 | + String activateArchiveCandidate(); |
|
| 184 | 186 | } |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.properties
| ... | ... | @@ -169,4 +169,6 @@ publicIp=Public IP address |
| 169 | 169 | privateIp=Private IP address |
| 170 | 170 | igtimiRiotPort=Igtimi Riot Port |
| 171 | 171 | examplePort=e.g., {0} |
| 172 | -successfullyLaunchedNewArchiveCandidate=Successfully launched new {0} candidate with release {1}. You will receive an e-mail when the candidate is ready for spot checks and rotation to production. This can take several hours, depending on the number of events to load. |
|
| ... | ... | \ No newline at end of file |
| 0 | +successfullyLaunchedNewArchiveCandidate=Successfully launched new {0} candidate with release {1}. You will receive an e-mail when the candidate is ready for spot checks and rotation to production. This can take several hours, depending on the number of events to load. |
|
| 1 | +successfullySwitchedToNewArchiveCandidate=Successfully switched to new {0} server |
|
| 2 | +activateArchiveCandidate=Activate ARCHIVE candidate (must have run an upgrade before and received the success e-mail!) |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_de.properties
| ... | ... | @@ -168,4 +168,6 @@ publicIp=Öffentlich IP-Adresse |
| 168 | 168 | privateIP=Private IP-Adresse |
| 169 | 169 | igtimiRiotPort=Igtimi Riot Port |
| 170 | 170 | examplePort=z.B. {0} |
| 171 | -successfullyLaunchedNewArchiveCandidate=Neuen Kandidaten für {0} mit Version {1} gestartet. Es erfolgt eine Benachrichtigung per e-Mail. Das kann, je nach Umfang der zu ladenden Daten, etliche Stunden dauern. |
|
| ... | ... | \ No newline at end of file |
| 0 | +successfullyLaunchedNewArchiveCandidate=Neuen Kandidaten für {0} mit Version {1} gestartet. Es erfolgt eine Benachrichtigung per e-Mail. Das kann, je nach Umfang der zu ladenden Daten, etliche Stunden dauern. |
|
| 1 | +successfullySwitchedToNewArchiveCandidate=Erfolgreich auf neuen Server {0} umgeschaltet |
|
| 2 | +activateArchiveCandidate=ARCHIVE-Kandidaten aktivieren (zuvor muss ein Upgrade-Versuch per e-Mail als erfolgreich markiert worden sein!) |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java
| ... | ... | @@ -683,7 +683,6 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem |
| 683 | 683 | String instanceType, String releaseNameOrNullForLatestMaster, String optionalKeyName, |
| 684 | 684 | byte[] privateKeyEncryptionPassphrase, String securityReplicationBearerToken, |
| 685 | 685 | String replicaReplicationBearerToken, Integer optionalMemoryInMegabytesOrNull, Integer optionalMemoryTotalSizeFactorOrNull) throws Exception { |
| 686 | - // TODO bug6203: we also should provide a possibility to specify memory size; if not provided, we should clone the current archive's settings |
|
| 687 | 686 | checkLandscapeManageAwsPermission(); |
| 688 | 687 | final String userSetOrArchiveServerSecurityReplicationBearerToken; |
| 689 | 688 | final AwsRegion region = new AwsRegion(regionId, getLandscape()); |
| ... | ... | @@ -706,10 +705,17 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem |
| 706 | 705 | .createArchiveReplicaSet(regionId, replicaSetName, instanceType, releaseNameOrNullForLatestMaster, |
| 707 | 706 | databaseConfiguration, optionalKeyName, privateKeyEncryptionPassphrase, |
| 708 | 707 | userSetOrArchiveServerSecurityReplicationBearerToken, replicaReplicationBearerToken, domainName, |
| 709 | - /* optionalMemoryInMegabytesOrNull TODO bug6203 */ null, /* optionalMemoryTotalSizeFactorOrNull TODO bug6203 */ null, |
|
| 708 | + optionalMemoryInMegabytesOrNull, optionalMemoryTotalSizeFactorOrNull, |
|
| 710 | 709 | /* optionalIgtimiRiotPort */ null); |
| 711 | 710 | } |
| 712 | 711 | |
| 712 | + @Override |
|
| 713 | + public void makeCandidateArchiveServerGoLive(String regionId, SailingApplicationReplicaSetDTO<String> archiveReplicaSetToUpgrade, |
|
| 714 | + String optionalKeyName, byte[] privateKeyEncryptionPassphrase) throws Exception { |
|
| 715 | + final String domainName = AwsLandscape.getHostedZoneName(archiveReplicaSetToUpgrade.getHostname()); |
|
| 716 | + getLandscapeService().makeCandidateArchiveServerGoLive(regionId, optionalKeyName, privateKeyEncryptionPassphrase, domainName); |
|
| 717 | + } |
|
| 718 | + |
|
| 713 | 719 | /** |
| 714 | 720 | * Starts a first master process of a new replica set whose name is provided by the {@code replicaSetName} |
| 715 | 721 | * parameter. The process is started on the host identified by the {@code hostToDeployTo} parameter. A set of |
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/LandscapeService.java
| ... | ... | @@ -157,6 +157,16 @@ public interface LandscapeService { |
| 157 | 157 | Integer optionalIgtimiRiotPort, Optional<Integer> minimumAutoScalingGroupSize, Optional<Integer> maximumAutoScalingGroupSize) |
| 158 | 158 | throws Exception; |
| 159 | 159 | |
| 160 | + /** |
|
| 161 | + * Runs phase 1 of an ARCHIVE server upgrade. This includes launching the new instance in a favorable availability |
|
| 162 | + * zone where ideally we have a reverse proxy and that ideally is different from the AZ in which the current |
|
| 163 | + * production ARCHIVE server runs. It then installs a {@link ArchiveCandidateMonitoringBackgroundTask background |
|
| 164 | + * task} that keeps applying a sequence of checks. When any of the checks keeps failing beyond a timeout, the |
|
| 165 | + * activity is aborted, and the user who triggered it receives an e-mail about this. If all checks pass, the user |
|
| 166 | + * receives an e-mail that asks for manual spot checks and a confirmation about the rotation. A link embedded in the |
|
| 167 | + * e-mail grants the user easy access to the |
|
| 168 | + * {@link #makeCandidateArchiveServerGoLive(String, String, byte[], String)} method which then performs phase 2. |
|
| 169 | + */ |
|
| 160 | 170 | void createArchiveReplicaSet( |
| 161 | 171 | String regionId, String name, String instanceType, String releaseNameOrNullForLatestMaster, Database databaseConfiguration, |
| 162 | 172 | String optionalKeyName, byte[] privateKeyEncryptionPassphrase, String securityServiceReplicationBearerToken, |
| ... | ... | @@ -164,6 +174,15 @@ public interface LandscapeService { |
| 164 | 174 | Integer optionalMemoryTotalSizeFactorOrNull, Integer optionalIgtimiRiotPort) throws Exception; |
| 165 | 175 | |
| 166 | 176 | /** |
| 177 | + * Phase 2 of an ARCHIVE server upgrade. This is to be triggered ideally after a "human in the loop" step |
|
| 178 | + * where a user makes some spot checks and then confirms that the archive candidate can be installed as the |
|
| 179 | + * new production server, with the previous production server then becoming the failover, and the old failover |
|
| 180 | + * instance being terminated. |
|
| 181 | + */ |
|
| 182 | + void makeCandidateArchiveServerGoLive(String regionId, String optionalKeyName, |
|
| 183 | + byte[] privateKeyEncryptionPassphrase, String optionalDomainName) throws Exception; |
|
| 184 | + |
|
| 185 | + /** |
|
| 167 | 186 | * Starts a first master process of a new replica set whose name is provided by the {@code replicaSetName} |
| 168 | 187 | * parameter. The process is started on the host identified by the {@code hostToDeployTo} parameter. A set of |
| 169 | 188 | * available ports is identified and chosen automatically. The target groups and load balancing set-up is created. |
| ... | ... | @@ -519,7 +538,4 @@ public interface LandscapeService { |
| 519 | 538 | throws MailException; |
| 520 | 539 | |
| 521 | 540 | SailingServerFactory getSailingServerFactory(); |
| 522 | - |
|
| 523 | - void makeCandidateArchiveServerGoLive(String regionId, String optionalKeyName, |
|
| 524 | - byte[] privateKeyEncryptionPassphrase, String optionalDomainName) throws Exception; |
|
| 525 | 541 | } |
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/impl/ArchiveCandidateMonitoringBackgroundTask.java
| ... | ... | @@ -41,6 +41,7 @@ import com.sap.sse.security.shared.impl.User; |
| 41 | 41 | * <li>the one-minute system load average must be below 2 (per cent)</li> |
| 42 | 42 | * <li>the default foreground thread pool queue must contain less than 10 tasks</li> |
| 43 | 43 | * <li>the default background thread pool queue must contain less than 10 tasks</li> |
| 44 | + * <li>the old and new ARCHIVE must compare equal with the {@link SailingServer#compareServers(Optional, SailingServer, Optional)} method</li> |
|
| 44 | 45 | * </ol> |
| 45 | 46 | * |
| 46 | 47 | * When any of these conditions is not fulfilled, the task will re-schedule itself after some delay to check again until |
java/com.sap.sailing.landscape/src/com/sap/sailing/landscape/impl/LandscapeServiceImpl.java
| ... | ... | @@ -253,8 +253,15 @@ public class LandscapeServiceImpl implements LandscapeService { |
| 253 | 253 | } |
| 254 | 254 | final AwsRegion region = new AwsRegion(regionId, landscape); |
| 255 | 255 | final Release release = getRelease(releaseNameOrNullForLatestMaster); |
| 256 | + final AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> oldArchiveReplicaSet = getApplicationReplicaSet( |
|
| 257 | + region, SharedLandscapeConstants.ARCHIVE_SERVER_APPLICATION_REPLICA_SET_NAME, |
|
| 258 | + Landscape.WAIT_FOR_PROCESS_TIMEOUT.map(Duration::asMillis).orElse(null), |
|
| 259 | + optionalKeyName, privateKeyEncryptionPassphrase); |
|
| 260 | + final Integer oldArchiveMemoryInMB = getMemoryInMegabytes(optionalKeyName, privateKeyEncryptionPassphrase, oldArchiveReplicaSet.getMaster()); |
|
| 256 | 261 | final com.sap.sailing.landscape.procedures.SailingAnalyticsMasterConfiguration.Builder<?, String> masterConfigurationBuilder = |
| 257 | - createArchiveConfigurationBuilder(replicaSetName, databaseConfiguration, securityServiceReplicationBearerToken, optionalMemoryInMegabytesOrNull, |
|
| 262 | + createArchiveConfigurationBuilder(replicaSetName, databaseConfiguration, securityServiceReplicationBearerToken, |
|
| 263 | + // if no memory size is specified, use that of existing production ARCHIVE server |
|
| 264 | + optionalMemoryInMegabytesOrNull == null && optionalMemoryTotalSizeFactorOrNull == null ? oldArchiveMemoryInMB : optionalMemoryInMegabytesOrNull, |
|
| 258 | 265 | optionalMemoryTotalSizeFactorOrNull, optionalIgtimiRiotPort, region, release); |
| 259 | 266 | final String bearerTokenUsedByReplicas = getEffectiveBearerToken(replicaReplicationBearerToken); |
| 260 | 267 | final InboundReplicationConfiguration inboundMasterReplicationConfiguration = masterConfigurationBuilder.getInboundReplicationConfiguration().get(); |
| ... | ... | @@ -277,6 +284,7 @@ public class LandscapeServiceImpl implements LandscapeService { |
| 277 | 284 | final StartSailingAnalyticsMasterHost<String> masterHostStartProcedure = masterHostBuilder.build(); |
| 278 | 285 | masterHostStartProcedure.run(); |
| 279 | 286 | final SailingAnalyticsProcess<String> master = masterHostStartProcedure.getSailingAnalyticsProcess(); |
| 287 | + master.getHost().setTerminationProtection(true); |
|
| 280 | 288 | final AwsApplicationReplicaSet<String, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> replicaSet = |
| 281 | 289 | landscape.getApplicationReplicaSet(region, replicaSetName, master, /* replicas */ Collections.emptySet(), |
| 282 | 290 | Landscape.WAIT_FOR_PROCESS_TIMEOUT, Optional.ofNullable(optionalKeyName), privateKeyEncryptionPassphrase); |
| ... | ... | @@ -358,6 +366,7 @@ public class LandscapeServiceImpl implements LandscapeService { |
| 358 | 366 | archiveAndFailoverIPs.getB(), new SailingAnalyticsHostSupplier<>()); |
| 359 | 367 | logger.info("Terminating old failover host " + oldFailover.getInstanceId() + " with internal IP " |
| 360 | 368 | + oldFailover.getPrivateAddress()); |
| 369 | + oldFailover.setTerminationProtection(false); |
|
| 361 | 370 | oldFailover.terminate(); |
| 362 | 371 | logger.info("Removing reverse proxy rule for archive candidate with hostname "+ candidateHostname); |
| 363 | 372 | reverseProxyCluster.removeRedirect(candidateHostname, Optional.ofNullable(optionalKeyName), privateKeyEncryptionPassphrase); |
java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/check.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sse.gwt/resources/com/sap/sse/gwt/client/images/check.png differ |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/IconResources.java
| ... | ... | @@ -85,4 +85,7 @@ public interface IconResources extends ClientBundle { |
| 85 | 85 | |
| 86 | 86 | @Source("images/command_symbol.png") |
| 87 | 87 | ImageResource commandSymbol(); |
| 88 | + |
|
| 89 | + @Source("images/check.png") |
|
| 90 | + ImageResource check(); |
|
| 88 | 91 | } |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsInstance.java
| ... | ... | @@ -53,6 +53,8 @@ public interface AwsInstance<ShardingKey> extends Host { |
| 53 | 53 | default String getId() { |
| 54 | 54 | return getInstanceId(); |
| 55 | 55 | } |
| 56 | + |
|
| 57 | + void setTerminationProtection(boolean terminationProtection); |
|
| 56 | 58 | |
| 57 | 59 | void terminate(); |
| 58 | 60 |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
| ... | ... | @@ -288,6 +288,8 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> { |
| 288 | 288 | */ |
| 289 | 289 | SSHKeyPair importKeyPair(Region region, byte[] publicKey, byte[] encryptedPrivateKey, String keyName) throws JSchException; |
| 290 | 290 | |
| 291 | + void setTerminationProtection(AwsInstance<ShardingKey> host, boolean terminationProtection); |
|
| 292 | + |
|
| 291 | 293 | void terminate(AwsInstance<ShardingKey> host); |
| 292 | 294 | |
| 293 | 295 | /** |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/ReverseProxy.java
| ... | ... | @@ -133,5 +133,5 @@ public interface ReverseProxy<ShardingKey, MetricsT extends ApplicationProcessMe |
| 133 | 133 | */ |
| 134 | 134 | Pair<String, String> getArchiveAndFailoverIPs(Optional<String> optionalKeyName, byte[] privateKeyEncryptionPassphrase) throws Exception; |
| 135 | 135 | |
| 136 | - void setArchiveAndFailoverIPs(String hostAddress, String b, Optional<String> ofNullable, byte[] privateKeyEncryptionPassphrase); |
|
| 136 | + void setArchiveAndFailoverIPs(String hostAddress, String b, Optional<String> ofNullable, byte[] privateKeyEncryptionPassphrase) throws Exception; |
|
| 137 | 137 | } |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/ApacheReverseProxy.java
| ... | ... | @@ -123,8 +123,9 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> { |
| 123 | 123 | |
| 124 | 124 | @Override |
| 125 | 125 | public Pair<String, String> getArchiveAndFailoverIPs(Optional<String> optionalKeyName, byte[] privateKeyEncryptionPassphrase) throws Exception { |
| 126 | - final String command = "cat "+CONFIG_REPO_PATH+"/"+RELATIVE_CONFIG_PATH+"/"+CONFIG_FILE_FOR_ARCHIVE_AND_FAILOVER_DEFINITION+" | grep \"^Define "+ARCHIVE_IP+"\" | sed -e 's/^Define "+ARCHIVE_IP+" //'; " |
|
| 127 | - + "cat "+CONFIG_REPO_PATH+"/"+RELATIVE_CONFIG_PATH+"/"+CONFIG_FILE_FOR_ARCHIVE_AND_FAILOVER_DEFINITION+" | grep \"^Define "+ARCHIVE_FAILOVER_IP+"\" | sed -e 's/^Define "+ARCHIVE_FAILOVER_IP+" //'"; |
|
| 126 | + final String absolute000MacrosConfigFilePath = getAbsoluteConfigFilePath(CONFIG_FILE_FOR_ARCHIVE_AND_FAILOVER_DEFINITION); |
|
| 127 | + final String command = "cat "+absolute000MacrosConfigFilePath+" | grep \"^Define "+ARCHIVE_IP+"\" | sed -e 's/^Define "+ARCHIVE_IP+" //'; " |
|
| 128 | + + "cat "+absolute000MacrosConfigFilePath+" | grep \"^Define "+ARCHIVE_FAILOVER_IP+"\" | sed -e 's/^Define "+ARCHIVE_FAILOVER_IP+" //'"; |
|
| 128 | 129 | final String[] archiveAndFailoverIPs = runCommandAndReturnStdoutAndLogStderr(command, |
| 129 | 130 | "Standard error from getting "+ARCHIVE_IP+" and "+ARCHIVE_FAILOVER_IP+": ", |
| 130 | 131 | Level.INFO, optionalKeyName, privateKeyEncryptionPassphrase).split("\n"); |
| ... | ... | @@ -132,10 +133,17 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> { |
| 132 | 133 | } |
| 133 | 134 | |
| 134 | 135 | @Override |
| 135 | - public void setArchiveAndFailoverIPs(String hostAddress, String b, Optional<String> ofNullable, |
|
| 136 | - byte[] privateKeyEncryptionPassphrase) { |
|
| 137 | - // TODO Auto-generated method stub |
|
| 138 | - |
|
| 136 | + public void setArchiveAndFailoverIPs(String productionArchiveIP, String failoverArchiveIP, Optional<String> optionalKeyName, |
|
| 137 | + byte[] privateKeyEncryptionPassphrase) throws Exception { |
|
| 138 | + final String absolute000MacrosConfigFilePath = getAbsoluteConfigFilePath(CONFIG_FILE_FOR_ARCHIVE_AND_FAILOVER_DEFINITION); |
|
| 139 | + final SshCommandChannel sshChannel = getHost().createRootSshChannel(TIMEOUT, optionalKeyName, privateKeyEncryptionPassphrase); |
|
| 140 | + String patch000MacrosCommand = "su - " + CONFIG_USER + " -c 'cd " + CONFIG_REPO_PATH + " && git checkout " |
|
| 141 | + + CONFIG_REPO_MAIN_BRANCH_NAME |
|
| 142 | + + " && sed -i -e \"s/^Define "+ARCHIVE_IP+" .*$/Define "+ARCHIVE_IP+" "+productionArchiveIP+"/\" -e \"s/^Define "+ARCHIVE_FAILOVER_IP+" .*$/Define "+ARCHIVE_FAILOVER_IP+" "+failoverArchiveIP+"/\" "+absolute000MacrosConfigFilePath |
|
| 143 | + + " && " + createCommitAndPushString(CONFIG_FILE_FOR_ARCHIVE_AND_FAILOVER_DEFINITION, "Switching to new ARCHIVE server", /* performPush */ true) |
|
| 144 | + + "'"; // concludes the "su"; re-loading is expected to happen through the post-receive hook triggered by the push |
|
| 145 | + final String stdout = sshChannel.runCommandAndReturnStdoutAndLogStderr(patch000MacrosCommand, "Standard error from switching to new ARCHIVE server", Level.WARNING); |
|
| 146 | + logger.info("Stdout from upgrading to new ARCHIVE: "+stdout); |
|
| 139 | 147 | } |
| 140 | 148 | |
| 141 | 149 | /** |
| ... | ... | @@ -165,7 +173,7 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> { |
| 165 | 173 | + CONFIG_REPO_MAIN_BRANCH_NAME + " && echo \"Use " + macroName + " " + hostname + " " |
| 166 | 174 | + String.join(" ", macroArguments) + "\" > " + getAbsoluteConfigFilePath(configFileNameForHostname); |
| 167 | 175 | if (doCommit) { |
| 168 | - command = command + " && cd " |
|
| 176 | + command = command + " && cd " |
|
| 169 | 177 | + CONFIG_REPO_PATH + " && " + createCommitAndPushString(configFileNameForHostname, |
| 170 | 178 | "Set " + configFileNameForHostname + " redirect", doPush); |
| 171 | 179 | } |
| ... | ... | @@ -199,10 +207,16 @@ implements com.sap.sse.landscape.Process<RotatingFileBasedLog, MetricsT> { |
| 199 | 207 | } |
| 200 | 208 | |
| 201 | 209 | /** |
| 202 | - * Creates a command, that can be ran on an instance to commit, and optionally push, changes to a file (within a git repository). ASSUMES the command is ran from within the repository. |
|
| 203 | - * @param editedFileName The file name edited, created or deleted to commit. This includes the {@link #CONFIG_FILE_EXTENSION}, but not a path. The method appends the relative path. |
|
| 204 | - * @param commitMsg The commit message, without escaped speech marks. |
|
| 205 | - * @param performPush Boolean indicating whether to push changes or not. True for performing a push. |
|
| 210 | + * Creates a command, that can be ran on an instance to commit, and optionally push, changes to a file (within a git |
|
| 211 | + * repository). ASSUMES the command is ran from within the repository. |
|
| 212 | + * |
|
| 213 | + * @param editedFileName |
|
| 214 | + * The file name edited, created or deleted to commit. This includes the {@link #CONFIG_FILE_EXTENSION}, |
|
| 215 | + * but not a path. The method appends the relative path. |
|
| 216 | + * @param commitMsg |
|
| 217 | + * The commit message, without escaped speech marks. |
|
| 218 | + * @param performPush |
|
| 219 | + * Boolean indicating whether to push changes or not. True for performing a push. |
|
| 206 | 220 | * @return Returns the created command (in String form) to perform a commit and optional push. |
| 207 | 221 | */ |
| 208 | 222 | private String createCommitAndPushString(String editedFileName, String commitMsg, boolean performPush) { |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/ApacheReverseProxyCluster.java
| ... | ... | @@ -259,4 +259,13 @@ public class ApacheReverseProxyCluster<ShardingKey, MetricsT extends Application |
| 259 | 259 | byte[] privateKeyEncryptionPassphrase) throws Exception { |
| 260 | 260 | return getReverseProxies().iterator().next().getArchiveAndFailoverIPs(optionalKeyName, privateKeyEncryptionPassphrase); |
| 261 | 261 | } |
| 262 | + |
|
| 263 | + @Override |
|
| 264 | + public void setArchiveAndFailoverIPs(String hostAddress, String b, Optional<String> optionalKeyName, |
|
| 265 | + byte[] privateKeyEncryptionPassphrase) throws Exception { |
|
| 266 | + if (getReverseProxies().iterator().hasNext()) { |
|
| 267 | + final ApacheReverseProxy<ShardingKey, MetricsT, ProcessT> proxy = getReverseProxies().iterator().next(); |
|
| 268 | + proxy.getArchiveAndFailoverIPs(optionalKeyName, privateKeyEncryptionPassphrase); |
|
| 269 | + } |
|
| 270 | + } |
|
| 262 | 271 | } |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsInstanceImpl.java
| ... | ... | @@ -334,6 +334,11 @@ public class AwsInstanceImpl<ShardingKey> implements AwsInstance<ShardingKey> { |
| 334 | 334 | } |
| 335 | 335 | |
| 336 | 336 | @Override |
| 337 | + public void setTerminationProtection(boolean terminationProtection) { |
|
| 338 | + landscape.setTerminationProtection(this, terminationProtection); |
|
| 339 | + } |
|
| 340 | + |
|
| 341 | + @Override |
|
| 337 | 342 | public void terminate() { |
| 338 | 343 | landscape.terminate(this); |
| 339 | 344 | } |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
| ... | ... | @@ -1069,6 +1069,12 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey> |
| 1069 | 1069 | } |
| 1070 | 1070 | |
| 1071 | 1071 | @Override |
| 1072 | + public void setTerminationProtection(AwsInstance<ShardingKey> host, boolean terminationProtection) { |
|
| 1073 | + logger.info("Setting termination protection for instance "+host+" to "+terminationProtection); |
|
| 1074 | + getEc2Client(getRegion(host.getAvailabilityZone().getRegion())).modifyInstanceAttribute(b->b.disableApiTermination(a->a.value(terminationProtection))); |
|
| 1075 | + } |
|
| 1076 | + |
|
| 1077 | + @Override |
|
| 1072 | 1078 | public void terminate(AwsInstance<ShardingKey> host) { |
| 1073 | 1079 | logger.info("Terminating instance "+host); |
| 1074 | 1080 | getEc2Client(getRegion(host.getAvailabilityZone().getRegion())).terminateInstances( |