aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2020-03-05 11:00:52 +0100
committerJon Marius Venstad <venstad@gmail.com>2020-03-05 11:10:11 +0100
commitad5f5c22b4e9a9612073eb863abf0e6441fab0ed (patch)
tree8cd9ccf6a9cf7efbcd613cd6f8d4016ddff4a874 /controller-server
parent21ffbf56b2d6ef21c55641d5fc41460f8a2e6d47 (diff)
Validate CNAMEs and IP lookup after deployment
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java37
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java10
6 files changed, 54 insertions, 24 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 4637de9a32a..e84cbe3fc07 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -37,6 +37,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -121,13 +122,11 @@ public class RoutingController {
}
/** Returns all non-global endpoints and corresponding cluster IDs for given deployments, grouped by their zone */
- public Map<ZoneId, Map<URI, ClusterSpec.Id>> zoneEndpointsOf(Collection<DeploymentId> deployments) {
- var endpoints = new TreeMap<ZoneId, Map<URI, ClusterSpec.Id>>(Comparator.comparing(ZoneId::value));
+ public Map<ZoneId, List<Endpoint>> zoneEndpointsOf(Collection<DeploymentId> deployments) {
+ var endpoints = new TreeMap<ZoneId, List<Endpoint>>(Comparator.comparing(ZoneId::value));
for (var deployment : deployments) {
- var zoneEndpoints = endpointsOf(deployment).scope(Endpoint.Scope.zone).asList().stream()
- .collect(Collectors.toUnmodifiableMap(Endpoint::url,
- endpoint -> ClusterSpec.Id.from(endpoint.name())));
- if (!zoneEndpoints.isEmpty()) {
+ var zoneEndpoints = endpointsOf(deployment).scope(Endpoint.Scope.zone).asList();
+ if ( ! zoneEndpoints.isEmpty()) {
endpoints.put(deployment.zoneId(), zoneEndpoints);
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
index 6bfbaf24775..729ceaeff34 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java
@@ -53,7 +53,6 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.math.BigInteger;
-import java.net.URI;
import java.security.KeyPair;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
@@ -472,11 +471,41 @@ public class InternalStepRunner implements StepRunner {
logger.log("Endpoints not yet ready.");
return false;
}
- for (var endpoint : endpoints.get(zone).keySet())
- if ( ! controller.jobController().cloud().exists(endpoint)) {
- logger.log(INFO, "DNS lookup yielded no IP address for '" + endpoint + "'.");
+ var policies = controller.routing().policies().get(new DeploymentId(id, zone));
+ for (var endpoint : endpoints.get(zone)) {
+ HostName endpointName = HostName.from(endpoint.dnsName());
+ var ipAddress = controller.jobController().cloud().resolveHostName(endpointName);
+ if (ipAddress.isEmpty()) {
+ logger.log(INFO, "DNS lookup yielded no IP address for '" + endpointName + "'.");
return false;
}
+ if (endpoint.routingMethod() == RoutingMethod.exclusive) {
+ var policy = policies.get(new RoutingPolicyId(id, ClusterSpec.Id.from(endpoint.name()), zone));
+ if (policy == null)
+ throw new IllegalStateException(endpoint + " has no matching policy in " + policies);
+
+ var cNameValue = controller.jobController().cloud().resolveCName(endpointName);
+ if (cNameValue.isEmpty()) {
+ logger.log(INFO, "CNAME '" + endpointName + "' does not yet point to anything");
+ return false;
+ }
+ if ( ! cNameValue.get().equals(policy.canonicalName())) {
+ logger.log(INFO, "CNAME '" + endpointName + "' doesn't point to expected host name '" + policy.canonicalName() + "'");
+ return false;
+ }
+ var loadBalancerAddress = controller.jobController().cloud().resolveHostName(policy.canonicalName());
+ if (loadBalancerAddress.isEmpty()) {
+ logger.log(INFO, "DNS lookup yielded no IP address for load balancer '" + policy.canonicalName() + "'");
+ return false;
+ }
+ // Verify that the JVMs internal DNS cache has seen the update. Both names should resolve to the same IP address.
+ if ( ! loadBalancerAddress.equals(ipAddress)) {
+ logger.log(INFO, "IP address of CNAME '" + endpointName + "' (" + ipAddress.get() + ") and load balancer '" +
+ policy.canonicalName() + "' (" + loadBalancerAddress.get() + ") are not equal");
+ return false;
+ }
+ }
+ }
logEndpoints(endpoints, logger);
return true;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
index cee492276af..775eb2a4d75 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java
@@ -164,7 +164,9 @@ public final class ControllerTester {
public AthenzDbMock athenzDb() { return athenzDb; }
- public MemoryNameService nameService() { return serviceRegistry.nameServiceMock(); }
+ public MemoryNameService nameService() {
+ return serviceRegistry.nameService();
+ }
public ZoneRegistryMock zoneRegistry() { return serviceRegistry.zoneRegistry(); }
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index 5eae68adf5a..36ab6a9aa60 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -309,9 +309,6 @@ public class DeploymentContext {
throw new AssertionError("Job '" + run.id() + "' was run twice");
assertFalse("Change should have no targets, but was " + instance().change(), instance().change().hasTargets());
- if (!deferDnsUpdates) {
- flushDnsUpdates();
- }
return this;
}
@@ -411,6 +408,8 @@ public class DeploymentContext {
var id = newRun(JobType.systemTest);
var testZone = JobType.systemTest.zone(tester.controller().system());
runner.run();
+ if ( ! deferDnsUpdates)
+ flushDnsUpdates();
configServer().convergeServices(instanceId, testZone);
configServer().convergeServices(testerId.id(), testZone);
runner.run();
@@ -438,6 +437,9 @@ public class DeploymentContext {
// First step is always a deployment.
runner.advance(currentRun(job));
+ if ( ! deferDnsUpdates)
+ flushDnsUpdates();
+
if (job.type().isTest())
doInstallTester(job);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index fb0817f8320..44fd677a4d3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -260,11 +260,13 @@ public class InternalStepRunnerTest {
tester.controllerTester().zoneRegistry().exclusiveRoutingIn(ZoneApiMock.from(systemTestZone), ZoneApiMock.from(stagingZone));
app.newRun(JobType.systemTest);
tester.runner().run();
- tester.configServer().convergeServices(app.instanceId(), JobType.systemTest.zone(system()));
- tester.configServer().convergeServices(app.testerId().id(), JobType.systemTest.zone(system()));
assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal));
assertEquals(unfinished, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
- tester.runner().run();
+
+ app.flushDnsUpdates();
+ tester.configServer().convergeServices(app.instanceId(), JobType.systemTest.zone(system()));
+ tester.configServer().convergeServices(app.testerId().id(), JobType.systemTest.zone(system()));
+ tester.runner().run();;
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installReal));
assertEquals(succeeded, tester.jobs().last(app.instanceId(), JobType.systemTest).get().stepStatuses().get(Step.installTester));
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 69f4e0672bf..8863651e1b5 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -42,7 +42,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final ConfigServerMock configServerMock;
private final MemoryNameService memoryNameService = new MemoryNameService();
private final MemoryGlobalRoutingService memoryGlobalRoutingService = new MemoryGlobalRoutingService();
- private final RoutingGeneratorMock routingGeneratorMock;
+ private final RoutingGeneratorMock routingGeneratorMock = new RoutingGeneratorMock();
private final MockMailer mockMailer = new MockMailer();
private final EndpointCertificateMock endpointCertificateMock = new EndpointCertificateMock();
private final MockMeteringClient mockMeteringClient = new MockMeteringClient();
@@ -55,7 +55,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final MockBilling mockBilling = new MockBilling();
private final MockAwsEventFetcher mockAwsEventFetcher = new MockAwsEventFetcher();
private final ArtifactRepositoryMock artifactRepositoryMock = new ArtifactRepositoryMock();
- private final MockTesterCloud mockTesterCloud = new MockTesterCloud();
+ private final MockTesterCloud mockTesterCloud;
private final ApplicationStoreMock applicationStoreMock = new ApplicationStoreMock();
private final MockRunDataStore mockRunDataStore = new MockRunDataStore();
private final MockTenantCost mockTenantCost = new MockTenantCost();
@@ -64,7 +64,7 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
public ServiceRegistryMock(SystemName system) {
this.zoneRegistryMock = new ZoneRegistryMock(system);
this.configServerMock = new ConfigServerMock(zoneRegistryMock);
- this.routingGeneratorMock = new RoutingGeneratorMock();
+ this.mockTesterCloud = new MockTesterCloud(nameService());
}
@Inject
@@ -193,10 +193,6 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
return configServerMock;
}
- public MemoryNameService nameServiceMock() {
- return memoryNameService;
- }
-
public MemoryGlobalRoutingService globalRoutingServiceMock() {
return memoryGlobalRoutingService;
}