summaryrefslogtreecommitdiffstats
path: root/clustercontroller-apps
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /clustercontroller-apps
Publish
Diffstat (limited to 'clustercontroller-apps')
-rw-r--r--clustercontroller-apps/.gitignore2
-rw-r--r--clustercontroller-apps/OWNERS2
-rw-r--r--clustercontroller-apps/pom.xml83
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java107
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java100
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/DummyZooKeeperProvider.java11
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StandaloneZooKeeperProvider.java15
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2Handler.java65
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandler.java22
-rw-r--r--clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ZooKeeperProvider.java11
-rw-r--r--clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurerTest.java77
-rw-r--r--clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerTest.java57
-rw-r--r--clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2HandlerTest.java51
-rw-r--r--clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandlerTest.java19
14 files changed, 622 insertions, 0 deletions
diff --git a/clustercontroller-apps/.gitignore b/clustercontroller-apps/.gitignore
new file mode 100644
index 00000000000..3cc25b51fc4
--- /dev/null
+++ b/clustercontroller-apps/.gitignore
@@ -0,0 +1,2 @@
+/pom.xml.build
+/target
diff --git a/clustercontroller-apps/OWNERS b/clustercontroller-apps/OWNERS
new file mode 100644
index 00000000000..b3db17e22d8
--- /dev/null
+++ b/clustercontroller-apps/OWNERS
@@ -0,0 +1,2 @@
+vekterli
+hakon
diff --git a/clustercontroller-apps/pom.xml b/clustercontroller-apps/pom.xml
new file mode 100644
index 00000000000..b8103cd0210
--- /dev/null
+++ b/clustercontroller-apps/pom.xml
@@ -0,0 +1,83 @@
+<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>parent</artifactId>
+ <version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+ <artifactId>clustercontroller-apps</artifactId>
+ <version>6-SNAPSHOT</version>
+ <packaging>container-plugin</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>container-dev</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr4-runtime</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>clustercontroller-apputil</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>clustercontroller-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>clustercontroller-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>zkfacade</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.yahoo.vespa</groupId>
+ <artifactId>bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <compilerArgs>
+ <arg>-Xlint:rawtypes</arg>
+ <arg>-Xlint:unchecked</arg>
+ <arg>-Xlint:deprecation</arg>
+ <arg>-Werror</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java
new file mode 100644
index 00000000000..2beb248d032
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterController.java
@@ -0,0 +1,107 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.vespa.clustercontroller.apputil.communication.http.JDiscMetricWrapper;
+import com.yahoo.vespa.clustercontroller.core.FleetController;
+import com.yahoo.vespa.clustercontroller.core.FleetControllerOptions;
+import com.yahoo.vespa.clustercontroller.core.RemoteClusterControllerTaskScheduler;
+import com.yahoo.vespa.clustercontroller.core.restapiv2.ClusterControllerStateRestAPI;
+import com.yahoo.vespa.clustercontroller.core.status.StatusHandler;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.zookeeper.ZooKeeperServer;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.logging.Logger;
+
+/**
+ * Wrapper around fleet controller state to be able to use it in container.
+ */
+public class ClusterController extends AbstractComponent
+ implements ClusterControllerStateRestAPI.FleetControllerResolver,
+ StatusHandler.ClusterStatusPageServerSet {
+
+ private static final Logger log = Logger.getLogger(ClusterController.class.getName());
+ private final JDiscMetricWrapper metricWrapper;
+ private final Map<String, FleetController> controllers = new TreeMap<>();
+ private final Map<String, StatusHandler.ContainerStatusPageServer> status = new TreeMap<>();
+
+ /**
+ * Dependency injection constructor for controller. {@link ZooKeeperProvider} argument given
+ * to ensure that zookeeper has started before we start polling it.
+ */
+ @Inject
+ public ClusterController(ZooKeeperProvider zooKeeperProvider) {
+ this();
+ }
+
+ ClusterController() {
+ metricWrapper = new JDiscMetricWrapper(null);
+ }
+
+
+ public void setOptions(String clusterName, FleetControllerOptions options, Metric metricImpl) throws Exception {
+ metricWrapper.updateMetricImplementation(metricImpl);
+ if (options.zooKeeperServerAddress != null && !"".equals(options.zooKeeperServerAddress)) {
+ // Wipe this path ... it's unclear why
+ String path = "/" + options.clusterName + options.fleetControllerIndex;
+ Curator curator = Curator.create(options.zooKeeperServerAddress);
+ if (curator.framework().checkExists().forPath(path) != null)
+ curator.framework().delete().deletingChildrenIfNeeded().forPath(path);
+ curator.framework().create().creatingParentsIfNeeded().forPath(path);
+ }
+ synchronized (controllers) {
+ FleetController controller = controllers.get(clusterName);
+
+ if (controller == null) {
+ StatusHandler.ContainerStatusPageServer statusPageServer = new StatusHandler.ContainerStatusPageServer();
+ controller = FleetController.createForContainer(options, statusPageServer, metricWrapper);
+ controllers.put(clusterName, controller);
+ status.put(clusterName, statusPageServer);
+ } else {
+ controller.updateOptions(options, 0);
+ }
+ }
+ }
+
+ @Override
+ public void deconstruct() {
+ synchronized (controllers) {
+ for (FleetController controller : controllers.values()) {
+ try{
+ shutdownController(controller);
+ } catch (Exception e) {
+ log.warning("Failed to shut down fleet controller: " + e.getMessage());
+ }
+ }
+ }
+ super.deconstruct();
+ }
+
+ @Override
+ public Map<String, RemoteClusterControllerTaskScheduler> getFleetControllers() {
+ Map<String, RemoteClusterControllerTaskScheduler> m = new LinkedHashMap<>();
+ synchronized (controllers) {
+ m.putAll(controllers);
+ }
+ return m;
+ }
+
+ @Override
+ public StatusHandler.ContainerStatusPageServer get(String cluster) {
+ return status.get(cluster);
+ }
+
+ @Override
+ public Map<String, StatusHandler.ContainerStatusPageServer> getAll() {
+ return status;
+ }
+
+ void shutdownController(FleetController controller) throws Exception {
+ controller.shutdown();
+ }
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java
new file mode 100644
index 00000000000..07b0a68520b
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurer.java
@@ -0,0 +1,100 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.jdisc.Metric;
+import com.yahoo.vdslib.distribution.Distribution;
+import com.yahoo.vdslib.state.NodeType;
+import com.yahoo.vespa.clustercontroller.core.FleetControllerOptions;
+import com.yahoo.vespa.config.content.FleetcontrollerConfig;
+import com.yahoo.cloud.config.SlobroksConfig;
+import com.yahoo.vespa.config.content.StorDistributionConfig;
+import com.yahoo.cloud.config.ZookeepersConfig;
+
+/**
+ * When the cluster controller is reconfigured, a new instance of this is created, which will propagate configured
+ * options to receivers such as the fleet controller.
+ */
+public class ClusterControllerClusterConfigurer {
+
+ private final FleetControllerOptions options = new FleetControllerOptions(null);
+
+ public ClusterControllerClusterConfigurer(ClusterController controller,
+ StorDistributionConfig distributionConfig,
+ FleetcontrollerConfig fleetcontrollerConfig,
+ SlobroksConfig slobroksConfig,
+ ZookeepersConfig zookeepersConfig,
+ Metric metricImpl) throws Exception
+ {
+ configure(distributionConfig);
+ configure(fleetcontrollerConfig);
+ configure(slobroksConfig);
+ configure(zookeepersConfig);
+ checkIfZooKeeperNeeded();
+ if (controller != null) {
+ controller.setOptions(options.clusterName, options, metricImpl);
+ }
+ }
+
+ public FleetControllerOptions getOptions() { return options; }
+
+ private void configure(StorDistributionConfig config) {
+ options.setStorageDistribution(new Distribution(config));
+ }
+
+ private void configure(FleetcontrollerConfig config) {
+ options.clusterName = config.cluster_name();
+ options.fleetControllerIndex = config.index();
+ options.fleetControllerCount = config.fleet_controller_count();
+ options.zooKeeperSessionTimeout = (int) (config.zookeeper_session_timeout() * 1000);
+ options.masterZooKeeperCooldownPeriod = (int) (config.master_zookeeper_cooldown_period() * 1000);
+ options.stateGatherCount = config.state_gather_count();
+ options.rpcPort = config.rpc_port();
+ options.httpPort = config.http_port();
+ options.maxTransitionTime.put(NodeType.STORAGE, config.storage_transition_time());
+ options.maxTransitionTime.put(NodeType.DISTRIBUTOR, config.distributor_transition_time());
+ options.maxInitProgressTime = config.init_progress_time();
+ options.statePollingFrequency = config.state_polling_frequency();
+ options.maxPrematureCrashes = config.max_premature_crashes();
+ options.stableStateTimePeriod = config.stable_state_time_period();
+ options.eventLogMaxSize = config.event_log_max_size();
+ options.eventNodeLogMaxSize = config.event_node_log_max_size();
+ options.minDistributorNodesUp = config.min_distributors_up_count();
+ options.minStorageNodesUp = config.min_storage_up_count();
+ options.minRatioOfDistributorNodesUp = config.min_distributor_up_ratio();
+ options.minRatioOfStorageNodesUp = config.min_storage_up_ratio();
+ options.cycleWaitTime = (int) (config.cycle_wait_time() * 1000);
+ options.minTimeBeforeFirstSystemStateBroadcast = (int) (config.min_time_before_first_system_state_broadcast() * 1000);
+ options.nodeStateRequestTimeoutMS = (int) (config.get_node_state_request_timeout() * 1000);
+ options.showLocalSystemStatesInEventLog = config.show_local_systemstates_in_event_log();
+ options.minTimeBetweenNewSystemStates = config.min_time_between_new_systemstates();
+ options.maxSlobrokDisconnectGracePeriod = (int) (config.max_slobrok_disconnect_grace_period() * 1000);
+ options.distributionBits = config.ideal_distribution_bits();
+ }
+
+ private void configure(SlobroksConfig config) {
+ String specs[] = new String[config.slobrok().size()];
+ for (int i = 0; i < config.slobrok().size(); i++) {
+ specs[i] = config.slobrok().get(i).connectionspec();
+ }
+ options.slobrokConnectionSpecs = specs;
+ }
+
+ private void configure(ZookeepersConfig config) {
+ options.zooKeeperServerAddress = config.zookeeperserverlist();
+ }
+
+ private void checkIfZooKeeperNeeded() {
+ // For legacy (testing, presumably) reasons, support running 1 instance
+ // without a ZK cluster. This is really a Horrible Thing(tm) since we
+ // violate cluster state versioning invariants when the controller is
+ // restarted.
+ if (options.zooKeeperServerAddress == null || "".equals(options.zooKeeperServerAddress)) {
+ if (options.fleetControllerCount > 1) {
+ throw new IllegalArgumentException(
+ "Must set zookeeper server with multiple fleetcontrollers");
+ } else {
+ options.zooKeeperServerAddress = null; // Force null
+ }
+ }
+ }
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/DummyZooKeeperProvider.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/DummyZooKeeperProvider.java
new file mode 100644
index 00000000000..7de4b9c9d99
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/DummyZooKeeperProvider.java
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+/**
+ * A dummy zookeeper provider when we do not run our own zookeeper instance.
+ *
+ * @author lulf
+ * @since 5.26
+ */
+public class DummyZooKeeperProvider implements ZooKeeperProvider {
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StandaloneZooKeeperProvider.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StandaloneZooKeeperProvider.java
new file mode 100644
index 00000000000..5ef2fb618e8
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StandaloneZooKeeperProvider.java
@@ -0,0 +1,15 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.vespa.zookeeper.ZooKeeperServer;
+
+/**
+ * ZooKeeper provider that ensures we are running our own instance of zookeeper.
+ *
+ * @author lulf
+ * @since 5.26
+ */
+public class StandaloneZooKeeperProvider implements ZooKeeperProvider {
+ public StandaloneZooKeeperProvider(ZooKeeperServer server) {
+ }
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2Handler.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2Handler.java
new file mode 100644
index 00000000000..ef6202b4d9e
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2Handler.java
@@ -0,0 +1,65 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.google.inject.Inject;
+import com.yahoo.cloud.config.ClusterInfoConfig;
+import com.yahoo.cloud.config.ModelConfig;
+import com.yahoo.container.jdisc.config.HttpServerConfig;
+import com.yahoo.container.logging.AccessLog;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.clustercontroller.apputil.communication.http.JDiscHttpRequestHandler;
+import com.yahoo.vespa.clustercontroller.core.restapiv2.ClusterControllerStateRestAPI;
+import com.yahoo.vespa.clustercontroller.utils.staterestapi.server.RestApiHandler;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+public class StateRestApiV2Handler extends JDiscHttpRequestHandler {
+ private static final Logger log = Logger.getLogger(StateRestApiV2Handler.class.getName());
+
+ @Inject
+ public StateRestApiV2Handler(Executor executor, ClusterController cc, ClusterInfoConfig config, AccessLog accessLog) {
+ this(executor, new ClusterControllerStateRestAPI(cc, getClusterControllerSockets(config)), "/cluster/v2", accessLog);
+ }
+
+ private StateRestApiV2Handler(Executor executor, ClusterControllerStateRestAPI restApi, String pathPrefix, AccessLog accessLog) {
+ super(new RestApiHandler(restApi).setDefaultPathPrefix(pathPrefix), executor, accessLog);
+ }
+
+ // This method is package-private instead of private to be accessible to unit-tests.
+ static Map<Integer, ClusterControllerStateRestAPI.Socket> getClusterControllerSockets(ClusterInfoConfig config) {
+ Map<Integer, ClusterControllerStateRestAPI.Socket> result = new TreeMap<>();
+ for (ClusterInfoConfig.Services service : config.services()) {
+ for (ClusterInfoConfig.Services.Ports port : service.ports()) {
+ Set<String> tags = parseTags(port.tags());
+ if (tags.contains("http") && tags.contains("state")) {
+ result.put(service.index(), new ClusterControllerStateRestAPI.Socket(service.hostname(), port.number()));
+ break;
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ log.warning("Found no cluster controller in model config");
+ } else if (log.isLoggable(LogLevel.DEBUG)) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Found ").append(result.size()).append(" cluster controllers in model config:");
+ for (Map.Entry<Integer, ClusterControllerStateRestAPI.Socket> e : result.entrySet()) {
+ sb.append("\n ").append(e.getKey()).append(" -> ").append(e.getValue());
+ }
+ log.fine(sb.toString());
+ }
+ return result;
+ }
+
+ private static Set<String> parseTags(String tags) {
+ Set<String> set = new HashSet<>();
+ for(String s : tags.toLowerCase().split(" ")) {
+ set.add(s.trim());
+ }
+ return set;
+ }
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandler.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandler.java
new file mode 100644
index 00000000000..a03042e9675
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandler.java
@@ -0,0 +1,22 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.google.inject.Inject;
+import com.yahoo.container.logging.AccessLog;
+import com.yahoo.vespa.clustercontroller.apputil.communication.http.JDiscHttpRequestHandler;
+
+import java.util.concurrent.Executor;
+
+public class StatusHandler extends JDiscHttpRequestHandler {
+ private final com.yahoo.vespa.clustercontroller.core.status.StatusHandler statusHandler;
+
+ @Inject
+ public StatusHandler(ClusterController fc, Executor executor, AccessLog accessLog) {
+ this(new com.yahoo.vespa.clustercontroller.core.status.StatusHandler(fc), executor, accessLog);
+ }
+
+ private StatusHandler(com.yahoo.vespa.clustercontroller.core.status.StatusHandler handler, Executor executor, AccessLog accessLog) {
+ super(handler, executor, accessLog);
+ this.statusHandler = handler;
+ }
+}
diff --git a/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ZooKeeperProvider.java b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ZooKeeperProvider.java
new file mode 100644
index 00000000000..169ad1a1246
--- /dev/null
+++ b/clustercontroller-apps/src/main/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ZooKeeperProvider.java
@@ -0,0 +1,11 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+/**
+ * Abstraction we can depend on providing us with a zookeeper server being up.
+ *
+ * @author lulf
+ * @since 5.25
+ */
+public interface ZooKeeperProvider {
+}
diff --git a/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurerTest.java b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurerTest.java
new file mode 100644
index 00000000000..6cf4f962c9f
--- /dev/null
+++ b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerClusterConfigurerTest.java
@@ -0,0 +1,77 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.cloud.config.SlobroksConfig;
+import com.yahoo.cloud.config.ZookeepersConfig;
+import com.yahoo.jdisc.Metric;
+import com.yahoo.vespa.config.content.FleetcontrollerConfig;
+import com.yahoo.vespa.config.content.StorDistributionConfig;
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+public class ClusterControllerClusterConfigurerTest extends TestCase {
+
+ public void testSimple() throws Exception {
+ ClusterController controller = new ClusterController();
+ StorDistributionConfig.Builder distributionConfig = new StorDistributionConfig.Builder();
+ StorDistributionConfig.Group.Builder group = new StorDistributionConfig.Group.Builder();
+ group.index("0").name("foo");
+ StorDistributionConfig.Group.Nodes.Builder node = new StorDistributionConfig.Group.Nodes.Builder();
+ node.index(0);
+ group.nodes.add(node);
+ distributionConfig.group.add(group);
+ FleetcontrollerConfig.Builder fleetcontrollerConfig = new FleetcontrollerConfig.Builder();
+ fleetcontrollerConfig.cluster_name("storage").index(0).zookeeper_server("zoo");
+ SlobroksConfig.Builder slobroksConfig = new SlobroksConfig.Builder();
+ SlobroksConfig.Slobrok.Builder slobrok = new SlobroksConfig.Slobrok.Builder();
+ slobrok.connectionspec("foo");
+ slobroksConfig.slobrok.add(slobrok);
+ ZookeepersConfig.Builder zookeepersConfig = new ZookeepersConfig.Builder();
+ zookeepersConfig.zookeeperserverlist("foo");
+ Metric metric = new Metric() {
+ @Override
+ public void set(String s, Number number, Context context) {}
+ @Override
+ public void add(String s, Number number, Context context) {}
+ @Override
+ public Context createContext(Map<String, ?> stringMap) { return null; }
+ };
+ // Used in standalone modus to get config without a cluster controller instance
+ ClusterControllerClusterConfigurer configurer = new ClusterControllerClusterConfigurer(
+ null,
+ new StorDistributionConfig(distributionConfig),
+ new FleetcontrollerConfig(fleetcontrollerConfig),
+ new SlobroksConfig(slobroksConfig),
+ new ZookeepersConfig(zookeepersConfig),
+ metric
+ );
+ assertTrue(configurer.getOptions() != null);
+
+ // Oki with no zookeeper if one node
+ zookeepersConfig.zookeeperserverlist("");
+ new ClusterControllerClusterConfigurer(
+ controller,
+ new StorDistributionConfig(distributionConfig),
+ new FleetcontrollerConfig(fleetcontrollerConfig),
+ new SlobroksConfig(slobroksConfig),
+ new ZookeepersConfig(zookeepersConfig),
+ metric
+ );
+
+ try{
+ fleetcontrollerConfig.fleet_controller_count(5);
+ new ClusterControllerClusterConfigurer(
+ controller,
+ new StorDistributionConfig(distributionConfig),
+ new FleetcontrollerConfig(fleetcontrollerConfig),
+ new SlobroksConfig(slobroksConfig),
+ new ZookeepersConfig(zookeepersConfig),
+ metric
+ );
+ fail("Should not get here");
+ } catch (Exception e) {
+ assertEquals("Must set zookeeper server with multiple fleetcontrollers", e.getMessage());
+ }
+ }
+}
diff --git a/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerTest.java b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerTest.java
new file mode 100644
index 00000000000..be5c7bf026e
--- /dev/null
+++ b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/ClusterControllerTest.java
@@ -0,0 +1,57 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * Doesn't really test cluster controller, but runs some lines of code.
+ * System tests verifies that container can load it..
+ */
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.jdisc.Metric;
+import com.yahoo.vespa.clustercontroller.core.FleetController;
+import com.yahoo.vespa.clustercontroller.core.FleetControllerOptions;
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+public class ClusterControllerTest extends TestCase {
+ private FleetControllerOptions options = new FleetControllerOptions("storage");
+ private Metric metric = new Metric() {
+ @Override
+ public void set(String s, Number number, Context context) {}
+ @Override
+ public void add(String s, Number number, Context context) {}
+ @Override
+ public Context createContext(Map<String, ?> stringMap) { return null; }
+ };
+
+ public void setUp() {
+ options = new FleetControllerOptions("storage");
+ options.zooKeeperServerAddress = null;
+ options.slobrokConfigId = "raw:";
+ options.slobrokConnectionSpecs = null;
+ }
+
+ public void testSimple() throws Exception {
+ // Cluster controller object keeps state and should never be remade, so should
+ // inject nothing
+ ClusterController cc = new ClusterController();
+ cc.setOptions("storage", options, metric);
+ cc.setOptions("storage", options, metric);
+ cc.getFleetControllers();
+ cc.getAll();
+
+ assertTrue(cc.get("storage") != null);
+ assertFalse(cc.get("music") != null);
+ cc.deconstruct();
+ }
+
+ public void testShutdownException() throws Exception {
+ ClusterController cc = new ClusterController() {
+ void shutdownController(FleetController controller) throws Exception {
+ throw new Exception("Foo");
+ }
+ };
+ cc.setOptions("storage", options, metric);
+ cc.deconstruct();
+ }
+
+}
diff --git a/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2HandlerTest.java b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2HandlerTest.java
new file mode 100644
index 00000000000..9230c08f735
--- /dev/null
+++ b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StateRestApiV2HandlerTest.java
@@ -0,0 +1,51 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.cloud.config.ClusterInfoConfig;
+import com.yahoo.container.logging.AccessLog;
+import com.yahoo.vespa.clustercontroller.core.restapiv2.ClusterControllerStateRestAPI;
+import junit.framework.TestCase;
+
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class StateRestApiV2HandlerTest extends TestCase {
+
+ public void testNoMatchingSockets() {
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000));
+ ClusterController controller = new ClusterController();
+ ClusterInfoConfig config = new ClusterInfoConfig(
+ new ClusterInfoConfig.Builder().clusterId("cluster-id").nodeCount(1));
+ ClusterInfoConfig.Builder clusterConfig = new ClusterInfoConfig.Builder();
+ new StateRestApiV2Handler(executor, controller, config, AccessLog.voidAccessLog());
+ executor.shutdown();
+ }
+
+ public void testMappingOfIndexToClusterControllers() {
+ ClusterInfoConfig.Builder builder = new ClusterInfoConfig.Builder()
+ .clusterId("cluster-id")
+ .nodeCount(1)
+ .services(new ClusterInfoConfig.Services.Builder()
+ .index(1)
+ .hostname("host-1")
+ .ports(new ClusterInfoConfig.Services.Ports.Builder().number(80).tags("state http"))
+ .ports(new ClusterInfoConfig.Services.Ports.Builder().number(81).tags("ignored port http")))
+ .services(new ClusterInfoConfig.Services.Builder()
+ .index(3)
+ .hostname("host-3")
+ .ports(new ClusterInfoConfig.Services.Ports.Builder().number(85).tags("state http"))
+ .ports(new ClusterInfoConfig.Services.Ports.Builder().number(86).tags("foo http bar state")));
+
+ ClusterInfoConfig config = new ClusterInfoConfig(builder);
+ Map<Integer, ClusterControllerStateRestAPI.Socket> mapping = StateRestApiV2Handler.getClusterControllerSockets(config);
+ Map<Integer, ClusterControllerStateRestAPI.Socket> expected = new TreeMap<>();
+
+ expected.put(1, new ClusterControllerStateRestAPI.Socket("host-1", 80));
+ expected.put(3, new ClusterControllerStateRestAPI.Socket("host-3", 85));
+
+ assertEquals(expected, mapping);
+ }
+}
diff --git a/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandlerTest.java b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandlerTest.java
new file mode 100644
index 00000000000..e066e0b367e
--- /dev/null
+++ b/clustercontroller-apps/src/test/java/com/yahoo/vespa/clustercontroller/apps/clustercontroller/StatusHandlerTest.java
@@ -0,0 +1,19 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.clustercontroller.apps.clustercontroller;
+
+import com.yahoo.container.logging.AccessLog;
+import junit.framework.TestCase;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class StatusHandlerTest extends TestCase {
+
+ public void testSimple() {
+ ClusterController controller = new ClusterController();
+ ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000));
+ StatusHandler handler = new StatusHandler(controller, executor, AccessLog.voidAccessLog());
+ executor.shutdown();
+ }
+}