java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/server/LandscapeManagementWriteServiceImpl.java
... ...
@@ -81,6 +81,7 @@ import com.sap.sse.landscape.aws.impl.AwsApplicationReplicaSetImpl;
81 81
import com.sap.sse.landscape.aws.impl.AwsAvailabilityZoneImpl;
82 82
import com.sap.sse.landscape.aws.impl.AwsInstanceImpl;
83 83
import com.sap.sse.landscape.aws.impl.AwsRegion;
84
+import com.sap.sse.landscape.aws.impl.DNSCache;
84 85
import com.sap.sse.landscape.aws.orchestration.CreateDNSBasedLoadBalancerMapping;
85 86
import com.sap.sse.landscape.aws.orchestration.CreateDynamicLoadBalancerMapping;
86 87
import com.sap.sse.landscape.aws.orchestration.CreateLoadBalancerMapping;
... ...
@@ -562,9 +563,10 @@ public class LandscapeManagementWriteServiceImpl extends ResultCachingProxiedRem
562 563
final CompletableFuture<Iterable<ApplicationLoadBalancer<String>>> allLoadBalancersInRegion = landscape.getLoadBalancersAsync(region);
563 564
final CompletableFuture<Map<TargetGroup<String>, Iterable<TargetHealthDescription>>> allTargetGroupsInRegion = landscape.getTargetGroupsAsync(region);
564 565
final CompletableFuture<Map<Listener, Iterable<Rule>>> allLoadBalancerRulesInRegion = landscape.getLoadBalancerListenerRulesAsync(region, allLoadBalancersInRegion);
566
+ final DNSCache dnsCache = landscape.getNewDNSCache();
565 567
final ApplicationReplicaSet<String,SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> applicationReplicaSet =
566 568
new AwsApplicationReplicaSetImpl<>(name, masterHostname, master, /* no replicas yet */ Optional.empty(),
567
- allLoadBalancersInRegion, allTargetGroupsInRegion, allLoadBalancerRulesInRegion);
569
+ allLoadBalancersInRegion, allTargetGroupsInRegion, allLoadBalancerRulesInRegion, landscape, dnsCache);
568 570
final CreateLaunchConfigurationAndAutoScalingGroup.Builder<String, ?, SailingAnalyticsMetrics, SailingAnalyticsProcess<String>> createLaunchConfigurationAndAutoScalingGroupBuilder =
569 571
CreateLaunchConfigurationAndAutoScalingGroup.builder(landscape, region, applicationReplicaSet, userBearerToken, createLoadBalancerMapping.getPublicTargetGroup());
570 572
createLaunchConfigurationAndAutoScalingGroupBuilder
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
... ...
@@ -23,6 +23,7 @@ import com.sap.sse.landscape.aws.impl.AwsInstanceImpl;
23 23
import com.sap.sse.landscape.aws.impl.AwsLandscapeImpl;
24 24
import com.sap.sse.landscape.aws.impl.AwsRegion;
25 25
import com.sap.sse.landscape.aws.impl.AwsTargetGroupImpl;
26
+import com.sap.sse.landscape.aws.impl.DNSCache;
26 27
import com.sap.sse.landscape.aws.orchestration.AwsApplicationConfiguration;
27 28
import com.sap.sse.landscape.mongodb.Database;
28 29
import com.sap.sse.landscape.mongodb.MongoEndpoint;
... ...
@@ -51,6 +52,7 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetHealth
51 52
import software.amazon.awssdk.services.route53.Route53Client;
52 53
import software.amazon.awssdk.services.route53.model.ChangeInfo;
53 54
import software.amazon.awssdk.services.route53.model.RRType;
55
+import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
54 56
import software.amazon.awssdk.services.sts.model.Credentials;
55 57
56 58
/**
... ...
@@ -620,4 +622,8 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> {
620 622
static String getHostedZoneName(String hostname) {
621 623
return hostname.substring(hostname.indexOf('.')+1);
622 624
}
625
+
626
+ CompletableFuture<Iterable<ResourceRecordSet>> getResourceRecordSetsAsync(String hostname);
627
+
628
+ DNSCache getNewDNSCache();
623 629
}
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsApplicationReplicaSetImpl.java
... ...
@@ -18,6 +18,7 @@ import com.sap.sse.landscape.application.impl.ApplicationReplicaSetImpl;
18 18
import com.sap.sse.landscape.aws.ApplicationLoadBalancer;
19 19
import com.sap.sse.landscape.aws.AwsApplicationReplicaSet;
20 20
import com.sap.sse.landscape.aws.AwsAutoScalingGroup;
21
+import com.sap.sse.landscape.aws.AwsLandscape;
21 22
import com.sap.sse.landscape.aws.TargetGroup;
22 23
23 24
import software.amazon.awssdk.services.autoscaling.model.AutoScalingGroup;
... ...
@@ -28,6 +29,7 @@ import software.amazon.awssdk.services.elasticloadbalancingv2.model.ProtocolEnum
28 29
import software.amazon.awssdk.services.elasticloadbalancingv2.model.Rule;
29 30
import software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetHealthDescription;
30 31
import software.amazon.awssdk.services.route53.Route53AsyncClient;
32
+import software.amazon.awssdk.services.route53.model.RRType;
31 33
import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
32 34
33 35
/**
... ...
@@ -57,12 +59,14 @@ implements AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> {
57 59
private final CompletableFuture<TargetGroup<ShardingKey>> masterTargetGroup;
58 60
private final CompletableFuture<TargetGroup<ShardingKey>> publicTargetGroup;
59 61
private final CompletableFuture<ResourceRecordSet> resourceRecordSet;
62
+ private final AwsLandscape<ShardingKey> landscape;
60 63
61 64
public AwsApplicationReplicaSetImpl(String replicaSetAndServerName, String hostname, ProcessT master, Optional<Iterable<ProcessT>> replicas,
62 65
CompletableFuture<Iterable<ApplicationLoadBalancer<ShardingKey>>> allLoadBalancersInRegion,
63 66
CompletableFuture<Map<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>>> allTargetGroupsInRegion,
64
- CompletableFuture<Map<Listener, Iterable<Rule>>> allLoadBalancerRulesInRegion) {
67
+ CompletableFuture<Map<Listener, Iterable<Rule>>> allLoadBalancerRulesInRegion, AwsLandscape<ShardingKey> landscape, DNSCache dnsCache) {
65 68
super(replicaSetAndServerName, hostname, master, replicas);
69
+ this.landscape = landscape;
66 70
autoScalingGroup = new CompletableFuture<>();
67 71
defaultRedirectRule = new CompletableFuture<>();
68 72
hostedZoneId = new CompletableFuture<>();
... ...
@@ -73,7 +77,7 @@ implements AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> {
73 77
resourceRecordSet = new CompletableFuture<>();
74 78
allLoadBalancersInRegion.thenCompose(loadBalancers->
75 79
allTargetGroupsInRegion.thenCompose(targetGroupsAndTheirTargetHealthDescriptions->
76
- allLoadBalancerRulesInRegion.handle((listenersAndTheirRules, e)->establishState(loadBalancers, targetGroupsAndTheirTargetHealthDescriptions, listenersAndTheirRules))))
80
+ allLoadBalancerRulesInRegion.handle((listenersAndTheirRules, e)->establishState(loadBalancers, targetGroupsAndTheirTargetHealthDescriptions, listenersAndTheirRules, dnsCache))))
77 81
.handle((v, e)->{
78 82
if (e != null) {
79 83
logger.log(Level.SEVERE, "Exception while trying to establish state of application replica set "+getName(), e);
... ...
@@ -82,11 +86,14 @@ implements AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> {
82 86
});
83 87
}
84 88
85
- public AwsApplicationReplicaSetImpl(String replicaSetAndServerName, ProcessT master, Optional<Iterable<ProcessT>> replicas,
89
+ public AwsApplicationReplicaSetImpl(String replicaSetAndServerName, ProcessT master,
90
+ Optional<Iterable<ProcessT>> replicas,
86 91
CompletableFuture<Iterable<ApplicationLoadBalancer<ShardingKey>>> allLoadBalancersInRegion,
87 92
CompletableFuture<Map<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>>> allTargetGroupsInRegion,
88
- CompletableFuture<Map<Listener, Iterable<Rule>>> allLoadBalancerRulesInRegion) {
89
- this(replicaSetAndServerName, /* hostname to be inferred */ null, master, replicas, allLoadBalancersInRegion, allTargetGroupsInRegion, allLoadBalancerRulesInRegion);
93
+ CompletableFuture<Map<Listener, Iterable<Rule>>> allLoadBalancerRulesInRegion,
94
+ AwsLandscape<ShardingKey> landscape, DNSCache dnsCache) {
95
+ this(replicaSetAndServerName, /* hostname to be inferred */ null, master, replicas, allLoadBalancersInRegion,
96
+ allTargetGroupsInRegion, allLoadBalancerRulesInRegion, landscape, dnsCache);
90 97
}
91 98
92 99
/**
... ...
@@ -126,7 +133,7 @@ implements AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> {
126 133
*/
127 134
private Void establishState(Iterable<ApplicationLoadBalancer<ShardingKey>> loadBalancers,
128 135
Map<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>> targetGroupsAndTheirTargetHealthDescriptions,
129
- Map<Listener, Iterable<Rule>> listenersAndTheirRules) {
136
+ Map<Listener, Iterable<Rule>> listenersAndTheirRules, DNSCache dnsCache) {
130 137
TargetGroup<ShardingKey> myMasterTargetGroup = null;
131 138
for (final Entry<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>> e : targetGroupsAndTheirTargetHealthDescriptions.entrySet()) {
132 139
if ((e.getKey().getProtocol() == ProtocolEnum.HTTP || e.getKey().getProtocol() == ProtocolEnum.HTTPS)
... ...
@@ -158,6 +165,25 @@ implements AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> {
158 165
// and we can determine the load balancer via the Listener now:
159 166
myLoadBalancer = Util.first(Util.filter(loadBalancers, loadBalancer->loadBalancer.getArn().equals(e.getKey().loadBalancerArn())));
160 167
loadBalancer.complete(myLoadBalancer);
168
+ dnsCache.getHostedZoneId(AwsLandscape.getHostedZoneName(hostname)).handle((hzid, ex)->hostedZoneId.complete(hzid));
169
+ dnsCache.getResourceRecordSetsAsync(hostname).thenAccept(resourceRecordSets->
170
+ Util.stream(resourceRecordSets).findFirst().ifPresent(rrs->{
171
+ resourceRecordSet.complete(rrs);
172
+ try {
173
+ if (rrs.type() == RRType.CNAME && !Util.isEmpty(Util.filter(rrs.resourceRecords(), rr->{
174
+ try {
175
+ return rr.value().equals(getLoadBalancer().getDNSName());
176
+ } catch (InterruptedException | ExecutionException e1) {
177
+ logger.log(Level.WARNING, "This shouldn't have happened", e1);
178
+ throw new RuntimeException(e1);
179
+ }
180
+ }))) {
181
+ logger.fine("Found DNS resource record "+getHostname()+" pointing to application replica set's load balancer "+getLoadBalancer().getArn());
182
+ }
183
+ } catch (InterruptedException | ExecutionException e1) {
184
+ logger.log(Level.WARNING, "This shouldn't have happened", e1);
185
+ }
186
+ }));
161 187
break outer;
162 188
}
163 189
}
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
... ...
@@ -157,9 +157,6 @@ import software.amazon.awssdk.services.route53.model.ChangeInfo;
157 157
import software.amazon.awssdk.services.route53.model.ChangeResourceRecordSetsRequest;
158 158
import software.amazon.awssdk.services.route53.model.ChangeResourceRecordSetsResponse;
159 159
import software.amazon.awssdk.services.route53.model.GetChangeRequest;
160
-import software.amazon.awssdk.services.route53.model.HostedZone;
161
-import software.amazon.awssdk.services.route53.model.ListResourceRecordSetsRequest;
162
-import software.amazon.awssdk.services.route53.model.ListResourceRecordSetsResponse;
163 160
import software.amazon.awssdk.services.route53.model.RRType;
164 161
import software.amazon.awssdk.services.route53.model.ResourceRecord;
165 162
import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
... ...
@@ -355,8 +352,7 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
355 352
356 353
@Override
357 354
public Iterable<TargetGroup<ShardingKey>> getTargetGroupsByLoadBalancerArn(com.sap.sse.landscape.Region region, String loadBalancerArn) {
358
- // FIXME handle "paging" in case nextMarker() is set in the response; see also describeTargetGroupsPaginator
359
- return Util.map(getLoadBalancingClient(getRegion(region)).describeTargetGroups(tg->tg.loadBalancerArn(loadBalancerArn)).targetGroups(),
355
+ return Util.map(getLoadBalancingClient(getRegion(region)).describeTargetGroupsPaginator(tg->tg.loadBalancerArn(loadBalancerArn)).targetGroups(),
360 356
tg->new AwsTargetGroupImpl<>(this, region, tg.targetGroupName(), tg.targetGroupArn(), tg.loadBalancerArns().iterator().next(),
361 357
tg.protocol(), tg.port(), tg.healthCheckProtocol(), getHealthCheckPort(tg), tg.healthCheckPath()));
362 358
}
... ...
@@ -1244,10 +1240,11 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1244 1240
}
1245 1241
backgroundExecutor.shutdown();
1246 1242
final Set<AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT>> result = new HashSet<>();
1243
+ final DNSCache dnsCache = getNewDNSCache();
1247 1244
for (final Entry<String, ProcessT> serverNameAndMaster : mastersByServerName.entrySet()) {
1248 1245
final AwsApplicationReplicaSet<ShardingKey, MetricsT, ProcessT> replicaSet = new AwsApplicationReplicaSetImpl<ShardingKey, MetricsT, ProcessT>(serverNameAndMaster.getKey(),
1249 1246
serverNameAndMaster.getValue(), Optional.ofNullable(replicasByServerName.get(serverNameAndMaster.getKey())),
1250
- allLoadBalancersInRegion, allTargetGroupsInRegion, allLoadBalancerRulesInRegion);
1247
+ allLoadBalancersInRegion, allTargetGroupsInRegion, allLoadBalancerRulesInRegion, this, dnsCache);
1251 1248
result.add(replicaSet);
1252 1249
}
1253 1250
return result;
... ...
@@ -1302,40 +1299,12 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1302 1299
return result;
1303 1300
}
1304 1301
1305
- private <MetricsT extends ApplicationProcessMetrics, ProcessT extends ApplicationProcess<ShardingKey, MetricsT, ProcessT>>
1306
- String findHostnameForProcessThroughLoadBalancers(com.sap.sse.landscape.Region region, ProcessT process) {
1307
- final ApplicationLoadBalancer<ShardingKey> loadBalancer = findLoadBalancerForProcess(region, process);
1308
- // FIXME loadBalancer may be null / not found, especially when the process is served only through the reverse proxy, such as for the ARCHIVE server
1309
- String result;
1310
- if (loadBalancer == null) {
1311
- logger.info("Using default hostname for process running on "+process.getHost().getId());
1312
- // FIXME this seems a strange default:
1313
- result = "www.sapsailing.com";
1314
- } else {
1315
- result = null;
1316
- // FIXME many DNS entries may point to the same load balancer; how do we know it's the correct one? --> Disambiguation...
1317
- final String loadBalancerDNSName = loadBalancer.getDNSName();
1318
- final Route53Client route53Client = getRoute53Client();
1319
- outer: for (final HostedZone hostedZone : route53Client.listHostedZones().hostedZones()) {
1320
- ListResourceRecordSetsResponse resourceRecordSetsResponse = null;
1321
- do {
1322
- final ListResourceRecordSetsRequest.Builder resourceRecordSetsRequestBuilder = ListResourceRecordSetsRequest.builder();
1323
- resourceRecordSetsRequestBuilder.hostedZoneId(hostedZone.id());
1324
- if (resourceRecordSetsResponse != null) {
1325
- assert resourceRecordSetsResponse.isTruncated();
1326
- resourceRecordSetsRequestBuilder.startRecordName(resourceRecordSetsResponse.nextRecordName());
1327
- }
1328
- resourceRecordSetsResponse = route53Client.listResourceRecordSets(resourceRecordSetsRequestBuilder.build());
1329
- for (final ResourceRecordSet resourceRecordSet : resourceRecordSetsResponse.resourceRecordSets()) {
1330
- if (resourceRecordSet.type() == RRType.CNAME && !Util.isEmpty(Util.filter(resourceRecordSet.resourceRecords(), rr->rr.value().equals(loadBalancerDNSName)))) {
1331
- result = resourceRecordSet.name().replaceFirst("\\.$", ""); // remove trailing dots
1332
- break outer;
1333
- }
1334
- }
1335
- } while (resourceRecordSetsResponse.isTruncated());
1336
- }
1337
- }
1338
- return result;
1302
+ @Override
1303
+ public CompletableFuture<Iterable<ResourceRecordSet>> getResourceRecordSetsAsync(String hostname) {
1304
+ final Route53AsyncClient route53Client = getRoute53AsyncClient();
1305
+ final String hostedZoneId = getDNSHostedZoneId(AwsLandscape.getHostedZoneName(hostname));
1306
+ return route53Client.listResourceRecordSets(b->b.hostedZoneId(hostedZoneId).startRecordName(hostname)).handle((response, e)->
1307
+ Util.filter(response.resourceRecordSets(), resourceRecordSet->resourceRecordSet.name().replaceFirst("\\.$", "").equals(hostname)));
1339 1308
}
1340 1309
1341 1310
@Override
... ...
@@ -1400,15 +1369,18 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1400 1369
1401 1370
@Override
1402 1371
public CompletableFuture<Map<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>>> getTargetGroupsAsync(com.sap.sse.landscape.Region region) {
1403
- // FIXME handle paging for large target groups response if nextMarker() is set in response; see also DescribeTargetGroupsResponseFetcher and ; see also describeTargetGroupsPaginator
1404
- return getLoadBalancingAsyncClient(getRegion(region)).describeTargetGroups().thenCompose(response->{
1372
+ final Set<DescribeTargetGroupsResponse> responses = new HashSet<>();
1373
+ return getLoadBalancingAsyncClient(getRegion(region)).describeTargetGroupsPaginator().subscribe(response->responses.add(response)).thenCompose(someVoid->{
1374
+ // now we have all responses
1405 1375
final Map<TargetGroup<ShardingKey>, CompletableFuture<Iterable<TargetHealthDescription>>> futures = new HashMap<>();
1406
- for (final software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetGroup tg : response.targetGroups()) {
1407
- final TargetGroup<ShardingKey> targetGroup = new AwsTargetGroupImpl<ShardingKey>(this, region,
1408
- tg.targetGroupName(), tg.targetGroupArn(), Util.first(tg.loadBalancerArns()),
1409
- tg.protocol(), tg.port(), tg.healthCheckProtocol(), getHealthCheckPort(tg),
1410
- tg.healthCheckPath());
1411
- futures.put(targetGroup, getTargetHealthDescriptionsAsync(region, targetGroup));
1376
+ for (final DescribeTargetGroupsResponse response : responses) {
1377
+ for (final software.amazon.awssdk.services.elasticloadbalancingv2.model.TargetGroup tg : response.targetGroups()) {
1378
+ final TargetGroup<ShardingKey> targetGroup = new AwsTargetGroupImpl<ShardingKey>(this, region,
1379
+ tg.targetGroupName(), tg.targetGroupArn(), Util.first(tg.loadBalancerArns()),
1380
+ tg.protocol(), tg.port(), tg.healthCheckProtocol(), getHealthCheckPort(tg),
1381
+ tg.healthCheckPath());
1382
+ futures.put(targetGroup, getTargetHealthDescriptionsAsync(region, targetGroup));
1383
+ }
1412 1384
}
1413 1385
return CompletableFuture.allOf(futures.values().toArray(new CompletableFuture<?>[0])).handle((v, e)->{
1414 1386
final Map<TargetGroup<ShardingKey>, Iterable<TargetHealthDescription>> result = new HashMap<>();
... ...
@@ -1424,25 +1396,6 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1424 1396
});
1425 1397
}
1426 1398
1427
- private <MetricsT extends ApplicationProcessMetrics, ProcessT extends ApplicationProcess<ShardingKey, MetricsT, ProcessT>>
1428
- ApplicationLoadBalancer<ShardingKey> findLoadBalancerForProcess(com.sap.sse.landscape.Region region, ProcessT process) {
1429
- for (final ApplicationLoadBalancer<ShardingKey> loadBalancer : getLoadBalancers(region)) {
1430
- for (final TargetGroup<ShardingKey> targetGroup : loadBalancer.getTargetGroups()) {
1431
- // first check that the target group is forwarding to the correct port:
1432
- // FIXME What about the case where the target group health-checks the targets through the HTTPS port through the reverse proxy? The "backward-compatible" case...
1433
- if (Util.equalsWithNull(targetGroup.getHealthCheckPort(), process.getPort())) {
1434
- // then check whether the process is part of targets registered:
1435
- final Optional<Entry<AwsInstance<ShardingKey>, TargetHealth>> target = targetGroup.getRegisteredTargets().entrySet().stream()
1436
- .filter(e->e.getKey().getInstanceId().equals(process.getHost().getId())).findAny();
1437
- if (target.isPresent()) {
1438
- return loadBalancer;
1439
- }
1440
- }
1441
- }
1442
- }
1443
- return null;
1444
- }
1445
-
1446 1399
@Override
1447 1400
public AwsRegion getDefaultRegion() {
1448 1401
return new AwsRegion(Region.EU_WEST_2); // TODO actually, EU_WEST_1 (Ireland) is our default region, but as long as this is under development, EU_WEST_2 gives us an isolated test environment
... ...
@@ -1507,4 +1460,9 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1507 1460
final DescribeSnapshotsResponse describeSnapshotResponse = getEc2Client(getRegion(region)).describeSnapshots(b->b.filters(Filter.builder().name("snapshot-id").values(snapshotId).build()));
1508 1461
return describeSnapshotResponse.hasSnapshots() ? describeSnapshotResponse.snapshots().iterator().next() : null;
1509 1462
}
1463
+
1464
+ @Override
1465
+ public DNSCache getNewDNSCache() {
1466
+ return new DNSCache(getRoute53AsyncClient());
1467
+ }
1510 1468
}
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/DNSCache.java
... ...
@@ -0,0 +1,55 @@
1
+package com.sap.sse.landscape.aws.impl;
2
+
3
+import java.util.concurrent.CompletableFuture;
4
+import java.util.concurrent.ConcurrentHashMap;
5
+import java.util.concurrent.ConcurrentLinkedQueue;
6
+import java.util.concurrent.ConcurrentMap;
7
+
8
+import com.sap.sse.common.Util;
9
+import com.sap.sse.landscape.aws.AwsLandscape;
10
+
11
+import software.amazon.awssdk.services.route53.Route53AsyncClient;
12
+import software.amazon.awssdk.services.route53.model.ResourceRecordSet;
13
+
14
+/**
15
+ * When during landscape discovery several similar DNS requests are required, e.g., mapping a hosted zone
16
+ * name to a hosted zone ID, or listing the resource record sets within a hosted zone, this cache can be used
17
+ * to avoid repetitive requests. This cache has no invalidation logic. Toss it after your series of requests
18
+ * is finished.
19
+ *
20
+ * @author Axel Uhl (D043530)
21
+ *
22
+ */
23
+public class DNSCache {
24
+ private final Route53AsyncClient route53Client;
25
+
26
+ private final ConcurrentMap<String, CompletableFuture<String>> hostedZoneNamesToHostedZoneIds;
27
+
28
+ private final ConcurrentMap<String, CompletableFuture<Iterable<ResourceRecordSet>>> hostedZoneIdsToResourceRecordSets;
29
+
30
+ public DNSCache(Route53AsyncClient route53Client) {
31
+ this.route53Client = route53Client;
32
+ hostedZoneNamesToHostedZoneIds = new ConcurrentHashMap<>();
33
+ hostedZoneIdsToResourceRecordSets = new ConcurrentHashMap<>();
34
+ }
35
+
36
+ public CompletableFuture<String> getHostedZoneId(String hostedZoneName) {
37
+ return hostedZoneNamesToHostedZoneIds.computeIfAbsent(hostedZoneName,
38
+ hzn->route53Client.listHostedZonesByName(b->b.dnsName(hzn)).handle(
39
+ (response, e)->response.hostedZones().iterator().next().id().replaceFirst("^\\/hostedzone\\/", "")));
40
+ }
41
+
42
+ private CompletableFuture<Iterable<ResourceRecordSet>> getResourceRecordSets(String hostedZoneId) {
43
+ final ConcurrentLinkedQueue<ResourceRecordSet> result = new ConcurrentLinkedQueue<>();
44
+ return hostedZoneIdsToResourceRecordSets.computeIfAbsent(hostedZoneId, hzid->{
45
+ return route53Client.listResourceRecordSetsPaginator(b->b.hostedZoneId(hzid))
46
+ .subscribe(rrs->result.addAll(rrs.resourceRecordSets())).handle((v, e)->result);
47
+ });
48
+ }
49
+
50
+ public CompletableFuture<Iterable<ResourceRecordSet>> getResourceRecordSetsAsync(String hostname) {
51
+ return getHostedZoneId(AwsLandscape.getHostedZoneName(hostname)).thenCompose(hostedZoneId->
52
+ getResourceRecordSets(hostedZoneId)).handle((resourceRecordSets, e)->
53
+ Util.filter(resourceRecordSets, resourceRecordSet->resourceRecordSet.name().replaceFirst("\\.$", "").equals(hostname)));
54
+ }
55
+}
wiki/info/landscape/creating-ec2-image-from-scratch.md
... ...
@@ -71,9 +71,11 @@ and added the lines
71 71
```
72 72
PermitRootLogin without-password
73 73
PermitRootLogin Yes
74
+MaxStartups 100
74 75
```
75 76
76
-to allow root shell login.
77
+to allow root shell login, and allow for several concurrent SSH connections (up to 100) starting up around the
78
+same time.
77 79
78 80
I copied the JDK7/JDK8 installations, particularly the current sapjvm_8 VM, from an existing SL instance to /opt.
79 81