summaryrefslogtreecommitdiffstats
path: root/routing-generator
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2022-02-10 14:22:24 +0100
committerMartin Polden <mpolden@mpolden.no>2022-02-11 09:58:08 +0100
commit4415929f77b8a2caa23fbd08a9a840f28c3ef4ad (patch)
treee1a1925c3d9568e5b5de6ce1cf6d36ea86737208 /routing-generator
parent2d06f5ddf396157d9898b56f0e59d6352947001b (diff)
Include only sharedLayer4 endpoints in Nginx config
Diffstat (limited to 'routing-generator')
-rw-r--r--routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/RoutingTable.java63
-rw-r--r--routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/nginx/Nginx.java2
-rw-r--r--routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandler.java3
-rw-r--r--routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/RoutingTableTest.java23
-rw-r--r--routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxMetricsReporterTest.java9
-rw-r--r--routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxTest.java3
-rw-r--r--routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandlerTest.java9
-rw-r--r--routing-generator/src/test/resources/lbservices-config24
8 files changed, 97 insertions, 39 deletions
diff --git a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/RoutingTable.java b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/RoutingTable.java
index c19dd506c87..90a38da8687 100644
--- a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/RoutingTable.java
+++ b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/RoutingTable.java
@@ -8,17 +8,18 @@ import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -35,7 +36,7 @@ public class RoutingTable {
private static final String HOSTED_VESPA_TENANT_NAME = "hosted-vespa";
- private final Map<Endpoint, Target> table;
+ private final SortedMap<Endpoint, Target> table;
private final long generation;
public RoutingTable(Map<Endpoint, Target> table, long generation) {
@@ -43,13 +44,20 @@ public class RoutingTable {
this.generation = generation;
}
+ public SortedMap<Endpoint, Target> asMap() {
+ return table;
+ }
+
/** Returns the target for given dnsName, if any */
- public Optional<Target> targetOf(String dnsName) {
- return Optional.ofNullable(table.get(new Endpoint(dnsName)));
+ public Optional<Target> targetOf(String dnsName, RoutingMethod routingMethod) {
+ return Optional.ofNullable(table.get(new Endpoint(dnsName, routingMethod)));
}
- public Map<Endpoint, Target> asMap() {
- return table;
+ /** Returns a copy of this containing only endpoints using given routing method */
+ public RoutingTable routingMethod(RoutingMethod method) {
+ Map<Endpoint, Target> copy = new TreeMap<>(table);
+ copy.keySet().removeIf(endpoint -> !endpoint.routingMethod().equals(method));
+ return new RoutingTable(copy, generation);
}
/** Returns the Vespa config generation this is based on */
@@ -76,7 +84,7 @@ public class RoutingTable {
}
public static RoutingTable from(LbServicesConfig config, long generation) {
- Map<Endpoint, Target> entries = new HashMap<>();
+ Map<Endpoint, Target> entries = new TreeMap<>();
for (var tenants : config.tenants().entrySet()) {
TenantName tenantName = TenantName.from(tenants.getKey());
if (tenantName.value().equals(HOSTED_VESPA_TENANT_NAME)) continue;
@@ -95,7 +103,7 @@ public class RoutingTable {
configuredEndpoint.weight(),
applications.getValue().activeRotation()))
.collect(Collectors.toList());
- Endpoint endpoint = new Endpoint(configuredEndpoint.dnsName());
+ Endpoint endpoint = new Endpoint(configuredEndpoint.dnsName(), routingMethodFrom(configuredEndpoint));
ClusterSpec.Id cluster = ClusterSpec.Id.from(configuredEndpoint.clusterId());
Target target;
boolean applicationEndpoint = configuredEndpoint.scope() == LbServicesConfig.Tenants.Applications.Endpoints.Scope.Enum.application;
@@ -118,6 +126,14 @@ public class RoutingTable {
return new RoutingTable(entries, generation);
}
+ private static RoutingMethod routingMethodFrom(LbServicesConfig.Tenants.Applications.Endpoints endpoint) {
+ switch (endpoint.routingMethod()) {
+ case shared: return RoutingMethod.shared;
+ case sharedLayer4: return RoutingMethod.sharedLayer4;
+ }
+ throw new IllegalArgumentException("Unhandled routing method: " + endpoint.routingMethod());
+ }
+
/** The target of an {@link Endpoint} */
public static class Target implements Comparable<Target> {
@@ -217,6 +233,11 @@ public class RoutingTable {
",reals=" + reals;
}
+ @Override
+ public int compareTo(RoutingTable.Target other) {
+ return id.compareTo(other.id);
+ }
+
/** Create an instance-level tartget */
public static Target create(ApplicationId instance, ClusterSpec.Id cluster, ZoneId zone, List<Real> reals) {
return new Target(createId("", instance.tenant(), instance.application(), Optional.of(instance.instance()), cluster, zone),
@@ -260,20 +281,20 @@ public class RoutingTable {
.replaceAll("[^a-z0-9-]*", "");
}
- @Override
- public int compareTo(RoutingTable.Target other) {
- return id.compareTo(other.id);
- }
-
}
/** An externally visible endpoint */
public static class Endpoint implements Comparable<Endpoint> {
+ private static final Comparator<Endpoint> COMPARATOR = Comparator.comparing(Endpoint::dnsName)
+ .thenComparing(Endpoint::routingMethod);
+
private final String dnsName;
+ private final RoutingMethod routingMethod;
- public Endpoint(String dnsName) {
+ public Endpoint(String dnsName, RoutingMethod routingMethod) {
this.dnsName = Objects.requireNonNull(dnsName);
+ this.routingMethod = Objects.requireNonNull(routingMethod);
}
/** The DNS name of this endpoint. This does not contain a trailing dot */
@@ -281,9 +302,13 @@ public class RoutingTable {
return dnsName;
}
+ public RoutingMethod routingMethod() {
+ return routingMethod;
+ }
+
@Override
public String toString() {
- return "endpoint " + dnsName;
+ return "endpoint " + dnsName + " (routing method: " + routingMethod + ")";
}
@Override
@@ -291,17 +316,17 @@ public class RoutingTable {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Endpoint endpoint = (Endpoint) o;
- return dnsName.equals(endpoint.dnsName);
+ return dnsName.equals(endpoint.dnsName) && routingMethod == endpoint.routingMethod;
}
@Override
public int hashCode() {
- return Objects.hash(dnsName);
+ return Objects.hash(dnsName, routingMethod);
}
@Override
- public int compareTo(RoutingTable.Endpoint o) {
- return dnsName.compareTo(o.dnsName);
+ public int compareTo(Endpoint o) {
+ return COMPARATOR.compare(this, o);
}
}
diff --git a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/nginx/Nginx.java b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/nginx/Nginx.java
index f3368c43b92..408deabbcb6 100644
--- a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/nginx/Nginx.java
+++ b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/nginx/Nginx.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.routing.nginx;
import com.yahoo.collections.Pair;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.jdisc.Metric;
import com.yahoo.system.ProcessExecuter;
import com.yahoo.vespa.hosted.routing.Router;
@@ -63,6 +64,7 @@ public class Nginx implements Router {
public void load(RoutingTable table) {
synchronized (monitor) {
try {
+ table = table.routingMethod(RoutingMethod.sharedLayer4); // This router only supports layer 4 endpoints
testConfig(table);
loadConfig(table.asMap().size());
gcConfig();
diff --git a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandler.java b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandler.java
index e4507edd850..9ed05278331 100644
--- a/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandler.java
+++ b/routing-generator/src/main/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandler.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.routing.restapi;
import com.yahoo.component.annotation.Inject;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
@@ -73,7 +74,7 @@ public class AkamaiHandler extends ThreadedHttpRequestHandler {
private HttpResponse status(HttpRequest request) {
String hostHeader = request.getHeader("host");
String hostname = withoutPort(hostHeader);
- Optional<RoutingTable.Target> target = tableSupplier.get().flatMap(table -> table.targetOf(hostname));
+ Optional<RoutingTable.Target> target = tableSupplier.get().flatMap(table -> table.targetOf(hostname, RoutingMethod.sharedLayer4));
if (target.isEmpty())
return response(404, hostHeader, "", ROTATION_UNKNOWN_MESSAGE);
diff --git a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/RoutingTableTest.java b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/RoutingTableTest.java
index 288eabe16f7..dc4df7d45ad 100644
--- a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/RoutingTableTest.java
+++ b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/RoutingTableTest.java
@@ -5,6 +5,7 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.routing.RoutingTable.Endpoint;
import com.yahoo.vespa.hosted.routing.RoutingTable.Real;
@@ -24,31 +25,43 @@ public class RoutingTableTest {
@Test
public void translate_from_lb_services_config() {
RoutingTable expected = new RoutingTable(Map.of(
- new Endpoint("beta.music.vespa.us-north-1.vespa.oath.cloud"),
+ new Endpoint("beta.music.vespa.us-north-1.vespa.oath.cloud", RoutingMethod.sharedLayer4),
Target.create(ApplicationId.from("vespa", "music", "beta"),
ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
List.of(new Real("host3-beta", 4443, 1, true),
new Real("host4-beta", 4443, 1, true))),
- new Endpoint("music.vespa.global.vespa.oath.cloud"),
+ new Endpoint("music.vespa.global.vespa.oath.cloud", RoutingMethod.sharedLayer4),
Target.create(ApplicationId.from("vespa", "music", "default"),
ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
List.of(new Real("host1-default", 4443, 1, true),
new Real("host2-default", 4443, 1, true))),
- new Endpoint("music.vespa.us-north-1.vespa.oath.cloud"),
+ new Endpoint("music.vespa.us-north-1.vespa.oath.cloud", RoutingMethod.sharedLayer4),
Target.create(ApplicationId.from("vespa", "music", "default"),
ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
List.of(new Real("host1-default", 4443, 1, true),
new Real("host2-default", 4443, 1, true))),
- new Endpoint("rotation-02.vespa.global.routing"),
+ new Endpoint("rotation-02.vespa.global.routing", RoutingMethod.sharedLayer4),
Target.create(ApplicationId.from("vespa", "music", "default"),
ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
List.of(new Real("host1-default", 4443, 1, true),
new Real("host2-default", 4443, 1, true))),
- new Endpoint("use-weighted.music.vespa.us-north-1-r.vespa.oath.cloud"),
+ new Endpoint("rotation-02.vespa.global.routing", RoutingMethod.shared),
+ Target.create(ApplicationId.from("vespa", "music", "default"),
+ ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
+ List.of(new Real("host1-default", 4443, 1, true),
+ new Real("host2-default", 4443, 1, true))),
+
+ new Endpoint("music--vespa.global.vespa.oath.cloud", RoutingMethod.shared),
+ Target.create(ApplicationId.from("vespa", "music", "default"),
+ ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
+ List.of(new Real("host1-default", 4443, 1, true),
+ new Real("host2-default", 4443, 1, true))),
+
+ new Endpoint("use-weighted.music.vespa.us-north-1-r.vespa.oath.cloud", RoutingMethod.sharedLayer4),
Target.create("use-weighted.music.vespa.us-north-1-r.vespa.oath.cloud", TenantName.from("vespa"), ApplicationName.from("music"),
ClusterSpec.Id.from("default"), ZoneId.from("prod.us-north-1"),
List.of(new Real("host3-beta", 4443, 1, true),
diff --git a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxMetricsReporterTest.java b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxMetricsReporterTest.java
index 72014047db7..eea3724e7e9 100644
--- a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxMetricsReporterTest.java
+++ b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxMetricsReporterTest.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.routing.nginx;
import com.google.common.jimfs.Jimfs;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.jdisc.test.MockMetric;
import com.yahoo.vespa.hosted.routing.RoutingTable;
@@ -152,10 +153,10 @@ public class NginxMetricsReporterTest {
}
private static RoutingTable createRoutingTable() {
- return new RoutingTable(Map.of(new Endpoint("endpoint0"), target0,
- new Endpoint("endpoint1"), target1,
- new Endpoint("endpoint2"), target2,
- new Endpoint("endpoint3"), target3),
+ return new RoutingTable(Map.of(new Endpoint("endpoint0", RoutingMethod.sharedLayer4), target0,
+ new Endpoint("endpoint1", RoutingMethod.sharedLayer4), target1,
+ new Endpoint("endpoint2", RoutingMethod.sharedLayer4), target2,
+ new Endpoint("endpoint3", RoutingMethod.sharedLayer4), target3),
42);
}
diff --git a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxTest.java b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxTest.java
index bea4d2a822c..f422ae411db 100644
--- a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxTest.java
+++ b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/nginx/NginxTest.java
@@ -5,6 +5,7 @@ import com.google.common.jimfs.Jimfs;
import com.yahoo.collections.Pair;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.jdisc.test.MockMetric;
import com.yahoo.system.ProcessExecuter;
@@ -65,7 +66,7 @@ public class NginxTest {
// A new table is loaded
Map<RoutingTable.Endpoint, RoutingTable.Target> newEntries = new HashMap<>(table0.asMap());
- newEntries.put(new RoutingTable.Endpoint("endpoint1"),
+ newEntries.put(new RoutingTable.Endpoint("endpoint1", RoutingMethod.sharedLayer4),
RoutingTable.Target.create(ApplicationId.from("t1", "a1", "i1"),
ClusterSpec.Id.from("default"),
ZoneId.from("prod", "us-north-1"),
diff --git a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandlerTest.java b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandlerTest.java
index e38d5a654f7..2814fcff8f7 100644
--- a/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandlerTest.java
+++ b/routing-generator/src/test/java/com/yahoo/vespa/hosted/routing/restapi/AkamaiHandlerTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.routing.restapi;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpRequestBuilder;
@@ -93,10 +94,10 @@ public class AkamaiHandlerTest {
private static RoutingTable makeRoutingTable() {
return new RoutingTable(Map.of(
- new Endpoint(ENDPOINT_OK), createTarget("t1", "a1", "i1", "default", true),
- new Endpoint(ENDPOINT_UNAVAILABLE), createTarget("t3", "a3", "i3", "default", true),
- new Endpoint(ENDPOINT_UNHEALTHY), createTarget("t2", "a2", "i2", "default", true),
- new Endpoint(ENDPOINT_INACTIVE), createTarget("t1", "a1", "i1", "default", false)
+ new Endpoint(ENDPOINT_OK, RoutingMethod.sharedLayer4), createTarget("t1", "a1", "i1", "default", true),
+ new Endpoint(ENDPOINT_UNAVAILABLE, RoutingMethod.sharedLayer4), createTarget("t3", "a3", "i3", "default", true),
+ new Endpoint(ENDPOINT_UNHEALTHY, RoutingMethod.sharedLayer4), createTarget("t2", "a2", "i2", "default", true),
+ new Endpoint(ENDPOINT_INACTIVE, RoutingMethod.sharedLayer4), createTarget("t1", "a1", "i1", "default", false)
), 42);
}
diff --git a/routing-generator/src/test/resources/lbservices-config b/routing-generator/src/test/resources/lbservices-config
index d19fc5ee4ae..9c79a9b063f 100644
--- a/routing-generator/src/test/resources/lbservices-config
+++ b/routing-generator/src/test/resources/lbservices-config
@@ -15,18 +15,32 @@ tenants.vespa.applications.music:prod:us-north-1:default.endpoints[1].routingMet
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[1].weight 1
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].hosts[0] "host1-default"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].hosts[1] "host2-default"
-tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].dnsName "rotation-02.vespa.global.routing"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].dnsName "music--vespa.global.vespa.oath.cloud"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].clusterId "default"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].scope "global"
-tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].routingMethod "sharedLayer4"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].routingMethod "shared"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[2].weight 1
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].hosts[0] "host1-default"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].hosts[1] "host2-default"
-tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].dnsName "use-weighted.music.vespa.us-north-1-r.vespa.oath.cloud"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].dnsName "rotation-02.vespa.global.routing"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].clusterId "default"
-tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].scope "application"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].scope "global"
tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].routingMethod "sharedLayer4"
-tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].weight 0
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[3].weight 1
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].hosts[0] "host1-default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].hosts[1] "host2-default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].dnsName "rotation-02.vespa.global.routing"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].clusterId "default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].scope "global"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].routingMethod "shared"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[4].weight 1
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].hosts[0] "host1-default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].hosts[1] "host2-default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].dnsName "use-weighted.music.vespa.us-north-1-r.vespa.oath.cloud"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].clusterId "default"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].scope "application"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].routingMethod "sharedLayer4"
+tenants.vespa.applications.music:prod:us-north-1:default.endpoints[5].weight 0
tenants.vespa.applications.music:prod:us-north-1:beta.activeRotation true
tenants.vespa.applications.music:prod:us-north-1:beta.endpoints[0].hosts[0] "host3-beta"
tenants.vespa.applications.music:prod:us-north-1:beta.endpoints[0].hosts[1] "host4-beta"