diff options
author | Valerij Fredriksen <valerijf@yahooinc.com> | 2022-10-04 14:21:30 +0200 |
---|---|---|
committer | Valerij Fredriksen <valerijf@yahooinc.com> | 2022-10-10 13:32:41 +0200 |
commit | 65ee25dafbad18fec8cda51f0f3fad0933fb6bc7 (patch) | |
tree | 8eb3c34f27f79b062f00fd80cca574c568582e98 /controller-api | |
parent | 362a8b6ffc957d8fc425e50ca18fcfe14d4aa974 (diff) |
Create DirectTarget
Diffstat (limited to 'controller-api')
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 |