summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-01-24 10:37:07 +0100
committerGitHub <noreply@github.com>2019-01-24 10:37:07 +0100
commit25598180a1ae78c854208c02b47282b161d12df1 (patch)
tree4f27ab9f7ddf2e88177408d1c147efa984f54adf
parenta7266a1e0ee4d9463e1af93ab2565966e8b4c981 (diff)
parente3c50f15f5508140abaccb22b26fd2c881a93bf4 (diff)
Merge pull request #8185 from vespa-engine/mpolden/lb-acl
Add load balancer networks to node ACLs
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java30
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancer.java32
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerList.java45
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java19
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/NodeAcl.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisioner.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java19
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java61
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java45
15 files changed, 224 insertions, 114 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
index c870d7d830c..9c389561650 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java
@@ -16,6 +16,8 @@ import com.yahoo.transaction.Mutex;
import com.yahoo.transaction.NestedTransaction;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.hosted.provision.flag.Flags;
+import com.yahoo.vespa.hosted.provision.lb.LoadBalancer;
+import com.yahoo.vespa.hosted.provision.lb.LoadBalancerList;
import com.yahoo.vespa.hosted.provision.maintenance.PeriodicApplicationMaintainer;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.NodeAcl;
@@ -35,7 +37,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -173,6 +175,11 @@ public class NodeRepository extends AbstractComponent {
return new NodeList(getNodes());
}
+ /** Returns a filterable list of all load balancers in this repository */
+ public LoadBalancerList loadBalancers() {
+ return new LoadBalancerList(database().readLoadBalancers().values());
+ }
+
public List<Node> getNodes(ApplicationId id, Node.State ... inState) { return db.getNodes(id, inState); }
public List<Node> getInactive() { return db.getNodes(Node.State.inactive); }
public List<Node> getFailed() { return db.getNodes(Node.State.failed); }
@@ -180,14 +187,21 @@ public class NodeRepository extends AbstractComponent {
/**
* Returns the ACL for the node (trusted nodes, networks and ports)
*/
- private NodeAcl getNodeAcl(Node node, NodeList candidates) {
+ private NodeAcl getNodeAcl(Node node, NodeList candidates, LoadBalancerList loadBalancers) {
Set<Node> trustedNodes = new TreeSet<>(Comparator.comparing(Node::hostname));
- Set<Integer> trustedPorts = new HashSet<>();
+ Set<Integer> trustedPorts = new LinkedHashSet<>();
+ Set<String> trustedNetworks = new LinkedHashSet<>();
// For all cases below, trust:
// - nodes in same application
+ // - load balancers allocated to application
// - ssh
- node.allocation().ifPresent(allocation -> trustedNodes.addAll(candidates.owner(allocation.owner()).asList()));
+ node.allocation().ifPresent(allocation -> {
+ trustedNodes.addAll(candidates.owner(allocation.owner()).asList());
+ loadBalancers.owner(allocation.owner()).asList().stream()
+ .map(LoadBalancer::networks)
+ .forEach(trustedNetworks::addAll);
+ });
trustedPorts.add(22);
switch (node.type()) {
@@ -241,7 +255,7 @@ public class NodeRepository extends AbstractComponent {
node.hostname(), node.type()));
}
- return new NodeAcl(node, trustedNodes, Collections.emptySet(), trustedPorts);
+ return new NodeAcl(node, trustedNodes, trustedNetworks, trustedPorts);
}
/**
@@ -253,13 +267,13 @@ public class NodeRepository extends AbstractComponent {
*/
public List<NodeAcl> getNodeAcls(Node node, boolean children) {
NodeList candidates = list();
+ LoadBalancerList loadBalancers = loadBalancers();
if (children) {
return candidates.childrenOf(node).asList().stream()
- .map(childNode -> getNodeAcl(childNode, candidates))
+ .map(childNode -> getNodeAcl(childNode, candidates, loadBalancers))
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
- } else {
- return Collections.singletonList(getNodeAcl(node, candidates));
}
+ return Collections.singletonList(getNodeAcl(node, candidates, loadBalancers));
}
public NodeFlavors getAvailableFlavors() {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancer.java
index e59e17815bf..1230c9dc41e 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancer.java
@@ -1,12 +1,14 @@
// 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.lb;
-import com.google.common.collect.Ordering;
+import com.google.common.collect.ImmutableSortedSet;
import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.hosted.provision.maintenance.LoadBalancerExpirer;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Represents a load balancer for an application.
@@ -17,15 +19,22 @@ public class LoadBalancer {
private final LoadBalancerId id;
private final HostName hostname;
- private final List<Integer> ports;
- private final List<Real> reals;
+ private final Set<Integer> ports;
+ private final Set<String> networks;
+ private final Set<Real> reals;
private final boolean inactive;
+ // TODO: Remove this when no longer used by internal code
public LoadBalancer(LoadBalancerId id, HostName hostname, List<Integer> ports, List<Real> reals, boolean inactive) {
+ this(id, hostname, ImmutableSortedSet.copyOf(ports), Collections.emptySet(), ImmutableSortedSet.copyOf(reals), inactive);
+ }
+
+ public LoadBalancer(LoadBalancerId id, HostName hostname, Set<Integer> ports, Set<String> networks, Set<Real> reals, boolean inactive) {
this.id = Objects.requireNonNull(id, "id must be non-null");
this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null");
- this.ports = Ordering.natural().immutableSortedCopy(requirePorts(ports));
- this.reals = Ordering.natural().immutableSortedCopy(Objects.requireNonNull(reals, "targets must be non-null"));
+ this.ports = ImmutableSortedSet.copyOf(requirePorts(ports));
+ this.networks = ImmutableSortedSet.copyOf(Objects.requireNonNull(networks, "networks must be non-null"));
+ this.reals = ImmutableSortedSet.copyOf(Objects.requireNonNull(reals, "targets must be non-null"));
this.inactive = inactive;
}
@@ -40,12 +49,17 @@ public class LoadBalancer {
}
/** Listening port(s) of this load balancer */
- public List<Integer> ports() {
+ public Set<Integer> ports() {
return ports;
}
+ /** Networks (CIDR blocks) of this load balancer */
+ public Set<String> networks() {
+ return networks;
+ }
+
/** Real servers behind this load balancer */
- public List<Real> reals() {
+ public Set<Real> reals() {
return reals;
}
@@ -59,10 +73,10 @@ public class LoadBalancer {
/** Return a copy of this that is set inactive */
public LoadBalancer deactivate() {
- return new LoadBalancer(id, hostname, ports, reals, true);
+ return new LoadBalancer(id, hostname, ports, networks, reals, true);
}
- private static List<Integer> requirePorts(List<Integer> ports) {
+ private static Set<Integer> requirePorts(Set<Integer> ports) {
Objects.requireNonNull(ports, "ports must be non-null");
if (ports.isEmpty()) {
throw new IllegalArgumentException("ports must be non-empty");
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerList.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerList.java
new file mode 100644
index 00000000000..0bccc0d3ab6
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerList.java
@@ -0,0 +1,45 @@
+// 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.lb;
+
+import com.google.common.collect.ImmutableList;
+import com.yahoo.config.provision.ApplicationId;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.collectingAndThen;
+
+/**
+ * A filterable load balancer list
+ *
+ * @author mpolden
+ */
+public class LoadBalancerList {
+
+ private final List<LoadBalancer> loadBalancers;
+
+ public LoadBalancerList(Collection<LoadBalancer> loadBalancers) {
+ this.loadBalancers = ImmutableList.copyOf(Objects.requireNonNull(loadBalancers, "loadBalancers must be non-null"));
+ }
+
+ /** Returns the subset of load balancers owned by given application */
+ public LoadBalancerList owner(ApplicationId application) {
+ return loadBalancers.stream()
+ .filter(lb -> lb.id().application().equals(application))
+ .collect(collectingAndThen(Collectors.toList(), LoadBalancerList::new));
+ }
+
+ /** Returns the subset of load balancers that are inactive */
+ public LoadBalancerList inactive() {
+ return loadBalancers.stream()
+ .filter(LoadBalancer::inactive)
+ .collect(collectingAndThen(Collectors.toList(), LoadBalancerList::new));
+ }
+
+ public List<LoadBalancer> asList() {
+ return loadBalancers;
+ }
+
+}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java
index b5f59414c65..048f3758ac5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java
@@ -4,7 +4,9 @@ package com.yahoo.vespa.hosted.provision.lb;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* A managed load balance service.
@@ -14,7 +16,16 @@ import java.util.List;
public interface LoadBalancerService {
/** Create a load balancer for given application cluster. Implementations are expected to be idempotent */
- LoadBalancer create(ApplicationId application, ClusterSpec.Id cluster, List<Real> reals);
+ // TODO: Remove once removed from all implementations
+ default LoadBalancer create(ApplicationId application, ClusterSpec.Id cluster, List<Real> reals) {
+ return create(application, cluster, new HashSet<>(reals));
+ }
+
+ /** Create a load balancer for given application cluster. Implementations are expected to be idempotent */
+ // TODO: Remove default implementation once implemented everywhere
+ default LoadBalancer create(ApplicationId application, ClusterSpec.Id cluster, Set<Real> reals) {
+ throw new UnsupportedOperationException();
+ }
/** Permanently remove load balancer with given ID */
void remove(LoadBalancerId loadBalancer);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java
index a5a0d8cb2f8..2e01c657d22 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java
@@ -1,14 +1,15 @@
// 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.lb;
+import com.google.common.collect.ImmutableSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* @author mpolden
@@ -27,11 +28,12 @@ public class LoadBalancerServiceMock implements LoadBalancerService {
}
@Override
- public LoadBalancer create(ApplicationId application, ClusterSpec.Id cluster, List<Real> reals) {
+ public LoadBalancer create(ApplicationId application, ClusterSpec.Id cluster, Set<Real> reals) {
LoadBalancer loadBalancer = new LoadBalancer(
new LoadBalancerId(application, cluster),
HostName.from("lb-" + application.toShortString() + "-" + cluster.value()),
- Collections.singletonList(4443),
+ Collections.singleton(4443),
+ ImmutableSet.of("10.2.3.0/24", "10.4.5.0/24"),
reals,
false);
loadBalancers.put(loadBalancer.id(), loadBalancer);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java
index 4b66dff3032..115581c86b0 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirer.java
@@ -5,6 +5,7 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+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.persistence.CuratorDatabaseClient;
@@ -12,7 +13,6 @@ import com.yahoo.vespa.hosted.provision.persistence.CuratorDatabaseClient;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -45,15 +45,15 @@ public class LoadBalancerExpirer extends Maintainer {
List<LoadBalancerId> failed = new ArrayList<>();
Exception lastException = null;
try (Lock lock = db.lockLoadBalancers()) {
- for (LoadBalancerId loadBalancer : inactiveLoadBlancers()) {
- if (hasNodes(loadBalancer.application())) { // Defer removal if there are still nodes allocated to application
+ for (LoadBalancer loadBalancer : nodeRepository().loadBalancers().inactive().asList()) {
+ if (hasNodes(loadBalancer.id().application())) { // Defer removal if there are still nodes allocated to application
continue;
}
try {
- service.remove(loadBalancer);
- db.removeLoadBalancer(loadBalancer);
+ service.remove(loadBalancer.id());
+ db.removeLoadBalancer(loadBalancer.id());
} catch (Exception e) {
- failed.add(loadBalancer);
+ failed.add(loadBalancer.id());
lastException = e;
}
}
@@ -73,11 +73,4 @@ public class LoadBalancerExpirer extends Maintainer {
return !nodeRepository().getNodes(application).isEmpty();
}
- private List<LoadBalancerId> inactiveLoadBlancers() {
- return db.readLoadBalancers().entrySet().stream()
- .filter(entry -> entry.getValue().inactive())
- .map(Map.Entry::getKey)
- .collect(Collectors.toList());
- }
-
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/NodeAcl.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/NodeAcl.java
index 34a8b414ef4..b24fba83b12 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/NodeAcl.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/NodeAcl.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.node;
import com.google.common.collect.ImmutableSet;
import com.yahoo.vespa.hosted.provision.Node;
+import java.util.Objects;
import java.util.Set;
/**
@@ -20,10 +21,10 @@ public class NodeAcl {
private final Set<Integer> trustedPorts;
public NodeAcl(Node node, Set<Node> trustedNodes, Set<String> trustedNetworks, Set<Integer> trustedPorts) {
- this.node = node;
- this.trustedNodes = ImmutableSet.copyOf(trustedNodes);
- this.trustedNetworks = ImmutableSet.copyOf(trustedNetworks);
- this.trustedPorts = ImmutableSet.copyOf(trustedPorts);
+ this.node = Objects.requireNonNull(node, "node must be non-null");
+ this.trustedNodes = ImmutableSet.copyOf(Objects.requireNonNull(trustedNodes, "trustedNodes must be non-null"));
+ this.trustedNetworks = ImmutableSet.copyOf(Objects.requireNonNull(trustedNetworks, "trustedNetworks must be non-null"));
+ this.trustedPorts = ImmutableSet.copyOf(Objects.requireNonNull(trustedPorts, "trustedPorts must be non-null"));
}
public Node node() {
@@ -41,4 +42,5 @@ public class NodeAcl {
public Set<Integer> trustedPorts() {
return trustedPorts;
}
+
}
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 139f42b0f37..cddf668fd34 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
@@ -25,7 +25,6 @@ import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Status;
-import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
@@ -456,12 +455,6 @@ public class CuratorDatabaseClient {
Collections::unmodifiableMap));
}
- public List<LoadBalancer> readLoadBalancers(ApplicationId application) {
- return readLoadBalancers().values().stream()
- .filter(lb -> lb.id().application().equals(application))
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
- }
-
public Optional<LoadBalancer> readLoadBalancer(LoadBalancerId id) {
return read(loadBalancerPath(id), LoadBalancerSerializer::fromJson);
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java
index ba29fcf2920..334e851a4e5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java
@@ -12,8 +12,8 @@ import com.yahoo.vespa.hosted.provision.lb.Real;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.Set;
/**
* Serializer for load balancers.
@@ -26,6 +26,7 @@ public class LoadBalancerSerializer {
private static final String hostnameField = "hostname";
private static final String inactiveField = "inactive";
private static final String portsField = "ports";
+ private static final String networksField = "networks";
private static final String realsField = "reals";
private static final String ipAddressField = "ipAddress";
private static final String portField = "port";
@@ -38,6 +39,8 @@ public class LoadBalancerSerializer {
root.setString(hostnameField, loadBalancer.hostname().toString());
Cursor portArray = root.setArray(portsField);
loadBalancer.ports().forEach(portArray::addLong);
+ Cursor networkArray = root.setArray(networksField);
+ loadBalancer.networks().forEach(networkArray::addString);
Cursor realArray = root.setArray(realsField);
loadBalancer.reals().forEach(real -> {
Cursor realObject = realArray.addObject();
@@ -57,7 +60,7 @@ public class LoadBalancerSerializer {
public static LoadBalancer fromJson(byte[] data) {
Cursor object = SlimeUtils.jsonToSlime(data).get();
- List<Real> reals = new ArrayList<>();
+ Set<Real> reals = new LinkedHashSet<>();
object.field(realsField).traverse((ArrayTraverser) (i, realObject) -> {
reals.add(new Real(HostName.from(realObject.field(hostnameField).asString()),
realObject.field(ipAddressField).asString(),
@@ -65,12 +68,18 @@ public class LoadBalancerSerializer {
});
- List<Integer> ports = new ArrayList<>();
+ Set<Integer> ports = new LinkedHashSet<>();
object.field(portsField).traverse((ArrayTraverser) (i, port) -> ports.add((int) port.asLong()));
+ Set<String> networks = new LinkedHashSet<>();
+ if (object.field(networksField).valid()) { // TODO: Remove check after 2019-03-01
+ object.field(networksField).traverse((ArrayTraverser) (i, network) -> networks.add(network.asString()));
+ }
+
return new LoadBalancer(LoadBalancerId.fromSerializedForm(object.field(idField).asString()),
HostName.from(object.field(hostnameField).asString()),
ports,
+ networks,
reals,
object.field(inactiveField).asBool());
}
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 27ab25be3d5..e486f76c727 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
@@ -42,11 +42,6 @@ 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.
*
@@ -73,9 +68,9 @@ public class LoadBalancerProvisioner {
public void deactivate(ApplicationId application, NestedTransaction transaction) {
try (Mutex applicationLock = nodeRepository.lock(application)) {
try (Mutex loadBalancersLock = db.lockLoadBalancers()) {
- List<LoadBalancer> deactivatedLoadBalancers = db.readLoadBalancers(application).stream()
- .map(LoadBalancer::deactivate)
- .collect(Collectors.toList());
+ List<LoadBalancer> deactivatedLoadBalancers = nodeRepository.loadBalancers().owner(application).asList().stream()
+ .map(LoadBalancer::deactivate)
+ .collect(Collectors.toList());
db.writeLoadBalancers(deactivatedLoadBalancers, transaction);
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
index 04a1cdaeeda..e3d0eb10a2c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/LoadBalancersResponse.java
@@ -9,11 +9,11 @@ import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.lb.LoadBalancer;
+import com.yahoo.vespa.hosted.provision.lb.LoadBalancerList;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -32,12 +32,15 @@ public class LoadBalancersResponse extends HttpResponse {
}
private Optional<ApplicationId> application() {
- return Optional.ofNullable(request.getProperty("application")).map(ApplicationFilter::toApplicationId);
+ return Optional.ofNullable(request.getProperty("application"))
+ .map(ApplicationFilter::toApplicationId);
}
private List<LoadBalancer> loadBalancers() {
- return application().map(nodeRepository.database()::readLoadBalancers)
- .orElseGet(() -> new ArrayList<>(nodeRepository.database().readLoadBalancers().values()));
+ LoadBalancerList loadBalancers = nodeRepository.loadBalancers();
+ return application().map(loadBalancers::owner)
+ .map(LoadBalancerList::asList)
+ .orElseGet(loadBalancers::asList);
}
@Override
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java
index 5dcb4ab6a71..9947d2d5108 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodeAclResponse.java
@@ -4,8 +4,8 @@ package com.yahoo.vespa.hosted.provision.restapi.v2;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.HttpResponse;
import com.yahoo.slime.Cursor;
+import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.Slime;
-import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.NodeAcl;
@@ -33,8 +33,8 @@ public class NodeAclResponse extends HttpResponse {
this.slime = new Slime();
this.aclsForChildren = request.getBooleanProperty(CHILDREN_REQUEST_PROPERTY);
- final Cursor root = slime.setObject();
- final String hostname = baseName(request.getUri().getPath());
+ Cursor root = slime.setObject();
+ String hostname = baseName(request.getUri().getPath());
toSlime(hostname, root);
}
@@ -81,8 +81,8 @@ public class NodeAclResponse extends HttpResponse {
}
@Override
- public void render(OutputStream outputStream) throws IOException {
- outputStream.write(SlimeUtils.toJsonBytes(slime));
+ public void render(OutputStream stream) throws IOException {
+ new JsonFormat(true).encode(stream, slime);
}
@Override
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java
index 5fcbab64429..e0e62fbe628 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java
@@ -1,6 +1,7 @@
// 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.persistence;
+import com.google.common.collect.ImmutableSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.HostName;
@@ -9,8 +10,6 @@ import com.yahoo.vespa.hosted.provision.lb.LoadBalancerId;
import com.yahoo.vespa.hosted.provision.lb.Real;
import org.junit.Test;
-import java.util.Arrays;
-
import static org.junit.Assert.assertEquals;
/**
@@ -25,19 +24,21 @@ public class LoadBalancerSerializerTest {
"default"),
ClusterSpec.Id.from("qrs")),
HostName.from("lb-host"),
- Arrays.asList(4080, 4443),
- Arrays.asList(new Real(HostName.from("real-1"),
- "127.0.0.1",
- 4080),
- new Real(HostName.from("real-2"),
- "127.0.0.2",
- 4080)),
+ ImmutableSet.of(4080, 4443),
+ ImmutableSet.of("10.2.3.4/24"),
+ ImmutableSet.of(new Real(HostName.from("real-1"),
+ "127.0.0.1",
+ 4080),
+ new Real(HostName.from("real-2"),
+ "127.0.0.2",
+ 4080)),
false);
LoadBalancer serialized = LoadBalancerSerializer.fromJson(LoadBalancerSerializer.toJson(loadBalancer));
assertEquals(loadBalancer.id(), serialized.id());
assertEquals(loadBalancer.hostname(), serialized.hostname());
assertEquals(loadBalancer.ports(), serialized.ports());
+ assertEquals(loadBalancer.networks(), serialized.networks());
assertEquals(loadBalancer.inactive(), serialized.inactive());
assertEquals(loadBalancer.reals(), serialized.reals());
}
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 37327d45845..3e69a5391af 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
@@ -10,6 +10,7 @@ import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.flag.FlagId;
import com.yahoo.vespa.hosted.provision.node.NodeAcl;
import org.junit.Before;
import org.junit.Test;
@@ -21,13 +22,14 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import static com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester.createConfig;
+import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
/**
* @author mpolden
@@ -52,15 +54,24 @@ public class AclProvisioningTest {
List<Node> proxyNodes = tester.makeReadyNodes(3, "default", NodeType.proxy);
// Allocate 2 nodes
- List<Node> activeNodes = allocateNodes(2);
+ ApplicationId application = tester.makeApplicationId();
+ List<Node> activeNodes = deploy(application, 2);
assertEquals(2, activeNodes.size());
// Get trusted nodes for the first active node
Node node = activeNodes.get(0);
- List<NodeAcl> nodeAcls = tester.nodeRepository().getNodeAcls(node, false);
+ Supplier<List<NodeAcl>> nodeAcls = () -> tester.nodeRepository().getNodeAcls(node, false);
// Trusted nodes is active nodes in same application, proxy nodes and config servers
- assertAcls(Arrays.asList(activeNodes, proxyNodes, configServers, dockerHost), nodeAcls);
+ assertAcls(Arrays.asList(activeNodes, proxyNodes, configServers, dockerHost), nodeAcls.get());
+
+ // Allocate load balancer
+ tester.nodeRepository().flags().setEnabled(FlagId.exclusiveLoadBalancer, application, true);
+ deploy(application, 2);
+
+ // Load balancer networks are added to ACLs
+ assertAcls(Arrays.asList(activeNodes, proxyNodes, configServers, dockerHost),
+ ImmutableSet.of("10.2.3.0/24", "10.4.5.0/24"), nodeAcls.get());
}
@Test
@@ -72,7 +83,7 @@ public class AclProvisioningTest {
List<Node> proxyNodes = tester.makeReadyNodes(3, "default", NodeType.proxy);
// Allocate 2 nodes to an application
- allocateNodes(2);
+ deploy(2);
// Get trusted nodes for a ready tenant node
Node node = tester.nodeRepository().getNodes(NodeType.tenant, Node.State.ready).get(0);
@@ -92,7 +103,7 @@ public class AclProvisioningTest {
List<Node> proxyNodes = tester.makeReadyNodes(3, "default", NodeType.proxy);
// Allocate 2 nodes
- allocateNodes(4);
+ deploy(4);
List<Node> tenantNodes = tester.nodeRepository().getNodes(NodeType.tenant);
// Get trusted nodes for the first config server
@@ -114,7 +125,7 @@ public class AclProvisioningTest {
// Deploy zone application
ApplicationId zoneApplication = tester.makeApplicationId();
- allocateNodes(Capacity.fromRequiredNodeType(NodeType.proxy), zoneApplication);
+ deploy(zoneApplication, Capacity.fromRequiredNodeType(NodeType.proxy));
// Get trusted nodes for first proxy node
List<Node> proxyNodes = tester.nodeRepository().getNodes(zoneApplication);
@@ -156,7 +167,7 @@ public class AclProvisioningTest {
// Allocate
ApplicationId controllerApplication = tester.makeApplicationId();
- List<Node> controllers = allocateNodes(Capacity.fromRequiredNodeType(NodeType.controller), controllerApplication);
+ List<Node> controllers = deploy(controllerApplication, Capacity.fromRequiredNodeType(NodeType.controller));
// Controllers and hosts all trust each other
List<NodeAcl> controllerAcls = tester.nodeRepository().getNodeAcls(controllers.get(0), false);
@@ -165,6 +176,16 @@ public class AclProvisioningTest {
}
@Test
+ public void trusted_nodes_for_application_with_load_balancer() {
+ // Populate repo
+ tester.makeReadyNodes(10, "default");
+
+ // Allocate 2 nodes
+ List<Node> activeNodes = deploy(2);
+ assertEquals(2, activeNodes.size());
+ }
+
+ @Test
public void resolves_hostnames_from_connection_spec() {
tester.makeConfigServers(3, "default", Version.fromString("6.123.456"));
@@ -178,16 +199,20 @@ public class AclProvisioningTest {
assertEquals(singleton("127.0.1.3"), trustedNodes.next().ipAddresses());
}
- private List<Node> allocateNodes(int nodeCount) {
- return allocateNodes(Capacity.fromNodeCount(nodeCount), tester.makeApplicationId());
+ private List<Node> deploy(int nodeCount) {
+ return deploy(tester.makeApplicationId(), nodeCount);
}
- private List<Node> allocateNodes(Capacity capacity, ApplicationId applicationId) {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"),
+ private List<Node> deploy(ApplicationId application, int nodeCount) {
+ return deploy(application, Capacity.fromNodeCount(nodeCount));
+ }
+
+ private List<Node> deploy(ApplicationId application, Capacity capacity) {
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"),
Version.fromString("6.42"), false);
- List<HostSpec> prepared = tester.prepare(applicationId, cluster, capacity, 1);
- tester.activate(applicationId, new HashSet<>(prepared));
- return tester.getNodes(applicationId, Node.State.active).asList();
+ List<HostSpec> prepared = tester.prepare(application, cluster, capacity, 1);
+ tester.activate(application, new HashSet<>(prepared));
+ return tester.getNodes(application, Node.State.active).asList();
}
private static void assertAcls(List<List<Node>> expected, NodeAcl actual) {
@@ -195,6 +220,10 @@ public class AclProvisioningTest {
}
private static void assertAcls(List<List<Node>> expectedNodes, List<NodeAcl> actual) {
+ assertAcls(expectedNodes, emptySet(), actual);
+ }
+
+ private static void assertAcls(List<List<Node>> expectedNodes, Set<String> expectedNetworks, List<NodeAcl> actual) {
Set<Node> expectedTrustedNodes = expectedNodes.stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet());
@@ -206,6 +235,6 @@ public class AclProvisioningTest {
Set<String> actualTrustedNetworks = actual.stream()
.flatMap(acl -> acl.trustedNetworks().stream())
.collect(Collectors.toSet());
- assertTrue("No networks are trusted", actualTrustedNetworks.isEmpty());
+ assertEquals(expectedNetworks, actualTrustedNetworks);
}
}
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 e340abfa5c1..da99188b113 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
@@ -1,6 +1,7 @@
// 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.collect.Iterators;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
@@ -11,7 +12,6 @@ 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.LoadBalancerService;
import com.yahoo.vespa.hosted.provision.lb.Real;
import com.yahoo.vespa.hosted.provision.node.Agent;
import org.junit.Before;
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@@ -35,14 +36,10 @@ public class LoadBalancerProvisionerTest {
private final ApplicationId app2 = ApplicationId.from("tenant2", "application2", "default");
private ProvisioningTester tester;
- private LoadBalancerService service;
- private LoadBalancerProvisioner loadBalancerProvisioner;
@Before
public void before() {
tester = new ProvisioningTester(Zone.defaultZone());
- service = tester.loadBalancerService();
- loadBalancerProvisioner = new LoadBalancerProvisioner(tester.nodeRepository(), service);
}
@Test
@@ -57,16 +54,16 @@ public class LoadBalancerProvisionerTest {
clusterRequest(ClusterSpec.Type.container, ClusterSpec.Id.from("qrs"))));
// Provision a load balancer for each application
- List<LoadBalancer> loadBalancers = loadBalancerProvisioner.get(app1);
- assertEquals(1, loadBalancers.size());
+ Supplier<List<LoadBalancer>> loadBalancers = () -> tester.nodeRepository().loadBalancers().owner(app1).asList();
+ assertEquals(1, loadBalancers.get().size());
- 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(), 4080);
- assertEquals(loadBalancers.get(0).reals().get(1).ipAddress(), "127.0.0.2");
- assertEquals(loadBalancers.get(0).reals().get(1).port(), 4080);
+ assertEquals(app1, loadBalancers.get().get(0).id().application());
+ assertEquals(containerCluster1, loadBalancers.get().get(0).id().cluster());
+ assertEquals(Collections.singleton(4443), loadBalancers.get().get(0).ports());
+ assertEquals("127.0.0.1", get(loadBalancers.get().get(0).reals(), 0).ipAddress());
+ assertEquals(4080, get(loadBalancers.get().get(0).reals(), 0).port());
+ assertEquals("127.0.0.2", get(loadBalancers.get().get(0).reals(), 1).ipAddress());
+ assertEquals(4080, get(loadBalancers.get().get(0).reals(), 1).port());
// A container is failed
List<Node> containers = tester.getNodes(app1).type(ClusterSpec.Type.container).asList();
@@ -79,10 +76,10 @@ public class LoadBalancerProvisionerTest {
// Redeploy removed replaced failed node in load balancer
containers = tester.getNodes(app1).type(ClusterSpec.Type.container).asList();
- LoadBalancer loadBalancer = loadBalancerProvisioner.get(app1).get(0);
+ LoadBalancer loadBalancer = tester.nodeRepository().loadBalancers().owner(app1).asList().get(0);
assertEquals(2, loadBalancer.reals().size());
- assertEquals(containers.get(0).hostname(), loadBalancer.reals().get(0).hostname().value());
- assertEquals(containers.get(1).hostname(), loadBalancer.reals().get(1).hostname().value());
+ assertEquals(containers.get(0).hostname(), get(loadBalancer.reals(), 0).hostname().value());
+ assertEquals(containers.get(1).hostname(), get(loadBalancer.reals(), 1).hostname().value());
// Add another container cluster
ClusterSpec.Id containerCluster2 = ClusterSpec.Id.from("qrs2");
@@ -92,8 +89,7 @@ public class LoadBalancerProvisionerTest {
clusterRequest(ClusterSpec.Type.content, contentCluster)));
// Load balancer is provisioned for second container cluster
- loadBalancers = loadBalancerProvisioner.get(app1);
- assertEquals(2, loadBalancers.size());
+ assertEquals(2, loadBalancers.get().size());
List<HostName> activeContainers = tester.getNodes(app1, Node.State.active)
.type(ClusterSpec.Type.container).asList()
.stream()
@@ -101,7 +97,7 @@ public class LoadBalancerProvisionerTest {
.map(HostName::from)
.sorted()
.collect(Collectors.toList());
- List<HostName> reals = loadBalancers.stream()
+ List<HostName> reals = loadBalancers.get().stream()
.flatMap(lb -> lb.reals().stream())
.map(Real::hostname)
.sorted()
@@ -113,9 +109,8 @@ public class LoadBalancerProvisionerTest {
tester.provisioner().remove(removeTransaction, app1);
removeTransaction.commit();
- List<LoadBalancer> assignedLoadBalancer = tester.nodeRepository().database().readLoadBalancers(app1);
- assertEquals(2, loadBalancers.size());
- assertTrue("Deactivated load balancers", assignedLoadBalancer.stream().allMatch(LoadBalancer::inactive));
+ assertEquals(2, loadBalancers.get().size());
+ assertTrue("Deactivated load balancers", loadBalancers.get().stream().allMatch(LoadBalancer::inactive));
}
private ClusterSpec clusterRequest(ClusterSpec.Type type, ClusterSpec.Id id) {
@@ -131,4 +126,8 @@ public class LoadBalancerProvisionerTest {
return allNodes;
}
+ private static <T> T get(Set<T> set, int position) {
+ return Iterators.get(set.iterator(), position, null);
+ }
+
}