summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@yahooinc.com>2022-10-04 14:21:30 +0200
committerValerij Fredriksen <valerijf@yahooinc.com>2022-10-10 13:32:41 +0200
commit65ee25dafbad18fec8cda51f0f3fad0933fb6bc7 (patch)
tree8eb3c34f27f79b062f00fd80cca574c568582e98 /controller-api
parent362a8b6ffc957d8fc425e50ca18fcfe14d4aa974 (diff)
Create DirectTarget
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTarget.java57
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/LatencyDirectTarget.java66
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/WeightedDirectTarget.java70
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTargetTest.java36
4 files changed, 229 insertions, 0 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTarget.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTarget.java
new file mode 100644
index 00000000000..c3cedf93841
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTarget.java
@@ -0,0 +1,57 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.dns;
+
+import java.util.Objects;
+
+/**
+ * Same as {@link AliasTarget}, except for targets outside AWS (cannot be targeted with ALIAS record).
+ *
+ * @author freva
+ */
+public sealed abstract class DirectTarget permits LatencyDirectTarget, WeightedDirectTarget {
+
+ private final RecordData recordData;
+ private final String id;
+
+ protected DirectTarget(RecordData recordData, String id) {
+ this.recordData = Objects.requireNonNull(recordData, "recordData must be non-null");
+ this.id = Objects.requireNonNull(id, "id must be non-null");
+ }
+
+ /** A unique identifier of this record within the record group */
+ public String id() {
+ return id;
+ }
+
+ /** Data in this, e.g. IP address for records of type A */
+ public RecordData recordData() {
+ return recordData;
+ }
+
+ /** Returns the fields in this encoded as record data */
+ public abstract RecordData pack();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DirectTarget that = (DirectTarget) o;
+ return recordData.equals(that.recordData) && id.equals(that.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(recordData, id);
+ }
+
+ /** Unpack target from given record data */
+ public static DirectTarget unpack(RecordData data) {
+ String[] parts = data.asString().split("/");
+ return switch (parts[0]) {
+ case LatencyDirectTarget.TARGET_TYPE -> LatencyDirectTarget.unpack(data);
+ case WeightedDirectTarget.TARGET_TYPE -> WeightedDirectTarget.unpack(data);
+ default -> throw new IllegalArgumentException("Unknown alias type '" + parts[0] + "'");
+ };
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/LatencyDirectTarget.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/LatencyDirectTarget.java
new file mode 100644
index 00000000000..09795ae08a7
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/LatencyDirectTarget.java
@@ -0,0 +1,66 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.dns;
+
+import com.yahoo.config.provision.zone.ZoneId;
+
+import java.util.Objects;
+
+/**
+ * An implementation of {@link DirectTarget} that uses latency-based routing.
+ *
+ * @author freva
+ */
+public final class LatencyDirectTarget extends DirectTarget {
+
+ static final String TARGET_TYPE = "latency";
+
+ private final ZoneId zone;
+
+ public LatencyDirectTarget(RecordData recordData, ZoneId zone) {
+ super(recordData, zone.value());
+ this.zone = Objects.requireNonNull(zone);
+ }
+
+ /** The zone this record points to */
+ public ZoneId zone() {
+ return zone;
+ }
+
+ @Override
+ public RecordData pack() {
+ return RecordData.from(String.join("/", TARGET_TYPE, recordData().asString(), id()));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ LatencyDirectTarget that = (LatencyDirectTarget) o;
+ return zone.equals(that.zone);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), zone);
+ }
+
+ @Override
+ public String toString() {
+ return "latency target for " + recordData() + " [id=" + id() + "]";
+ }
+
+ /** Unpack latency alias from given record data */
+ public static LatencyDirectTarget unpack(RecordData data) {
+ var parts = data.asString().split("/");
+ if (parts.length != 3) {
+ throw new IllegalArgumentException("Expected data to be on format target-type/record-data/zone-id, but got " +
+ data.asString());
+ }
+ if (!TARGET_TYPE.equals(parts[0])) {
+ throw new IllegalArgumentException("Unexpected type '" + parts[0] + "'");
+ }
+ return new LatencyDirectTarget(RecordData.from(parts[1]), ZoneId.from(parts[2]));
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/WeightedDirectTarget.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/WeightedDirectTarget.java
new file mode 100644
index 00000000000..b899cb57b60
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/WeightedDirectTarget.java
@@ -0,0 +1,70 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.dns;
+
+import com.yahoo.config.provision.zone.ZoneId;
+
+import java.util.Objects;
+
+/**
+ * An implementation of {@link DirectTarget} where is requests are answered based on the weight assigned to the
+ * record, as a proportion of the total weight for all records having the same DNS name.
+ *
+ * The portion of received traffic is calculated as follows: (record weight / sum of the weights of all records).
+ *
+ * @author freva
+ */
+public final class WeightedDirectTarget extends DirectTarget {
+
+ static final String TARGET_TYPE = "weighted";
+
+ private final long weight;
+
+ public WeightedDirectTarget(RecordData recordData, ZoneId zone, long weight) {
+ super(recordData, zone.value());
+ this.weight = weight;
+ if (weight < 0) throw new IllegalArgumentException("Weight cannot be negative");
+ }
+
+ /** The weight of this target */
+ public long weight() {
+ return weight;
+ }
+
+ @Override
+ public RecordData pack() {
+ return RecordData.from(String.join("/", TARGET_TYPE, recordData().asString(), id(), Long.toString(weight)));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ WeightedDirectTarget that = (WeightedDirectTarget) o;
+ return weight == that.weight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), weight);
+ }
+
+ @Override
+ public String toString() {
+ return "weighted target for " + recordData() + "[id=" + id() + ",weight=" + weight + "]";
+ }
+
+ /** Unpack weighted alias from given record data */
+ public static WeightedDirectTarget unpack(RecordData data) {
+ var parts = data.asString().split("/");
+ if (parts.length != 4) {
+ throw new IllegalArgumentException("Expected data to be on format target-type/record-data/zone-id/weight, " +
+ "but got " + data.asString());
+ }
+ if (!TARGET_TYPE.equals(parts[0])) {
+ throw new IllegalArgumentException("Unexpected type '" + parts[0] + "'");
+ }
+ return new WeightedDirectTarget(RecordData.from(parts[1]), ZoneId.from(parts[2]), Long.parseLong(parts[3]));
+ }
+
+}
diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTargetTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTargetTest.java
new file mode 100644
index 00000000000..f262821a638
--- /dev/null
+++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/dns/DirectTargetTest.java
@@ -0,0 +1,36 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.api.integration.dns;
+
+import com.yahoo.config.provision.zone.ZoneId;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author freva
+ */
+class DirectTargetTest {
+
+ @Test
+ void packing() {
+ List<DirectTarget> tests = List.of(
+ new LatencyDirectTarget(RecordData.from("foo.example.com"), ZoneId.from("prod.us-north-1")),
+ new WeightedDirectTarget(RecordData.from("bar.example.com"), ZoneId.from("prod.us-north-2"), 50));
+ for (var target : tests) {
+ DirectTarget unpacked = DirectTarget.unpack(target.pack());
+ assertEquals(target, unpacked);
+ }
+
+ List<RecordData> invalidData = List.of(RecordData.from(""), RecordData.from("foobar"));
+ for (var data : invalidData) {
+ try {
+ DirectTarget.unpack(data);
+ fail("Expected exception");
+ } catch (IllegalArgumentException ignored) { }
+ }
+ }
+
+} \ No newline at end of file