diff options
18 files changed, 102 insertions, 90 deletions
diff --git a/node-repository/src/main/config/node-repository.xml b/node-repository/src/main/config/node-repository.xml index c10da86d2a8..2abd3f890fc 100644 --- a/node-repository/src/main/config/node-repository.xml +++ b/node-repository/src/main/config/node-repository.xml @@ -3,6 +3,7 @@ <component id="com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner" bundle="node-repository" /> <component id="NodeRepository" class="com.yahoo.vespa.hosted.provision.NodeRepository" bundle="node-repository"/> <component id="com.yahoo.vespa.hosted.provision.maintenance.NodeRepositoryMaintenance" bundle="node-repository"/> +<component id="com.yahoo.vespa.hosted.provision.lb.LoadBalancerServiceProvider" bundle="node-repository"/> <component id="com.yahoo.config.provision.NodeFlavors" bundle="config-provisioning" /> <handler id="com.yahoo.vespa.hosted.provision.restapi.v2.NodesApiHandler" bundle="node-repository"> 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 398f3fceca8..ed6d1744af4 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 @@ -52,13 +52,6 @@ public class NodeList { return new NodeList(nodes.stream().filter(node -> node.allocation().get().membership().cluster().type().equals(type)).collect(Collectors.toList())); } - /** Returns the subset of nodes that are in the given state */ - public NodeList in(Node.State state) { - return nodes.stream() - .filter(node -> node.state() == state) - .collect(collectingAndThen(Collectors.toList(), NodeList::new)); - } - /** Returns the subset of nodes owned by the given application */ public NodeList owner(ApplicationId application) { return nodes.stream() diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java index d03c4b8dd09..d8c1323151d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java @@ -99,6 +99,11 @@ public class CuratorDatabase { curator.create(path); } + /** Returns whether given path exists */ + public boolean exists(Path path) { + return curator.exists(path); + } + // --------- Read operations ------------------------------------------------------------------------------- // These can read from the memory file system, which accurately mirrors the ZooKeeper content IF diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java index 0fc5626241f..fb3f801cf65 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java @@ -11,6 +11,7 @@ import com.yahoo.config.provision.Zone; import com.yahoo.log.LogLevel; import com.yahoo.path.Path; import com.yahoo.transaction.NestedTransaction; +import com.yahoo.transaction.Transaction; import com.yahoo.vespa.curator.Curator; import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.curator.transaction.CuratorOperations; @@ -26,6 +27,7 @@ import com.yahoo.vespa.hosted.provision.node.Status; import java.time.Clock; import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -86,6 +88,7 @@ public class CuratorDatabaseClient { curatorDatabase.create(infrastructureVersionsPath()); curatorDatabase.create(osVersionsPath()); curatorDatabase.create(loadBalancersRoot); + curatorDatabase.create(flagsRoot); } /** @@ -432,15 +435,19 @@ public class CuratorDatabaseClient { } public void writeLoadBalancer(LoadBalancer loadBalancer) { - Path path = loadBalancerPath(loadBalancer.id()); - curatorDatabase.create(path); NestedTransaction transaction = new NestedTransaction(); - CuratorTransaction curatorTransaction = curatorDatabase.newCuratorTransactionIn(transaction); - curatorTransaction.add(CuratorOperations.setData(path.getAbsolute(), - LoadBalancerSerializer.toJson(loadBalancer))); + writeLoadBalancers(Collections.singletonList(loadBalancer), transaction); transaction.commit(); } + public void writeLoadBalancers(Collection<LoadBalancer> loadBalancers, NestedTransaction transaction) { + CuratorTransaction curatorTransaction = curatorDatabase.newCuratorTransactionIn(transaction); + loadBalancers.forEach(loadBalancer -> { + curatorTransaction.add(createOrSet(loadBalancerPath(loadBalancer.id()), + LoadBalancerSerializer.toJson(loadBalancer))); + }); + } + public void removeLoadBalancer(LoadBalancer loadBalancer) { NestedTransaction transaction = new NestedTransaction(); CuratorTransaction curatorTransaction = curatorDatabase.newCuratorTransactionIn(transaction); @@ -458,11 +465,9 @@ public class CuratorDatabaseClient { public void writeFlag(Flag flag) { Path path = flagPath(flag.id()); - curatorDatabase.create(path); NestedTransaction transaction = new NestedTransaction(); CuratorTransaction curatorTransaction = curatorDatabase.newCuratorTransactionIn(transaction); - curatorTransaction.add(CuratorOperations.setData(path.getAbsolute(), - FlagSerializer.toJson(flag))); + curatorTransaction.add(createOrSet(path, FlagSerializer.toJson(flag))); transaction.commit(); } @@ -478,4 +483,11 @@ public class CuratorDatabaseClient { return flagsRoot.append(id.serializedValue()); } + private Transaction.Operation createOrSet(Path path, byte[] data) { + if (curatorDatabase.exists(path)) { + return CuratorOperations.setData(path.getAbsolute(), data); + } + return CuratorOperations.create(path.getAbsolute(), data); + } + } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java index cc2bdad70c2..27ab25be3d5 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java @@ -1,11 +1,11 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.provisioning; -import com.google.common.net.InetAddresses; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostName; 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; @@ -13,10 +13,9 @@ import com.yahoo.vespa.hosted.provision.lb.LoadBalancer; import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId; import com.yahoo.vespa.hosted.provision.lb.LoadBalancerService; import com.yahoo.vespa.hosted.provision.lb.Real; +import com.yahoo.vespa.hosted.provision.node.IP; import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -43,6 +42,11 @@ public class LoadBalancerProvisioner { this.service = service; } + /** Get load balancers assigned to given application */ + public List<LoadBalancer> get(ApplicationId application) { + return db.readLoadBalancers(application); + } + /** * Provision load balancer(s) for given application. * @@ -62,17 +66,17 @@ public class LoadBalancerProvisioner { } } - /** Deactivate all load balancers assigned to given application */ - public void deactivate(ApplicationId application) { + /** + * Deactivate all load balancers assigned to given application. This is a no-op if an application does not have any + * load balancer(s) + */ + public void deactivate(ApplicationId application, NestedTransaction transaction) { try (Mutex applicationLock = nodeRepository.lock(application)) { try (Mutex loadBalancersLock = db.lockLoadBalancers()) { - if (!activeContainers(application).isEmpty()) { - throw new IllegalArgumentException(application + " has active containers, refusing to deactivate load balancers"); - } - db.readLoadBalancers(application) - .stream() - .map(LoadBalancer::deactivate) - .forEach(db::writeLoadBalancer); + List<LoadBalancer> deactivatedLoadBalancers = db.readLoadBalancers(application).stream() + .map(LoadBalancer::deactivate) + .collect(Collectors.toList()); + db.writeLoadBalancers(deactivatedLoadBalancers, transaction); } } } @@ -90,9 +94,8 @@ public class LoadBalancerProvisioner { /** Returns a list of active containers for given application, grouped by cluster ID */ private Map<ClusterSpec.Id, List<Node>> activeContainers(ApplicationId application) { - return new NodeList(nodeRepository.getNodes()) + return new NodeList(nodeRepository.getNodes(Node.State.active)) .owner(application) - .in(Node.State.active) .type(ClusterSpec.Type.container) .asList() .stream() @@ -105,21 +108,13 @@ public class LoadBalancerProvisioner { // Remove addresses unreachable by the load balancer service switch (service.protocol()) { case ipv4: - reachable.removeIf(this::isIpv6); + reachable.removeIf(IP::isV6); break; case ipv6: - reachable.removeIf(this::isIpv4); + reachable.removeIf(IP::isV4); break; } return reachable; } - private boolean isIpv4(String ipAddress) { - return InetAddresses.forString(ipAddress) instanceof Inet4Address; - } - - private boolean isIpv6(String ipAddress) { - return InetAddresses.forString(ipAddress) instanceof Inet6Address; - } - } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index 09f8fc41239..c02bba79698 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -20,6 +20,7 @@ import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.flag.FlagId; +import com.yahoo.vespa.hosted.provision.lb.LoadBalancerService; import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter; import com.yahoo.vespa.hosted.provision.node.filter.NodeHostFilter; @@ -47,13 +48,15 @@ public class NodeRepositoryProvisioner implements Provisioner { private final Zone zone; private final Preparer preparer; private final Activator activator; + private final LoadBalancerProvisioner loadBalancerProvisioner; int getSpareCapacityProd() { return SPARE_CAPACITY_PROD; } @Inject - public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone) { + public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone, + LoadBalancerService loadBalancerService) { this.nodeRepository = nodeRepository; this.capacityPolicies = new CapacityPolicies(zone, flavors); this.zone = zone; @@ -61,6 +64,7 @@ public class NodeRepositoryProvisioner implements Provisioner { ? SPARE_CAPACITY_PROD : SPARE_CAPACITY_NONPROD); this.activator = new Activator(nodeRepository); + this.loadBalancerProvisioner = new LoadBalancerProvisioner(nodeRepository, loadBalancerService); } /** @@ -105,9 +109,16 @@ public class NodeRepositoryProvisioner implements Provisioner { public void activate(NestedTransaction transaction, ApplicationId application, Collection<HostSpec> hosts) { validate(hosts); activator.activate(application, hosts, transaction); - if (nodeRepository.flags().get(FlagId.exclusiveLoadBalancer).isEnabled(application)) { - // TODO: Provision load balancer - } + transaction.onCommitted(() -> { + if (nodeRepository.flags().get(FlagId.exclusiveLoadBalancer).isEnabled(application)) { + try { + loadBalancerProvisioner.provision(application); + } catch (Exception e) { + log.log(LogLevel.ERROR, "Failed to provision load balancer for application " + + application.toShortString(), e); + } + } + }); } @Override @@ -118,6 +129,7 @@ public class NodeRepositoryProvisioner implements Provisioner { @Override public void remove(NestedTransaction transaction, ApplicationId application) { nodeRepository.deactivate(application, transaction); + loadBalancerProvisioner.deactivate(application, transaction); } private List<HostSpec> asSortedHosts(List<Node> nodes) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java index 46c144a1758..27215529cf4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/ContainerConfig.java @@ -23,6 +23,7 @@ public class ContainerConfig { " <component id='com.yahoo.vespa.hosted.provision.testutils.ServiceMonitorStub'/>\n" + " <component id='com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors'/>\n" + " <component id='com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository'/>\n" + + " <component id='com.yahoo.vespa.hosted.provision.lb.LoadBalancerServiceProvider'/>\n" + " <component id='com.yahoo.vespa.hosted.provision.maintenance.NodeRepositoryMaintenance'/>\n" + " <component id='com.yahoo.config.provision.Zone'/>\n" + " <handler id='com.yahoo.vespa.hosted.provision.restapi.v2.NodesApiHandler'>\n" + diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index e3fca286830..5d8f342d6bf 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -57,7 +57,7 @@ public class MockNodeRepository extends NodeRepository { } private void populate() { - NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(this, flavors, Zone.defaultZone()); + NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(this, flavors, Zone.defaultZone(), new LoadBalancerServiceMock()); List<Node> nodes = new ArrayList<>(); // Regular nodes diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockProvisioner.java index af0ca7b6b75..a0defcd0dfa 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockProvisioner.java @@ -10,6 +10,7 @@ import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.ProvisionLogger; import com.yahoo.config.provision.Provisioner; import com.yahoo.transaction.NestedTransaction; +import com.yahoo.vespa.hosted.provision.lb.LoadBalancerService; import java.util.Collection; import java.util.Collections; @@ -21,7 +22,9 @@ import java.util.List; public class MockProvisioner implements Provisioner { @Inject - public MockProvisioner() {} + public MockProvisioner(LoadBalancerService loadBalancerService) { + // Tests that we can inject a default instance using LoadBalancerServiceProvider + } @Override public List<HostSpec> prepare(ApplicationId applicationId, ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) { 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 a5f1dbb03cb..c98d53da874 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 @@ -26,6 +26,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import org.junit.Test; @@ -34,7 +35,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -238,7 +238,7 @@ public class FailedExpirerTest { new MockNameResolver().mockAnyLookup(), new DockerImage("docker-image"), true); - this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, Zone.defaultZone()); + this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, Zone.defaultZone(), new LoadBalancerServiceMock()); this.expirer = new FailedExpirer(nodeRepository, zone, clock, Duration.ofMinutes(30), new JobControl(nodeRepository.database())); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java index c29f26ef1f6..3c7d634b495 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java @@ -29,6 +29,7 @@ import com.yahoo.vespa.hosted.provision.monitoring.MetricsReporterTest; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockDeployer; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import com.yahoo.vespa.hosted.provision.testutils.OrchestratorMock; @@ -84,7 +85,7 @@ public class NodeFailTester { curator = new MockCurator(); nodeRepository = new NodeRepository(nodeFlavors, curator, clock, zone, new MockNameResolver().mockAnyLookup(), new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true); - provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone); + provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone, new LoadBalancerServiceMock()); hostLivenessTracker = new TestHostLivenessTracker(clock); orchestrator = new OrchestratorMock(); this.configserverConfig = configserverConfig; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java index 2bf4f831072..5cb0adc8718 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java @@ -22,6 +22,7 @@ import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.FlavorSpareChecker; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockDeployer; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import com.yahoo.vespa.orchestrator.OrchestrationException; @@ -74,7 +75,7 @@ public class NodeRetirerTester { nodeRepository = new NodeRepository(nodeFlavors, curator, clock, zone, new MockNameResolver().mockAnyLookup(), new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true); jobControl = new JobControl(nodeRepository.database()); - NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone); + NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone, new LoadBalancerServiceMock()); deployer = new MockDeployer(provisioner, clock, apps); flavors = nodeFlavors.getFlavors().stream().sorted(Comparator.comparing(Flavor::name)).collect(Collectors.toList()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java index 961e9991d71..55fdd1dadb4 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java @@ -26,6 +26,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockDeployer; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import org.junit.Test; @@ -122,7 +123,7 @@ public class OperatorChangeApplicationMaintainerTest { Fixture(Zone zone, NodeRepository nodeRepository, NodeFlavors flavors, Curator curator) { this.nodeRepository = nodeRepository; this.curator = curator; - this.provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, zone); + this.provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, zone, new LoadBalancerServiceMock()); } void activate() { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java index 7e48edfc805..87b98f7c002 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java @@ -27,6 +27,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockDeployer; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import org.junit.After; @@ -247,7 +248,7 @@ public class PeriodicApplicationMaintainerTest { Fixture(Zone zone, NodeRepository nodeRepository, NodeFlavors flavors, Curator curator) { this.nodeRepository = nodeRepository; this.curator = curator; - this.provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, zone); + this.provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, zone, new LoadBalancerServiceMock()); Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>(); apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java index 0301c570ad1..fba1bead42f 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java @@ -17,6 +17,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import org.junit.Test; @@ -44,7 +45,7 @@ public class ReservationExpirerTest { new MockNameResolver().mockAnyLookup(), new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true); - NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, Zone.defaultZone()); + NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, Zone.defaultZone(), new LoadBalancerServiceMock()); List<Node> nodes = new ArrayList<>(2); nodes.add(nodeRepository.createNode(UUID.randomUUID().toString(), UUID.randomUUID().toString(), Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant)); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java index 03f82e5c3d7..3f21e6b74ae 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java @@ -26,6 +26,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import com.yahoo.vespa.hosted.provision.provisioning.NodeRepositoryProvisioner; +import com.yahoo.vespa.hosted.provision.testutils.LoadBalancerServiceMock; import com.yahoo.vespa.hosted.provision.testutils.MockDeployer; import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; import com.yahoo.vespa.orchestrator.OrchestrationException; @@ -60,7 +61,7 @@ public class RetiredExpirerTest { private final NodeRepository nodeRepository = new NodeRepository(nodeFlavors, curator, clock, zone, new MockNameResolver().mockAnyLookup(), new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true); - private final NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone); + private final NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone, new LoadBalancerServiceMock()); private final Orchestrator orchestrator = mock(Orchestrator.class); private static final Duration RETIRED_EXPIRATION = Duration.ofHours(12); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java index ab7fda20889..ba8fad1f3c5 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java @@ -9,8 +9,8 @@ import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.Zone; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.flag.FlagId; import com.yahoo.vespa.hosted.provision.lb.LoadBalancer; -import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId; import com.yahoo.vespa.hosted.provision.lb.LoadBalancerService; import com.yahoo.vespa.hosted.provision.lb.Real; import com.yahoo.vespa.hosted.provision.node.Agent; @@ -20,13 +20,11 @@ import org.junit.Test; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; /** * @author mpolden @@ -51,6 +49,7 @@ public class LoadBalancerProvisionerTest { public void provision_load_balancer() { ClusterSpec.Id containerCluster1 = ClusterSpec.Id.from("qrs1"); ClusterSpec.Id contentCluster = ClusterSpec.Id.from("content"); + tester.nodeRepository().flags().setEnabled(FlagId.exclusiveLoadBalancer, true); tester.activate(app1, prepare(app1, clusterRequest(ClusterSpec.Type.container, containerCluster1), clusterRequest(ClusterSpec.Type.content, contentCluster))); @@ -58,40 +57,32 @@ public class LoadBalancerProvisionerTest { clusterRequest(ClusterSpec.Type.container, ClusterSpec.Id.from("qrs")))); // Provision a load balancer for each application - Map<LoadBalancerId, LoadBalancer> loadBalancers = loadBalancerProvisioner.provision(app1); - loadBalancerProvisioner.provision(app2); + List<LoadBalancer> loadBalancers = loadBalancerProvisioner.get(app1); assertEquals(1, loadBalancers.size()); - LoadBalancer loadBalancer = loadBalancers.values().iterator().next(); - assertEquals(loadBalancer.id().application(), app1); - assertEquals(loadBalancer.id().cluster(), containerCluster1); - assertEquals(loadBalancer.ports(), Collections.singletonList(4443)); - assertEquals(loadBalancer.reals().get(0).ipAddress(), "127.0.0.1"); - assertEquals(loadBalancer.reals().get(0).port(), 4443); - assertEquals(loadBalancer.reals().get(1).ipAddress(), "127.0.0.2"); - assertEquals(loadBalancer.reals().get(1).port(), 4443); + assertEquals(loadBalancers.get(0).id().application(), app1); + assertEquals(loadBalancers.get(0).id().cluster(), containerCluster1); + assertEquals(loadBalancers.get(0).ports(), Collections.singletonList(4443)); + assertEquals(loadBalancers.get(0).reals().get(0).ipAddress(), "127.0.0.1"); + assertEquals(loadBalancers.get(0).reals().get(0).port(), 4443); + assertEquals(loadBalancers.get(0).reals().get(1).ipAddress(), "127.0.0.2"); + assertEquals(loadBalancers.get(0).reals().get(1).port(), 4443); // A container is failed List<Node> containers = tester.getNodes(app1).type(ClusterSpec.Type.container).asList(); - Node container1 = containers.get(0); - Node container2 = containers.get(1); - tester.nodeRepository().fail(container1.hostname(), Agent.system, "Failed by unit test"); - - // Reprovisioning load balancer removes failed container - loadBalancer = loadBalancerProvisioner.provision(app1).values().iterator().next(); - assertEquals(1, loadBalancer.reals().size()); - assertEquals(container2.hostname(), loadBalancer.reals().get(0).hostname().value()); + tester.nodeRepository().fail(containers.get(0).hostname(), Agent.system, "Failed by unit test"); // Redeploying replaces failed node tester.activate(app1, prepare(app1, clusterRequest(ClusterSpec.Type.container, containerCluster1), clusterRequest(ClusterSpec.Type.content, contentCluster))); - // Reprovisioning load balancer adds the new node - Node container3 = tester.getNodes(app1).type(ClusterSpec.Type.container).asList().get(1); - loadBalancer = loadBalancerProvisioner.provision(app1).values().iterator().next(); + // Redeploy removed replaced failed node in load balancer + containers = tester.getNodes(app1).type(ClusterSpec.Type.container).asList(); + LoadBalancer loadBalancer = loadBalancerProvisioner.get(app1).get(0); assertEquals(2, loadBalancer.reals().size()); - assertEquals(container3.hostname(), loadBalancer.reals().get(1).hostname().value()); + assertEquals(containers.get(0).hostname(), loadBalancer.reals().get(0).hostname().value()); + assertEquals(containers.get(1).hostname(), loadBalancer.reals().get(1).hostname().value()); // Add another container cluster ClusterSpec.Id containerCluster2 = ClusterSpec.Id.from("qrs2"); @@ -101,7 +92,7 @@ public class LoadBalancerProvisionerTest { clusterRequest(ClusterSpec.Type.content, contentCluster))); // Load balancer is provisioned for second container cluster - loadBalancers = loadBalancerProvisioner.provision(app1); + loadBalancers = loadBalancerProvisioner.get(app1); assertEquals(2, loadBalancers.size()); List<HostName> activeContainers = tester.getNodes(app1, Node.State.active) .type(ClusterSpec.Type.container).asList() @@ -110,28 +101,21 @@ public class LoadBalancerProvisionerTest { .map(HostName::from) .sorted() .collect(Collectors.toList()); - List<HostName> reals = loadBalancers.values().stream() + List<HostName> reals = loadBalancers.stream() .flatMap(lb -> lb.reals().stream()) .map(Real::hostname) .sorted() .collect(Collectors.toList()); assertEquals(activeContainers, reals); - // Removing load balancer with active containers fails - try { - loadBalancerProvisioner.deactivate(app1); - fail("Expected exception"); - } catch (IllegalArgumentException ignored) {} - - // Application and load balancer is removed + // Application is removed and load balancer is deactivated NestedTransaction removeTransaction = new NestedTransaction(); tester.provisioner().remove(removeTransaction, app1); removeTransaction.commit(); - loadBalancerProvisioner.deactivate(app1); List<LoadBalancer> assignedLoadBalancer = tester.nodeRepository().database().readLoadBalancers(app1); assertEquals(2, loadBalancers.size()); - assertTrue("Load balancers marked for deletion", assignedLoadBalancer.stream().allMatch(LoadBalancer::inactive)); + assertTrue("Deactivated load balancers", assignedLoadBalancer.stream().allMatch(LoadBalancer::inactive)); } private ClusterSpec clusterRequest(ClusterSpec.Type type, ClusterSpec.Id id) { 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 8c0937d34b8..27b592b0d55 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 @@ -91,8 +91,8 @@ public class ProvisioningTester { new DockerImage("docker-registry.domain.tld:8080/dist/vespa"), true); this.orchestrator = mock(Orchestrator.class); doThrow(new RuntimeException()).when(orchestrator).acquirePermissionToRemove(any()); - this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone); this.loadBalancerService = new LoadBalancerServiceMock(); + this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone, loadBalancerService); this.capacityPolicies = new CapacityPolicies(zone, nodeFlavors); this.provisionLogger = new NullProvisionLogger(); } |