diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2016-06-15 23:09:44 +0200 |
commit | 72231250ed81e10d66bfe70701e64fa5fe50f712 (patch) | |
tree | 2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /clustercontroller-apps/src/main |
Publish
Diffstat (limited to 'clustercontroller-apps/src/main')
7 files changed, 331 insertions, 0 deletions
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 { +} |