diff options
Diffstat (limited to 'node-repository')
16 files changed, 203 insertions, 129 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java index 5ef5a46dc45..cbce0f38c43 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java @@ -9,6 +9,7 @@ import com.yahoo.config.provision.NodeType; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Collectors; import static java.util.stream.Collectors.collectingAndThen; @@ -29,78 +30,77 @@ public class NodeList { /** Returns the subset of nodes which are retired */ public NodeList retired() { - return new NodeList(nodes.stream().filter(node -> node.allocation().get().membership().retired()).collect(Collectors.toList())); + return filter(node -> node.allocation().get().membership().retired()); } /** Returns the subset of nodes which are not retired */ public NodeList nonretired() { - return new NodeList(nodes.stream().filter(node -> ! node.allocation().get().membership().retired()).collect(Collectors.toList())); + return filter(node -> ! node.allocation().get().membership().retired()); } /** Returns the subset of nodes of the given flavor */ public NodeList flavor(String flavor) { - return new NodeList(nodes.stream().filter(node -> node.flavor().name().equals(flavor)).collect(Collectors.toList())); + return filter(node -> node.flavor().name().equals(flavor)); } /** Returns the subset of nodes which does not have the given flavor */ public NodeList notFlavor(String flavor) { - return new NodeList(nodes.stream().filter(node -> ! node.flavor().name().equals(flavor)).collect(Collectors.toList())); + return filter(node -> ! node.flavor().name().equals(flavor)); } /** Returns the subset of nodes assigned to the given cluster type */ public NodeList type(ClusterSpec.Type type) { - return new NodeList(nodes.stream().filter(node -> node.allocation().get().membership().cluster().type().equals(type)).collect(Collectors.toList())); + return filter(node -> node.allocation().get().membership().cluster().type().equals(type)); } /** Returns the subset of nodes owned by the given application */ public NodeList owner(ApplicationId application) { - return nodes.stream() - .filter(node -> node.allocation().map(a -> a.owner().equals(application)).orElse(false)) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); + return filter(node -> node.allocation().map(a -> a.owner().equals(application)).orElse(false)); } /** Returns the subset of nodes matching the given node type */ public NodeList nodeType(NodeType nodeType) { - return nodes.stream() - .filter(node -> node.type() == nodeType) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); - } - - /** Returns the parent nodes of the given child nodes */ - public NodeList parentsOf(Collection<Node> children) { - return children.stream() - .map(Node::parentHostname) - .filter(Optional::isPresent) - .map(Optional::get) - .map(hostName -> nodes.stream().filter(node -> node.hostname().equals(hostName)).findFirst()) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); + return filter(node -> node.type() == nodeType); } /** Returns the subset of nodes that are parents */ public NodeList parents() { - return nodes.stream() - .filter(n -> !n.parentHostname().isPresent()) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); + return filter(n -> n.parentHostname().isEmpty()); } /** Returns the child nodes of the given parent node */ public NodeList childrenOf(String hostname) { - return nodes.stream() - .filter(n -> n.parentHostname() - .map(hostName -> hostName.equals(hostname)) - .orElse(false)) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); + return filter(n -> n.parentHostname().map(hostname::equals).orElse(false)); } public NodeList childrenOf(Node parent) { return childrenOf(parent.hostname()); } + /** Returns the subset of nodes that are in a given state */ + public NodeList state(Node.State state) { + return filter(node -> node.state() == state); + } + + /** Returns the parent nodes of the given child nodes */ + public NodeList parentsOf(Collection<Node> children) { + return children.stream() + .map(Node::parentHostname) + .filter(Optional::isPresent) + .map(Optional::get) + .map(hostName -> nodes.stream().filter(node -> node.hostname().equals(hostName)).findFirst()) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(collectingAndThen(Collectors.toList(), NodeList::new)); + } + public int size() { return nodes.size(); } /** Returns the immutable list of nodes in this */ public List<Node> asList() { return nodes; } + private NodeList filter(Predicate<Node> predicate) { + return nodes.stream().filter(predicate).collect(collectingAndThen(Collectors.toList(), NodeList::new)); + } + } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java index 520ceaf323b..93968eef37b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DirtyExpirer.java @@ -9,7 +9,6 @@ import com.yahoo.vespa.hosted.provision.node.History; import java.time.Clock; import java.time.Duration; import java.util.List; -import java.util.stream.Collectors; /** * This moves nodes from dirty to failed if they have been in dirty too long diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisioner.java index 68d597fb839..7d4d41bfc44 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/InfrastructureProvisioner.java @@ -17,9 +17,7 @@ import com.yahoo.vespa.service.monitor.DuperModelInfraApi; import com.yahoo.vespa.service.monitor.InfraApplicationApi; import java.time.Duration; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -38,7 +36,6 @@ public class InfrastructureProvisioner extends Maintainer { private final Provisioner provisioner; private final InfrastructureVersions infrastructureVersions; private final DuperModelInfraApi duperModel; - private final Map<ApplicationId, Version> lastActivations = new HashMap<>(); public InfrastructureProvisioner(Provisioner provisioner, NodeRepository nodeRepository, InfrastructureVersions infrastructureVersions, Duration interval, JobControl jobControl, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java index 985a13d3511..8a331209efc 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.provision.node; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; -import com.yahoo.vespa.hosted.provision.Node; /** * The allocation of a node diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ApplicationFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ApplicationFilter.java index a731b8fa94f..aa285cd707d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ApplicationFilter.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/filter/ApplicationFilter.java @@ -4,16 +4,12 @@ package com.yahoo.vespa.hosted.provision.node.filter; import com.google.common.collect.ImmutableSet; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; -import com.yahoo.config.provision.ClusterMembership; -import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.TenantName; import com.yahoo.text.StringUtilities; import com.yahoo.vespa.hosted.provision.Node; -import java.util.Collections; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CountingCuratorTransaction.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CountingCuratorTransaction.java index de803281a87..b1a4b1d7aa9 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CountingCuratorTransaction.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CountingCuratorTransaction.java @@ -1,12 +1,8 @@ package com.yahoo.vespa.hosted.provision.persistence; -import com.yahoo.path.Path; import com.yahoo.vespa.curator.Curator; -import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.curator.recipes.CuratorCounter; -import com.yahoo.vespa.curator.transaction.CuratorOperation; import com.yahoo.vespa.curator.transaction.CuratorTransaction; -import com.yahoo.vespa.curator.transaction.TransactionChanges; /** * CuratorTransaction wrapper which increments a counter, to signal invalidation of node repository caches. diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java index 4ba272c2366..f48f0c1bdce 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java @@ -3,17 +3,21 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.ParentHostUnavailableException; import com.yahoo.transaction.Mutex; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; -import java.time.Clock; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -45,10 +49,12 @@ class Activator { public void activate(ApplicationId application, Collection<HostSpec> hosts, NestedTransaction transaction) { try (Mutex lock = nodeRepository.lock(application)) { Set<String> hostnames = hosts.stream().map(HostSpec::hostname).collect(Collectors.toSet()); + NodeList allNodes = nodeRepository.list(); + NodeList applicationNodes = allNodes.owner(application); - List<Node> reserved = nodeRepository.getNodes(application, Node.State.reserved); + List<Node> reserved = applicationNodes.state(Node.State.reserved).asList(); List<Node> reservedToActivate = retainHostsInList(hostnames, reserved); - List<Node> active = nodeRepository.getNodes(application, Node.State.active); + List<Node> active = applicationNodes.state(Node.State.active).asList(); List<Node> continuedActive = retainHostsInList(hostnames, active); List<Node> allActive = new ArrayList<>(continuedActive); allActive.addAll(reservedToActivate); @@ -61,6 +67,8 @@ class Activator { "\nThis might happen if the time from reserving host to activation takes " + "longer time than reservation expiry (the hosts will then no longer be reserved)"); + validateParentHosts(application, allNodes, reservedToActivate); + List<Node> activeToRemove = removeHostsFromList(hostnames, active); activeToRemove = activeToRemove.stream().map(Node::unretire).collect(Collectors.toList()); // only active nodes can be retired nodeRepository.deactivate(activeToRemove, transaction); @@ -69,6 +77,35 @@ class Activator { } } + private static void validateParentHosts(ApplicationId application, NodeList nodes, List<Node> potentialChildren) { + Set<String> parentHostnames = potentialChildren.stream() + .map(Node::parentHostname) + .flatMap(Optional::stream) + .collect(Collectors.toSet()); + + Map<String, Node> parentsByHostname = nodes.asList().stream() + .filter(node -> parentHostnames.contains(node.hostname())) + .collect(Collectors.toMap(Node::hostname, Function.identity())); + + List<Node> unavailableChildren = potentialChildren.stream() + .filter(child -> child.parentHostname() + .map(parentsByHostname::get) + .map(parent -> parent.state() != Node.State.active) + .orElse(false)) + .collect(Collectors.toList()); + + if (!unavailableChildren.isEmpty()) { + String detailedError = unavailableChildren.stream() + .map(child -> child.parentHostname() + .map(parentsByHostname::get) + .map(parent -> String.format("Refusing to activate %s: Its parent (%s) is not active (is %s).", + child.hostname(), parent.hostname(), parent.state()))) + .flatMap(Optional::stream) + .collect(Collectors.joining(" ")); + throw new ParentHostUnavailableException("Activation of " + application + " failed: " + detailedError); + } + } + private List<Node> retainHostsInList(Set<String> hosts, List<Node> nodes) { return nodes.stream().filter(node -> hosts.contains(node.hostname())).collect(Collectors.toList()); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java index 7f1959d548e..713a5ac9f85 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodesApiHandler.java @@ -37,7 +37,6 @@ import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.time.Instant; import java.util.ArrayList; import java.util.HashSet; import java.util.List; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java index 91f29cd900a..bad1e155e0b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java @@ -47,6 +47,11 @@ import static org.junit.Assert.assertEquals; */ public class FailedExpirerTest { + private static final ApplicationId tenantHostApplicationId = ApplicationId.from("vespa", "zone-app", "default"); + private static final ClusterSpec tenantHostApplicationClusterSpec = ClusterSpec.request( + ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin"), Version.fromString("6.42"), false); + private static final Capacity tenantHostApplicationCapacity = Capacity.fromRequiredNodeType(NodeType.host); + @Test public void ensure_failed_nodes_are_deallocated_in_test_quickly() { FailureScenario scenario = new FailureScenario(SystemName.main, Environment.test) @@ -128,6 +133,8 @@ public class FailedExpirerTest { .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent1") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent2") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent3") + .setReady("parent1", "parent2", "parent3") + .allocate(tenantHostApplicationId, tenantHostApplicationClusterSpec, tenantHostApplicationCapacity) .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node1", "parent1") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node2", "parent2") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node3", "parent3") @@ -149,6 +156,8 @@ public class FailedExpirerTest { .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent1") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent2") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent3") + .setReady("parent1", "parent2", "parent3") + .allocate(tenantHostApplicationId, tenantHostApplicationClusterSpec, tenantHostApplicationCapacity) .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node1", "parent1") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node2", "parent2") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node3", "parent3") @@ -170,6 +179,8 @@ public class FailedExpirerTest { .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent1") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent2") .withNode(NodeType.host, FailureScenario.defaultFlavor, "parent3") + .setReady("parent1", "parent2", "parent3") + .allocate(tenantHostApplicationId, tenantHostApplicationClusterSpec, tenantHostApplicationCapacity) .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node1", "parent1") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node2", "parent2") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node3", "parent3") @@ -265,7 +276,7 @@ public class FailedExpirerTest { } public FailureScenario withNode(NodeType type, Flavor flavor, String hostname) { - return withNode(type, flavor, hostname,null); + return withNode(type, flavor, hostname, null); } public FailureScenario withNode(String hostname) { @@ -309,11 +320,12 @@ public class FailedExpirerTest { ClusterSpec.Id.from("test"), Version.fromString("6.42"), false); - List<HostSpec> preparedNodes = provisioner.prepare(applicationId, - clusterSpec, - Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()), - false, true), - 1, null); + Capacity capacity = Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()), false, true); + return allocate(applicationId, clusterSpec, capacity); + } + + public FailureScenario allocate(ApplicationId applicationId, ClusterSpec clusterSpec, Capacity capacity) { + List<HostSpec> preparedNodes = provisioner.prepare(applicationId, clusterSpec, capacity, 1, null); NestedTransaction transaction = new NestedTransaction().add(new CuratorTransaction(curator)); provisioner.activate(transaction, applicationId, new HashSet<>(preparedNodes)); transaction.commit(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java index 84e366c9510..eba338495ff 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java @@ -13,7 +13,6 @@ import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; -import com.yahoo.test.ManualClock; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.node.History; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java index 3e69a5391af..0f5343817e1 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java @@ -50,7 +50,9 @@ public class AclProvisioningTest { // Populate repo tester.makeReadyNodes(10, "default"); List<Node> dockerHost = tester.makeReadyNodes(1, "default", NodeType.host); - tester.makeReadyDockerNodes(1, "default", dockerHost.get(0).hostname()); + ApplicationId zoneApplication = tester.makeApplicationId(); + deploy(zoneApplication, Capacity.fromRequiredNodeType(NodeType.host)); + tester.makeReadyVirtualDockerNodes(1, "default", dockerHost.get(0).hostname()); List<Node> proxyNodes = tester.makeReadyNodes(3, "default", NodeType.proxy); // Allocate 2 nodes @@ -143,7 +145,7 @@ public class AclProvisioningTest { // Populate repo List<Node> dockerHostNodes = tester.makeReadyNodes(2, "default", NodeType.host); Node dockerHostNodeUnderTest = dockerHostNodes.get(0); - List<Node> dockerNodes = tester.makeReadyDockerNodes(5, "dockerSmall", + List<Node> dockerNodes = tester.makeReadyVirtualDockerNodes(5, "dockerSmall", dockerHostNodeUnderTest.hostname()); List<NodeAcl> acls = tester.nodeRepository().getNodeAcls(dockerHostNodeUnderTest, true); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java index bf93e114c8d..ae3253ecfd8 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java @@ -8,7 +8,6 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeType; -import com.yahoo.lang.MutableInteger; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.node.Allocation; @@ -17,7 +16,6 @@ import com.yahoo.vespa.hosted.provision.node.History; import com.yahoo.vespa.hosted.provision.node.Status; import javax.swing.JFrame; -import java.time.Clock; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java index 16aa613db4a..85ecf9c5073 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java @@ -9,6 +9,8 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; +import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.ParentHostUnavailableException; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.hosted.provision.Node; @@ -42,7 +44,7 @@ public class DockerProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); for (int i = 1; i < 10; i++) - tester.makeReadyDockerNodes(1, dockerFlavor, "dockerHost" + i); + tester.makeReadyVirtualDockerNodes(1, dockerFlavor, "dockerHost" + i); Version wantedVespaVersion = Version.fromString("6.39"); int nodeCount = 7; @@ -67,18 +69,54 @@ public class DockerProvisioningTest { assertEquals(hosts, upgradedHosts); } + @Test + public void refuses_to_activate_on_non_active_host() { + ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); + + ApplicationId zoneApplication = tester.makeApplicationId(); + List<Node> parents = tester.makeReadyVirtualDockerHosts(10, "large"); + for (Node parent : parents) + tester.makeReadyVirtualDockerNodes(1, dockerFlavor, parent.hostname()); + + ApplicationId application1 = tester.makeApplicationId(); + Version wantedVespaVersion = Version.fromString("6.39"); + int nodeCount = 7; + List<HostSpec> nodes = tester.prepare(application1, + ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false), + nodeCount, 1, dockerFlavor); + try { + tester.activate(application1, new HashSet<>(nodes)); + fail("Expected the allocation to fail due to parent hosts not being active yet"); + } catch (ParentHostUnavailableException ignored) { } + + // Activate the zone-app, thereby allocating the parents + List<HostSpec> hosts = tester.prepare(zoneApplication, + ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("zone-app"), wantedVespaVersion, false), + Capacity.fromRequiredNodeType(NodeType.host), 1); + tester.activate(zoneApplication, hosts); + + // Try allocating tenants again + nodes = tester.prepare(application1, + ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false), + nodeCount, 1, dockerFlavor); + tester.activate(application1, new HashSet<>(nodes)); + + NodeList activeNodes = tester.getNodes(application1, Node.State.active); + assertEquals(nodeCount, activeNodes.size()); + } + /** Exclusive app first, then non-exclusive: Should give the same result as below */ @Test public void docker_application_deployment_with_exclusive_app_first() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); for (int i = 1; i <= 4; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host1"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host1"); for (int i = 5; i <= 8; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host2"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host2"); for (int i = 9; i <= 12; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host3"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host3"); for (int i = 13; i <= 16; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host4"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host4"); ApplicationId application1 = tester.makeApplicationId(); prepareAndActivate(application1, 2, true, tester); @@ -95,13 +133,13 @@ public class DockerProvisioningTest { public void docker_application_deployment_with_exclusive_app_last() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); for (int i = 1; i <= 4; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host1"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host1"); for (int i = 5; i <= 8; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host2"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host2"); for (int i = 9; i <= 12; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host3"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host3"); for (int i = 13; i <= 16; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host4"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host4"); ApplicationId application1 = tester.makeApplicationId(); prepareAndActivate(application1, 2, false, tester); @@ -118,13 +156,13 @@ public class DockerProvisioningTest { public void docker_application_deployment_change_to_exclusive_and_back() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); for (int i = 1; i <= 4; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host1"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host1"); for (int i = 5; i <= 8; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host2"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host2"); for (int i = 9; i <= 12; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host3"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host3"); for (int i = 13; i <= 16; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host4"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host4"); ApplicationId application1 = tester.makeApplicationId(); prepareAndActivate(application1, 2, false, tester); @@ -147,13 +185,13 @@ public class DockerProvisioningTest { public void docker_application_deployment_with_exclusive_app_causing_allocation_failure() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east"))); for (int i = 1; i <= 4; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host1"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host1"); for (int i = 5; i <= 8; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host2"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host2"); for (int i = 9; i <= 12; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host3"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host3"); for (int i = 13; i <= 16; i++) - tester.makeReadyVirtualNode(i, dockerFlavor, "host4"); + tester.makeReadyVirtualDockerNode(i, dockerFlavor, "host4"); ApplicationId application1 = tester.makeApplicationId(); prepareAndActivate(application1, 2, true, tester); @@ -180,7 +218,7 @@ public class DockerProvisioningTest { public void get_specified_flavor_not_default_flavor_for_docker() { ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.test, RegionName.from("corp-us-east-1"))); ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyDockerNodes(1, dockerFlavor, "dockerHost"); + tester.makeReadyVirtualDockerNodes(1, dockerFlavor, "dockerHost"); List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false), 1, 1, dockerFlavor); tester.activate(application1, new HashSet<>(hosts)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java index 04138dfce55..8fdaf6c9eaa 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java @@ -335,29 +335,38 @@ public class ProvisioningTester { return nodeRepository.setReady(nodes, Agent.system, getClass().getSimpleName()); } - /** Creates a set of virtual docker nodes on a single docker host */ - public List<Node> makeReadyDockerNodes(int n, String flavor, String dockerHostId) { - return makeReadyVirtualNodes(n, flavor, Optional.of(dockerHostId)); + /** Creates a set of virtual docker hosts */ + public List<Node> makeReadyVirtualDockerHosts(int n, String flavor) { + return makeReadyVirtualNodes(n, 1, flavor, Optional.empty(), + i -> "dockerHost" + i, NodeType.host); } - /** Creates a set of virtual nodes on a single parent host */ - public List<Node> makeReadyVirtualNodes(int n, String flavor, Optional<String> parentHostId) { - return makeReadyVirtualNodes(n, 0, flavor, parentHostId, index -> UUID.randomUUID().toString()); + /** Creates a set of virtual docker nodes on a single docker host starting with index 1 and increasing */ + public List<Node> makeReadyVirtualDockerNodes(int n, String flavor, String dockerHostId) { + return makeReadyVirtualNodes(n, 1, flavor, Optional.of(dockerHostId), + i -> String.format("%s-%03d", dockerHostId, i), NodeType.tenant); } - /** Creates a set of virtual nodes on a single parent host */ - public List<Node> makeReadyVirtualNode(int index, String flavor, String parentHostId) { - return makeReadyVirtualNodes(1, index, flavor, Optional.of(parentHostId), i -> String.format("node%03d", i)); + /** Creates a single of virtual docker node on a single parent host */ + public List<Node> makeReadyVirtualDockerNode(int index, String flavor, String dockerHostId) { + return makeReadyVirtualNodes(1, index, flavor, Optional.of(dockerHostId), + i -> String.format("%s-%03d", dockerHostId, i), NodeType.tenant); + } + + /** Creates a set of virtual nodes without a parent host */ + public List<Node> makeReadyVirtualNodes(int n, String flavor) { + return makeReadyVirtualNodes(n, 0, flavor, Optional.empty(), + i -> UUID.randomUUID().toString(), NodeType.tenant); } /** Creates a set of virtual nodes on a single parent host */ - public List<Node> makeReadyVirtualNodes(int count, int startIndex, String flavor, Optional<String> parentHostId, - Function<Integer, String> nodeNamer) { + private List<Node> makeReadyVirtualNodes(int count, int startIndex, String flavor, Optional<String> parentHostId, + Function<Integer, String> nodeNamer, NodeType nodeType) { List<Node> nodes = new ArrayList<>(count); for (int i = startIndex; i < count + startIndex; i++) { String hostname = nodeNamer.apply(i); nodes.add(nodeRepository.createNode("openstack-id", hostname, parentHostId, - nodeFlavors.getFlavorOrThrow(flavor), NodeType.tenant)); + nodeFlavors.getFlavorOrThrow(flavor), nodeType)); } nodes = nodeRepository.addNodes(nodes); nodes = nodeRepository.setDirty(nodes, Agent.system, getClass().getSimpleName()); @@ -365,10 +374,6 @@ public class ProvisioningTester { return nodes; } - public List<Node> makeReadyVirtualNodes(int n, String flavor, String parentHostId) { - return makeReadyVirtualNodes(n, flavor, Optional.of(parentHostId)); - } - /** Returns the hosts from the input list which are not retired */ public List<HostSpec> nonRetired(Collection<HostSpec> hosts) { return hosts.stream().filter(host -> ! host.membership().get().retired()).collect(Collectors.toList()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java index c4af98dbbb6..9321d644242 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java @@ -49,10 +49,10 @@ public class VirtualNodeProvisioningTest { @Test public void distinct_parent_host_for_each_node_in_a_cluster() { - tester.makeReadyVirtualNodes(2, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(2, flavor, "parentHost2"); - tester.makeReadyVirtualNodes(2, flavor, "parentHost3"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost4"); + tester.makeReadyVirtualDockerNodes(2, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(2, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(2, flavor, "parentHost3"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost4"); final int containerNodeCount = 4; final int contentNodeCount = 3; @@ -88,7 +88,7 @@ public class VirtualNodeProvisioningTest { // Allowed to use same parent host for several nodes in same cluster in dev { tester = new ProvisioningTester(new Zone(Environment.dev, RegionName.from("us-east"))); - tester.makeReadyVirtualNodes(4, "default", "parentHost1"); + tester.makeReadyVirtualDockerNodes(4, "default", "parentHost1"); List<HostSpec> containerHosts = prepare(containerClusterSpec, containerNodeCount, groups); List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); @@ -101,7 +101,7 @@ public class VirtualNodeProvisioningTest { // Allowed to use same parent host for several nodes in same cluster in CD (even if prod env) { tester = new ProvisioningTester(new Zone(SystemName.cd, Environment.prod, RegionName.from("us-east"))); - tester.makeReadyVirtualNodes(4, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(4, flavor, "parentHost1"); List<HostSpec> containerHosts = prepare(containerClusterSpec, containerNodeCount, groups); List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); @@ -113,12 +113,12 @@ public class VirtualNodeProvisioningTest { @Test public void will_retire_clashing_active() { - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost3"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost4"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost5"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost6"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost3"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost4"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost5"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost6"); int containerNodeCount = 2; int contentNodeCount = 2; @@ -146,10 +146,10 @@ public class VirtualNodeProvisioningTest { @Test public void fail_when_all_hosts_become_clashing() { - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost3"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost4"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost3"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost4"); int containerNodeCount = 2; int contentNodeCount = 2; @@ -177,8 +177,8 @@ public class VirtualNodeProvisioningTest { @Test(expected = OutOfCapacityException.class) public void fail_when_too_few_distinct_parent_hosts() { - tester.makeReadyVirtualNodes(2, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(2, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost2"); int contentNodeCount = 3; List<HostSpec> hosts = prepare(contentClusterSpec, contentNodeCount, 1); @@ -190,9 +190,9 @@ public class VirtualNodeProvisioningTest { @Test public void incomplete_parent_hosts_has_distinct_distribution() { - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); - tester.makeReadyVirtualNodes(1, flavor, Optional.empty()); + tester.makeReadyVirtualDockerNode(1, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNode(1, flavor, "parentHost2"); + tester.makeReadyVirtualNodes(1, flavor); final int contentNodeCount = 3; final int groups = 1; @@ -200,15 +200,15 @@ public class VirtualNodeProvisioningTest { activate(contentHosts); assertEquals(3, getNodes(applicationId).size()); - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNode(2, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNode(2, flavor, "parentHost2"); assertEquals(contentHosts, prepare(contentClusterSpec, contentNodeCount, groups)); } @Test public void indistinct_distribution_with_known_ready_nodes() { - tester.makeReadyVirtualNodes(3, flavor, Optional.empty()); + tester.makeReadyVirtualNodes(3, flavor); final int contentNodeCount = 3; final int groups = 1; @@ -225,8 +225,8 @@ public class VirtualNodeProvisioningTest { nodes = getNodes(applicationId); assertEquals(3, nodes.stream().filter(n -> n.parentHostname().isPresent()).count()); - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(2, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(2, flavor, "parentHost2"); OutOfCapacityException expectedException = null; try { @@ -239,7 +239,7 @@ public class VirtualNodeProvisioningTest { @Test public void unknown_distribution_with_known_ready_nodes() { - tester.makeReadyVirtualNodes(3, flavor, Optional.empty()); + tester.makeReadyVirtualNodes(3, flavor); final int contentNodeCount = 3; final int groups = 1; @@ -247,15 +247,15 @@ public class VirtualNodeProvisioningTest { activate(contentHosts); assertEquals(3, getNodes(applicationId).size()); - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost2"); - tester.makeReadyVirtualNodes(1, flavor, "parentHost3"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost1"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost2"); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost3"); assertEquals(contentHosts, prepare(contentClusterSpec, contentNodeCount, groups)); } @Test public void unknown_distribution_with_known_and_unknown_ready_nodes() { - tester.makeReadyVirtualNodes(3, flavor, Optional.empty()); + tester.makeReadyVirtualNodes(3, flavor); final int contentNodeCount = 3; final int groups = 1; @@ -263,8 +263,8 @@ public class VirtualNodeProvisioningTest { activate(contentHosts); assertEquals(3, getNodes(applicationId).size()); - tester.makeReadyVirtualNodes(1, flavor, "parentHost1"); - tester.makeReadyVirtualNodes(1, flavor, Optional.empty()); + tester.makeReadyVirtualDockerNodes(1, flavor, "parentHost1"); + tester.makeReadyVirtualNodes(1, flavor); assertEquals(contentHosts, prepare(contentClusterSpec, contentNodeCount, groups)); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java index caecce1634d..c6546f05955 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java @@ -13,8 +13,6 @@ import javax.security.auth.x500.X500Principal; import java.math.BigInteger; import java.net.URI; import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; @@ -24,7 +22,6 @@ import java.util.Map; import java.util.Optional; import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA; -import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_RSA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; |