diff options
author | Martin Polden <mpolden@mpolden.no> | 2019-02-07 13:08:08 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2019-02-12 14:15:29 +0100 |
commit | 85fd303379221040adcf2984b3c5e13e20ea048c (patch) | |
tree | df9ca62541fdfe00e01a1cc41a0d781062e5660c /controller-api | |
parent | 6cd73b95dcdcf95a07a726aab88147c2aa19a029 (diff) |
Allow NameService to create ALIAS records
Diffstat (limited to 'controller-api')
7 files changed, 103 insertions, 14 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/AliasTarget.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/AliasTarget.java new file mode 100644 index 00000000000..e7d7cfe620d --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/AliasTarget.java @@ -0,0 +1,59 @@ +// Copyright 2019 Oath Inc. 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.HostName; +import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; + +import java.util.Objects; + +/** + * Represents the target of an ALIAS record. + * + * @author mpolden + */ +public class AliasTarget { + + private final HostName name; + private final String dnsZone; + private final ZoneId zone; + + public AliasTarget(HostName name, String dnsZone, ZoneId zone) { + this.name = Objects.requireNonNull(name, "name must be non-null"); + this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null"); + this.zone = Objects.requireNonNull(zone, "zone must be non-null"); + } + + /** DNS name of this */ + public HostName name() { + return name; + } + + /** DNS zone of this */ + public String dnsZone() { + return dnsZone; + } + + /** The zone where this exists */ + public ZoneId zone() { + return zone; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AliasTarget that = (AliasTarget) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public String toString() { + return String.format("rotation target %s [zone: %s] in %s", name, dnsZone, zone); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MemoryNameService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MemoryNameService.java index 105d2626dcd..46a6d1c35ad 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MemoryNameService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/MemoryNameService.java @@ -2,10 +2,14 @@ package com.yahoo.vespa.hosted.controller.api.integration.dns; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -16,22 +20,37 @@ import java.util.stream.Collectors; */ public class MemoryNameService implements NameService { - private final Map<RecordId, Record> records = new HashMap<>(); + private final Map<RecordId, Set<Record>> records = new HashMap<>(); - public Map<RecordId, Record> records() { + public Map<RecordId, Set<Record>> records() { return Collections.unmodifiableMap(records); } @Override - public RecordId createCname(RecordName alias, RecordData canonicalName) { + public RecordId createCname(RecordName name, RecordData canonicalName) { RecordId id = new RecordId(UUID.randomUUID().toString()); - records.put(id, new Record(id, Record.Type.CNAME, alias, canonicalName)); + records.put(id, Set.of(new Record(id, Record.Type.CNAME, name, canonicalName))); + return id; + } + + @Override + public RecordId createAlias(RecordName name, Set<AliasTarget> targets) { + RecordId id = new RecordId(UUID.randomUUID().toString()); + Set<Record> records = targets.stream() + .sorted((a, b) -> Comparator.comparing(AliasTarget::name).compare(a, b)) + .map(target -> new Record(id, Record.Type.ALIAS, name, + RecordData.fqdn(target.name().value()))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + // Satisfy idempotency contract of interface + findRecords(Record.Type.ALIAS, name).stream().map(Record::id).forEach(this::removeRecord); + this.records.put(id, records); return id; } @Override public List<Record> findRecords(Record.Type type, RecordName name) { return records.values().stream() + .flatMap(Collection::stream) .filter(record -> record.type() == type && record.name().equals(name)) .collect(Collectors.toUnmodifiableList()); } @@ -39,17 +58,29 @@ public class MemoryNameService implements NameService { @Override public List<Record> findRecords(Record.Type type, RecordData data) { return records.values().stream() + .flatMap(Collection::stream) .filter(record -> record.type() == type && record.data().equals(data)) .collect(Collectors.toUnmodifiableList()); } @Override public void updateRecord(RecordId id, RecordData newData) { - records.computeIfPresent(id, (k, record) -> new Record(id, record.type(), record.name(), newData)); + records.computeIfPresent(id, (k, records) -> { + if (records.isEmpty()) { + throw new IllegalArgumentException("No record with data '" + newData.asString() + "' exists"); + } + if (records.size() > 1) { + throw new IllegalArgumentException("Cannot update multi-value record '" + id.asString() + "' with '" + + newData.asString() + "'"); + } + Record existing = records.iterator().next(); + return Set.of(new Record(id, existing.type(), existing.name(), newData)); + }); } @Override public void removeRecord(RecordId id) { records.remove(id); } + } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/NameService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/NameService.java index 170196fd483..537460c8b1e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/NameService.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/NameService.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.controller.api.integration.dns; import java.util.List; +import java.util.Set; /** * A managed DNS service. @@ -18,6 +19,9 @@ public interface NameService { */ RecordId createCname(RecordName alias, RecordData canonicalName); + /** Create a non-standard ALIAS record pointing to given targets. Implementations of this are expected to be idempotent */ + RecordId createAlias(RecordName name, Set<AliasTarget> targets); + /** Find records matching type and name */ List<Record> findRecords(Record.Type type, RecordName name); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/Record.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/Record.java index dd9747ce5ca..218fc9f5266 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/Record.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/Record.java @@ -45,6 +45,7 @@ public class Record { public enum Type { A, AAAA, + ALIAS, CNAME, MX, NS, diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordData.java index e0d19e0fff9..6c765efd35a 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordData.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordData.java @@ -37,9 +37,7 @@ public class RecordData { @Override public String toString() { - return "RecordValue{" + - "value='" + data + '\'' + - '}'; + return data; } /** Create a new record containing the given data */ diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordId.java index 2aa3f4a810e..ed628f0c827 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordId.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordId.java @@ -22,9 +22,7 @@ public class RecordId { @Override public String toString() { - return "RecordId{" + - "id='" + id + '\'' + - '}'; + return id; } @Override diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordName.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordName.java index aa239ece588..d3abad9fb62 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordName.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordName.java @@ -35,9 +35,7 @@ public class RecordName { @Override public String toString() { - return "RecordName{" + - "name='" + name + '\'' + - '}'; + return name; } public static RecordName from(String name) { |