summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/Record.java14
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/dns/RecordData.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java94
4 files changed, 115 insertions, 11 deletions
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 fd9bddac2c6..293749be32d 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
@@ -13,13 +13,13 @@ public class Record {
private final RecordId id;
private final Type type;
private final RecordName name;
- private final RecordData value;
+ private final RecordData data;
- public Record(RecordId id, Type type, RecordName name, RecordData value) {
+ public Record(RecordId id, Type type, RecordName name, RecordData data) {
this.id = Objects.requireNonNull(id, "id cannot be null");
this.type = Objects.requireNonNull(type, "type cannot be null");
this.name = Objects.requireNonNull(name, "name cannot be null");
- this.value = Objects.requireNonNull(value, "value cannot be null");
+ this.data = Objects.requireNonNull(data, "data cannot be null");
}
/** Unique identifier for this */
@@ -34,7 +34,7 @@ public class Record {
/** Value for this, e.g. IP address for "A" record */
public RecordData value() {
- return value;
+ return data;
}
/** Name of this, e.g. a FQDN for "A" record */
@@ -60,7 +60,7 @@ public class Record {
"id=" + id +
", type=" + type +
", name='" + name + '\'' +
- ", value='" + value + '\'' +
+ ", data='" + data + '\'' +
'}';
}
@@ -72,11 +72,11 @@ public class Record {
return Objects.equals(id, record.id) &&
type == record.type &&
Objects.equals(name, record.name) &&
- Objects.equals(value, record.value);
+ Objects.equals(data, record.data);
}
@Override
public int hashCode() {
- return Objects.hash(id, type, name, value);
+ return Objects.hash(id, type, name, data);
}
}
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 444ee28f672..e0d19e0fff9 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
@@ -42,8 +42,14 @@ public class RecordData {
'}';
}
+ /** Create a new record containing the given data */
public static RecordData from(String data) {
return new RecordData(data);
}
+ /** Create a new record and append a trailing dot to given data, if missing */
+ public static RecordData fqdn(String data) {
+ return from(data.endsWith(".") ? data : data + ".");
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index ea917147970..dd803cda18b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -455,8 +455,16 @@ public class ApplicationController {
private void registerRotationInDns(Rotation rotation, String dnsName) {
try {
Optional<Record> record = nameService.findRecord(Record.Type.CNAME, RecordName.from(dnsName));
- if (!record.isPresent()) {
- RecordId id = nameService.createCname(RecordName.from(dnsName), RecordData.from(rotation.name()));
+ RecordData rotationName = RecordData.fqdn(rotation.name());
+ if (record.isPresent()) {
+ // Ensure that the existing record points to the correct rotation
+ if (!record.get().value().equals(rotationName)) {
+ nameService.updateRecord(record.get().id(), rotationName);
+ log.info("Updated mapping for record ID " + record.get().id().asString() + ": " + dnsName
+ + " -> " + rotation.name());
+ }
+ } else {
+ RecordId id = nameService.createCname(RecordName.from(dnsName), rotationName);
log.info("Registered mapping with record ID " + id.asString() + ": " + dnsName + " -> "
+ rotation.name());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index c41fc20d5c2..ccf59b56c00 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -37,6 +37,7 @@ import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.BuildSystem;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.persistence.ApplicationSerializer;
+import com.yahoo.vespa.hosted.controller.rotation.RotationId;
import com.yahoo.vespa.hosted.controller.versions.DeploymentStatistics;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
@@ -607,10 +608,99 @@ public class ControllerTest {
tester.deployCompletely(application, applicationPackage);
assertEquals(1, tester.controllerTester().nameService().records().size());
- Optional<Record> record = tester.controllerTester().nameService().findRecord(Record.Type.CNAME, RecordName.from("app1.tenant1.global.vespa.yahooapis.com"));
+ Optional<Record> record = tester.controllerTester().nameService().findRecord(
+ Record.Type.CNAME, RecordName.from("app1.tenant1.global.vespa.yahooapis.com")
+ );
assertTrue(record.isPresent());
assertEquals("app1.tenant1.global.vespa.yahooapis.com", record.get().name().asString());
- assertEquals("rotation-fqdn-01", record.get().value().asString());
+ assertEquals("rotation-fqdn-01.", record.get().value().asString());
+ }
+
+ @Test
+ public void testUpdatesExistingDnsAlias() {
+ DeploymentTester tester = new DeploymentTester();
+
+ // Application 1 is deployed and deleted
+ {
+ Application app1 = tester.createApplication("app1", "tenant1", 1, 1L);
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .globalServiceId("foo")
+ .region("us-west-1")
+ .region("us-central-1") // Two deployments should result in DNS alias being registered once
+ .build();
+
+ tester.deployCompletely(app1, applicationPackage);
+ assertEquals(1, tester.controllerTester().nameService().records().size());
+ Optional<Record> record = tester.controllerTester().nameService().findRecord(
+ Record.Type.CNAME, RecordName.from("app1.tenant1.global.vespa.yahooapis.com")
+ );
+ assertTrue(record.isPresent());
+ assertEquals("app1.tenant1.global.vespa.yahooapis.com", record.get().name().asString());
+ assertEquals("rotation-fqdn-01.", record.get().value().asString());
+
+ // Application is deleted and rotation is unassigned
+ applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .allow(ValidationId.deploymentRemoval)
+ .build();
+ tester.notifyJobCompletion(component, app1, true);
+ tester.deployAndNotify(app1, applicationPackage, true, systemTest);
+ tester.applications().deactivate(app1, new Zone(Environment.test, RegionName.from("us-east-1")));
+ tester.applications().deactivate(app1, new Zone(Environment.staging, RegionName.from("us-east-3")));
+ tester.applications().deleteApplication(app1.id(), Optional.of(new NToken("ntoken")));
+ assertTrue("Rotation is unassigned",
+ tester.applications().rotationRepository().availableRotations()
+ .contains(new RotationId("rotation-id-01")));
+
+ // Record remains
+ record = tester.controllerTester().nameService().findRecord(
+ Record.Type.CNAME, RecordName.from("app1.tenant1.global.vespa.yahooapis.com")
+ );
+ assertTrue(record.isPresent());
+ }
+
+ // Application 2 is deployed and assigned same rotation as application 1 had before deletion
+ {
+ Application app2 = tester.createApplication("app2", "tenant2", 1, 1L);
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .globalServiceId("foo")
+ .region("us-west-1")
+ .region("us-central-1")
+ .build();
+ tester.deployCompletely(app2, applicationPackage);
+ assertEquals(2, tester.controllerTester().nameService().records().size());
+ Optional<Record> record = tester.controllerTester().nameService().findRecord(
+ Record.Type.CNAME, RecordName.from("app2.tenant2.global.vespa.yahooapis.com")
+ );
+ assertTrue(record.isPresent());
+ assertEquals("app2.tenant2.global.vespa.yahooapis.com", record.get().name().asString());
+ assertEquals("rotation-fqdn-01.", record.get().value().asString());
+ }
+
+ // Application 1 is recreated, deployed and assigned a new rotation
+ {
+ Application app1 = tester.createApplication("app1", "tenant1", 1, 1L);
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .environment(Environment.prod)
+ .globalServiceId("foo")
+ .region("us-west-1")
+ .region("us-central-1")
+ .build();
+ tester.deployCompletely(app1, applicationPackage);
+ app1 = tester.applications().require(app1.id());
+ assertEquals("rotation-id-02", app1.rotation().get().id().asString());
+
+ // Existing DNS record is updated to point to the newly assigned rotation
+ assertEquals(2, tester.controllerTester().nameService().records().size());
+ Optional<Record> record = tester.controllerTester().nameService().findRecord(
+ Record.Type.CNAME, RecordName.from("app1.tenant1.global.vespa.yahooapis.com")
+ );
+ assertTrue(record.isPresent());
+ assertEquals("rotation-fqdn-02.", record.get().value().asString());
+ }
+
}
@Test