diff options
author | Valerij Fredriksen <freva@users.noreply.github.com> | 2017-09-22 10:59:43 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-22 10:59:43 +0200 |
commit | 4f4d5116f6c4ef7841838ba1b2cb021ed7b62d09 (patch) | |
tree | 87d8b35d749f215c73e35b2b035cdfadb481e1b0 | |
parent | 0583c695cd3deac590e6bd47e735c09bba0ec18d (diff) | |
parent | a8afd010fbc4ab3da20b99976b0e926fd379b0f1 (diff) |
Merge pull request #3464 from vespa-engine/freva/node-admin-shutdown
Freva/node admin shutdown
29 files changed, 322 insertions, 315 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java index 32a9158dbcf..40e997a0d81 100755 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java @@ -217,6 +217,7 @@ public final class ContainerCluster addSimpleComponent(AccessLog.class); // TODO better modelling addSimpleComponent(ThreadPoolProvider.class); + addSimpleComponent(com.yahoo.concurrent.lock.Locking.class); addSimpleComponent("com.yahoo.jdisc.http.filter.SecurityFilterInvoker"); addSimpleComponent(SIMPLE_LINGUISTICS_PROVIDER); addSimpleComponent("com.yahoo.container.jdisc.SslKeyStoreFactoryProvider"); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java index a4118ebe9ff..f6fd8c3bd18 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/docker/DockerOperationsImpl.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.regex.Pattern; import java.util.stream.Stream; import static com.yahoo.vespa.defaults.Defaults.getDefaults; @@ -40,8 +39,6 @@ public class DockerOperationsImpl implements DockerOperations { private static final String[] RESTART_VESPA_ON_NODE_COMMAND = new String[]{NODE_PROGRAM, "restart-vespa"}; private static final String[] STOP_NODE_COMMAND = new String[]{NODE_PROGRAM, "stop"}; - private static final Pattern VESPA_VERSION_PATTERN = Pattern.compile("^(\\S*)$", Pattern.MULTILINE); - private static final String MANAGER_NAME = "node-admin"; // Map of directories to mount and whether they should be writable by everyone diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdmin.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdmin.java index e02f81e8f30..cf70963eee1 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdmin.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdmin.java @@ -62,5 +62,5 @@ public interface NodeAdmin { /** * Stop the NodeAgent. Will not delete the storage or stop the container. */ - void shutdown(); + void stop(); } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java index e39abb47788..f227a166034 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImpl.java @@ -55,8 +55,6 @@ public class NodeAdminImpl implements NodeAdmin { private final Map<ContainerName, NodeAgent> nodeAgents = new ConcurrentHashMap<>(); - private final int nodeAgentScanIntervalMillis; - private final GaugeWrapper numberOfContainersInLoadImageState; private final CounterWrapper numberOfUnhandledExceptionsInNodeAgent; @@ -64,13 +62,11 @@ public class NodeAdminImpl implements NodeAdmin { final Function<String, NodeAgent> nodeAgentFactory, final StorageMaintainer storageMaintainer, final AclMaintainer aclMaintainer, - final int nodeAgentScanIntervalMillis, final MetricReceiverWrapper metricReceiver, final Clock clock) { this.dockerOperations = dockerOperations; this.nodeAgentFactory = nodeAgentFactory; this.storageMaintainer = storageMaintainer; - this.nodeAgentScanIntervalMillis = nodeAgentScanIntervalMillis; this.clock = clock; this.previousWantFrozen = true; @@ -183,24 +179,21 @@ public class NodeAdminImpl implements NodeAdmin { } @Override - public void shutdown() { + public void stop() { metricsScheduler.shutdown(); aclScheduler.shutdown(); - try { - boolean metricsSchedulerShutdown = metricsScheduler.awaitTermination(30, TimeUnit.SECONDS); - boolean aclSchedulerShutdown = aclScheduler.awaitTermination(30, TimeUnit.SECONDS); - if (! (metricsSchedulerShutdown && aclSchedulerShutdown)) { - throw new RuntimeException("Failed shutting down all scheduler(s), shutdown status:\n" + - "\tMetrics Scheduler: " + metricsSchedulerShutdown + "\n" + - "\tACL Scheduler: " + aclSchedulerShutdown); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - for (NodeAgent nodeAgent : nodeAgents.values()) { - nodeAgent.stop(); - } + // Stop all node-agents in parallel, will block until the last NodeAgent is stopped + nodeAgents.values().parallelStream().forEach(NodeAgent::stop); + + do { + try { + metricsScheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + aclScheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } catch (InterruptedException e) { + logger.info("Was interrupted while waiting for metricsScheduler and aclScheduler to shutdown"); + } + } while (!metricsScheduler.isTerminated() || !aclScheduler.isTerminated()); } // Set-difference. Returns minuend minus subtrahend. @@ -257,7 +250,7 @@ public class NodeAdminImpl implements NodeAdmin { } final NodeAgent agent = nodeAgentFactory.apply(hostname); - agent.start(nodeAgentScanIntervalMillis); + agent.start(); nodeAgents.put(containerName, agent); try { Thread.sleep(1000); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java index a848dae9388..d1f23b13e6c 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdater.java @@ -1,7 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.nodeadmin; -import com.yahoo.component.AbstractComponent; import com.yahoo.concurrent.ThreadFactoryFactory; import com.yahoo.log.LogLevel; import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec; @@ -36,7 +35,7 @@ import static com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater. * * @author dybis, stiankri */ -public class NodeAdminStateUpdater extends AbstractComponent { +public class NodeAdminStateUpdater { static final Duration FREEZE_CONVERGENCE_TIMEOUT = Duration.ofMinutes(5); private final AtomicBoolean terminated = new AtomicBoolean(false); @@ -52,35 +51,31 @@ public class NodeAdminStateUpdater extends AbstractComponent { private Thread loopThread; private final NodeRepository nodeRepository; + private final Orchestrator orchestrator; + private final StorageMaintainer storageMaintainer; private final NodeAdmin nodeAdmin; private final Clock clock; - private final Orchestrator orchestrator; private final String dockerHostHostName; + private final Duration nodeAdminConvergeStateInterval; - private long delaysBetweenEachTickMillis = 30_000; private Instant lastTick; public NodeAdminStateUpdater( - final NodeRepository nodeRepository, - final NodeAdmin nodeAdmin, + NodeRepository nodeRepository, + Orchestrator orchestrator, StorageMaintainer storageMaintainer, + NodeAdmin nodeAdmin, + String dockerHostHostName, Clock clock, - Orchestrator orchestrator, - String dockerHostHostName) { - log.log(LogLevel.INFO, objectToString() + ": Creating object"); + Duration nodeAdminConvergeStateInterval) { this.nodeRepository = nodeRepository; - this.nodeAdmin = nodeAdmin; - this.clock = clock; this.orchestrator = orchestrator; + this.storageMaintainer = storageMaintainer; + this.nodeAdmin = nodeAdmin; this.dockerHostHostName = dockerHostHostName; + this.clock = clock; + this.nodeAdminConvergeStateInterval = nodeAdminConvergeStateInterval; this.lastTick = clock.instant(); - - specVerifierScheduler.scheduleWithFixedDelay(() -> - updateHardwareDivergence(storageMaintainer), 5, 60, TimeUnit.MINUTES); - } - - private String objectToString() { - return this.getClass().getSimpleName() + "@" + Integer.toString(System.identityHashCode(this)); } public enum State { RESUMED, SUSPENDED_NODE_ADMIN, SUSPENDED} @@ -133,7 +128,8 @@ public class NodeAdminStateUpdater extends AbstractComponent { State wantedStateCopy; synchronized (monitor) { while (! workToDoNow) { - long remainder = delaysBetweenEachTickMillis - Duration.between(lastTick, clock.instant()).toMillis(); + Duration timeSinceLastConverge = Duration.between(lastTick, clock.instant()); + long remainder = nodeAdminConvergeStateInterval.minus(timeSinceLastConverge).toMillis(); if (remainder > 0) { try { monitor.wait(remainder); @@ -231,7 +227,7 @@ public class NodeAdminStateUpdater extends AbstractComponent { } final List<ContainerNodeSpec> containersToRun; try { - containersToRun = nodeRepository.getContainersToRun(); + containersToRun = nodeRepository.getContainersToRun(dockerHostHostName); } catch (Exception e) { log.log(LogLevel.WARNING, "Failed fetching container info from node repository", e); return; @@ -250,7 +246,7 @@ public class NodeAdminStateUpdater extends AbstractComponent { private List<String> getNodesInActiveState() { try { - return nodeRepository.getContainersToRun() + return nodeRepository.getContainersToRun(dockerHostHostName) .stream() .filter(nodespec -> nodespec.nodeState == Node.State.active) .map(nodespec -> nodespec.hostname) @@ -260,8 +256,7 @@ public class NodeAdminStateUpdater extends AbstractComponent { } } - public void start(long stateConvergeInterval) { - delaysBetweenEachTickMillis = stateConvergeInterval; + public void start() { if (loopThread != null) { throw new RuntimeException("Can not restart NodeAdminStateUpdater"); } @@ -271,24 +266,30 @@ public class NodeAdminStateUpdater extends AbstractComponent { }); loopThread.setName("tick-NodeAdminStateUpdater"); loopThread.start(); + + specVerifierScheduler.scheduleWithFixedDelay(() -> + updateHardwareDivergence(storageMaintainer), 5, 60, TimeUnit.MINUTES); } - @Override - public void deconstruct() { + public void stop() { + specVerifierScheduler.shutdown(); if (!terminated.compareAndSet(false, true)) { throw new RuntimeException("Can not re-stop a node agent."); } - log.log(LogLevel.INFO, objectToString() + ": Deconstruct called"); + + // First we need to stop NodeAdminStateUpdater thread to make sure no new NodeAgents are spawned signalWorkToBeDone(); - try { - loopThread.join(10000); - if (loopThread.isAlive()) { - log.log(LogLevel.ERROR, "Could not stop tick thread"); + + do { + try { + loopThread.join(); + specVerifierScheduler.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } catch (InterruptedException e1) { + log.info("Interrupted while waiting for NodeAdminStateUpdater thread and specVerfierScheduler to shutdown"); } - } catch (InterruptedException e1) { - log.log(LogLevel.ERROR, "Interrupted; Could not stop thread"); - } - nodeAdmin.shutdown(); - log.log(LogLevel.INFO, objectToString() + ": Deconstruct complete"); + } while (loopThread.isAlive() || !specVerifierScheduler.isTerminated()); + + // Finally, stop NodeAdmin and all the NodeAgents + nodeAdmin.stop(); } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgent.java index 5d31c10fcc1..92c44969d5e 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgent.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgent.java @@ -29,7 +29,7 @@ public interface NodeAgent { * Starts the agent. After this method is called, the agent will asynchronously maintain the node, continuously * striving to make the current state equal to the wanted state. */ - void start(int intervalMillis); + void start(); /** * Signals to the agent that the node is at the end of its lifecycle and no longer needs a managing agent. diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java index 6ea65be6799..77348a9dc45 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImpl.java @@ -64,20 +64,20 @@ public class NodeAgentImpl implements NodeAgent { private final PrefixLogger logger; private DockerImage imageBeingDownloaded = null; - private final String hostname; private final ContainerName containerName; + private final String hostname; private final NodeRepository nodeRepository; private final Orchestrator orchestrator; private final DockerOperations dockerOperations; private final StorageMaintainer storageMaintainer; + private final AclMaintainer aclMaintainer; private final Environment environment; private final Clock clock; - private final AclMaintainer aclMaintainer; + private final Duration timeBetweenEachConverge; private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private final LinkedList<String> debugMessages = new LinkedList<>(); - private long delaysBetweenEachConvergeMillis = 30_000; private int numberOfUnhandledException = 0; private Instant lastConverge; @@ -85,7 +85,7 @@ public class NodeAgentImpl implements NodeAgent { private final ScheduledExecutorService filebeatRestarter = Executors.newScheduledThreadPool(1, ThreadFactoryFactory.getDaemonThreadFactory("filebeatrestarter")); - private final Consumer<String> serviceRestarter; + private Consumer<String> serviceRestarter; private Future<?> currentFilebeatRestarter; private boolean resumeScriptRun = false; @@ -117,7 +117,8 @@ public class NodeAgentImpl implements NodeAgent { final StorageMaintainer storageMaintainer, final AclMaintainer aclMaintainer, final Environment environment, - final Clock clock) { + final Clock clock, + final Duration timeBetweenEachConverge) { this.containerName = ContainerName.fromHostname(hostName); this.logger = PrefixLogger.getNodeAgentLogger(NodeAgentImpl.class, containerName); this.hostname = hostName; @@ -128,19 +129,8 @@ public class NodeAgentImpl implements NodeAgent { this.aclMaintainer = aclMaintainer; this.environment = environment; this.clock = clock; + this.timeBetweenEachConverge = timeBetweenEachConverge; this.lastConverge = clock.instant(); - this.serviceRestarter = service -> { - try { - ProcessResult processResult = dockerOperations.executeCommandInContainerAsRoot( - containerName, "service", service, "restart"); - - if (!processResult.isSuccess()) { - logger.error("Failed to restart service " + service + ": " + processResult); - } - } catch (Exception e) { - logger.error("Failed to restart service " + service, e); - } - }; } @Override @@ -183,11 +173,11 @@ public class NodeAgentImpl implements NodeAgent { } @Override - public void start(int intervalMillis) { - String message = "Starting with interval " + intervalMillis + " ms"; + public void start() { + String message = "Starting with interval " + timeBetweenEachConverge.toMillis() + " ms"; logger.info(message); addDebugMessage(message); - delaysBetweenEachConvergeMillis = intervalMillis; + if (loopThread != null) { throw new RuntimeException("Can not restart a node agent."); } @@ -197,6 +187,19 @@ public class NodeAgentImpl implements NodeAgent { }); loopThread.setName("tick-" + hostname); loopThread.start(); + + serviceRestarter = service -> { + try { + ProcessResult processResult = dockerOperations.executeCommandInContainerAsRoot( + containerName, "service", service, "restart"); + + if (!processResult.isSuccess()) { + logger.error("Failed to restart service " + service + ": " + processResult); + } + } catch (Exception e) { + logger.error("Failed to restart service " + service, e); + } + }; } @Override @@ -207,19 +210,15 @@ public class NodeAgentImpl implements NodeAgent { throw new RuntimeException("Can not re-stop a node agent."); } signalWorkToBeDone(); - try { - loopThread.join(10000); - if (loopThread.isAlive()) { - logger.error("Could not stop host thread " + hostname); + + do { + try { + loopThread.join(); + filebeatRestarter.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } catch (InterruptedException e) { + logger.error("Interrupted while waiting for converge thread and filebeatRestarter scheduler to shutdown"); } - } catch (InterruptedException e1) { - logger.error("Interrupted; Could not stop host thread " + hostname); - } - try { - filebeatRestarter.awaitTermination(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - logger.error("Interrupted; Could not stop filebeatrestarter thread"); - } + } while (loopThread.isAlive() || !filebeatRestarter.isTerminated()); logger.info("Stopped"); } @@ -375,7 +374,7 @@ public class NodeAgentImpl implements NodeAgent { boolean isFrozenCopy; synchronized (monitor) { while (!workToDoNow) { - long remainder = delaysBetweenEachConvergeMillis - Duration.between(lastConverge, clock.instant()).toMillis(); + long remainder = timeBetweenEachConverge.minus(Duration.between(lastConverge, clock.instant())).toMillis(); if (remainder > 0) { try { monitor.wait(remainder); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java index d14cd2f1330..9f4c6916b48 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepository.java @@ -13,7 +13,7 @@ import java.util.Optional; * @author stiankri */ public interface NodeRepository { - List<ContainerNodeSpec> getContainersToRun() throws IOException; + List<ContainerNodeSpec> getContainersToRun(String baseHostName) throws IOException; Optional<ContainerNodeSpec> getContainerNodeSpec(String hostName); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java index 7d73d05ca36..08957a489b6 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImpl.java @@ -28,18 +28,17 @@ import java.util.stream.Collectors; */ public class NodeRepositoryImpl implements NodeRepository { private static final PrefixLogger NODE_ADMIN_LOGGER = PrefixLogger.getNodeAdminLogger(NodeRepositoryImpl.class); - private final String baseHostName; - private final int port; + private final ConfigServerHttpRequestExecutor requestExecutor; + private final int port; - public NodeRepositoryImpl(ConfigServerHttpRequestExecutor requestExecutor, int configPort, String baseHostName) { - this.baseHostName = baseHostName; - this.port = configPort; + public NodeRepositoryImpl(ConfigServerHttpRequestExecutor requestExecutor, int port) { this.requestExecutor = requestExecutor; + this.port = port; } @Override - public List<ContainerNodeSpec> getContainersToRun() throws IOException { + public List<ContainerNodeSpec> getContainersToRun(String baseHostName) throws IOException { try { final GetNodesResponse nodesForHost = requestExecutor.get( "/nodes/v2/node/?parentHost=" + baseHostName + "&recursive=true", diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java index 5117a1bb079..bd9df486e7b 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImpl.java @@ -1,8 +1,6 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.orchestrator; -import com.yahoo.vespa.defaults.Defaults; - import com.yahoo.vespa.hosted.node.admin.util.ConfigServerHttpRequestExecutor; import com.yahoo.vespa.orchestrator.restapi.HostApi; @@ -19,7 +17,6 @@ import java.util.Optional; * @author dybis */ public class OrchestratorImpl implements Orchestrator { - static final int WEB_SERVICE_PORT = Defaults.getDefaults().vespaWebServicePort(); // TODO: Find a way to avoid duplicating this (present in orchestrator's services.xml also). private static final String ORCHESTRATOR_PATH_PREFIX = "/orchestrator"; static final String ORCHESTRATOR_PATH_PREFIX_HOST_API @@ -28,9 +25,11 @@ public class OrchestratorImpl implements Orchestrator { = ORCHESTRATOR_PATH_PREFIX + HostSuspensionApi.PATH_PREFIX; private final ConfigServerHttpRequestExecutor requestExecutor; + private final int port; - public OrchestratorImpl(ConfigServerHttpRequestExecutor requestExecutor) { + public OrchestratorImpl(ConfigServerHttpRequestExecutor requestExecutor, int port) { this.requestExecutor = requestExecutor; + this.port = port; } @Override @@ -38,7 +37,7 @@ public class OrchestratorImpl implements Orchestrator { UpdateHostResponse response; try { response = requestExecutor.put(getSuspendPath(hostName), - WEB_SERVICE_PORT, + port, Optional.empty(), /* body */ UpdateHostResponse.class); } catch (ConfigServerHttpRequestExecutor.NotFoundException n) { @@ -58,7 +57,7 @@ public class OrchestratorImpl implements Orchestrator { try { batchOperationResult = requestExecutor.put( ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API, - WEB_SERVICE_PORT, + port, Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)), BatchOperationResult.class); } catch (Exception e) { @@ -75,7 +74,7 @@ public class OrchestratorImpl implements Orchestrator { UpdateHostResponse response; try { String path = getSuspendPath(hostName); - response = requestExecutor.delete(path, WEB_SERVICE_PORT, UpdateHostResponse.class); + response = requestExecutor.delete(path, port, UpdateHostResponse.class); } catch (ConfigServerHttpRequestExecutor.NotFoundException n) { throw new OrchestratorNotFoundException("Failed to resume " + hostName + ", host not found"); } catch (Exception e) { diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProvider.java deleted file mode 100644 index 3211e4feb56..00000000000 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.node.admin.provider; - -import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; -import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater; - -/** - * Class for setting up instances of classes; enables testing. - * - * @author dybis - */ -public interface ComponentsProvider { - NodeAdminStateUpdater getNodeAdminStateUpdater(); - - MetricReceiverWrapper getMetricReceiverWrapper(); -} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java deleted file mode 100644 index 98d7593ef69..00000000000 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/ComponentsProviderImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.node.admin.provider; - -import com.google.inject.Inject; -import com.yahoo.net.HostName; -import static com.yahoo.vespa.defaults.Defaults.getDefaults; - -import com.yahoo.system.ProcessExecuter; -import com.yahoo.vespa.hosted.dockerapi.ContainerName; -import com.yahoo.vespa.hosted.dockerapi.Docker; -import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; -import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations; -import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer; -import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer; -import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin; -import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl; -import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater; -import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent; -import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl; -import com.yahoo.vespa.hosted.node.admin.docker.DockerOperationsImpl; -import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository; -import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepositoryImpl; -import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator; -import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorImpl; -import com.yahoo.vespa.hosted.node.admin.util.ConfigServerHttpRequestExecutor; -import com.yahoo.vespa.hosted.node.admin.util.Environment; -import com.yahoo.vespa.hosted.node.admin.util.SecretAgentScheduleMaker; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Clock; -import java.util.Set; -import java.util.function.Function; - -/** - * Set up node admin for production. - * - * @author dybis - */ -public class ComponentsProviderImpl implements ComponentsProvider { - private static final ContainerName NODE_ADMIN_CONTAINER_NAME = new ContainerName("node-admin"); - - private final NodeAdminStateUpdater nodeAdminStateUpdater; - private final MetricReceiverWrapper metricReceiverWrapper; - - private static final int NODE_AGENT_SCAN_INTERVAL_MILLIS = 30000; - private static final int WEB_SERVICE_PORT = getDefaults().vespaWebServicePort(); - - // Converge towards desired node admin state every 30 seconds - private static final int NODE_ADMIN_CONVERGE_STATE_INTERVAL_MILLIS = 30000; - - @Inject - public ComponentsProviderImpl(Docker docker, MetricReceiverWrapper metricReceiver) { - String baseHostName = HostName.getLocalhost(); - Environment environment = new Environment(); - Set<String> configServerHosts = environment.getConfigServerHosts(); - if (configServerHosts.isEmpty()) { - throw new IllegalStateException("Environment setting for config servers missing or empty."); - } - - Clock clock = Clock.systemUTC(); - ProcessExecuter processExecuter = new ProcessExecuter(); - ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(configServerHosts); - Orchestrator orchestrator = new OrchestratorImpl(requestExecutor); - NodeRepository nodeRepository = new NodeRepositoryImpl(requestExecutor, WEB_SERVICE_PORT, baseHostName); - DockerOperations dockerOperations = new DockerOperationsImpl(docker, environment, processExecuter); - - StorageMaintainer storageMaintainer = new StorageMaintainer(docker, processExecuter, metricReceiver, environment, clock); - AclMaintainer aclMaintainer = new AclMaintainer(dockerOperations, nodeRepository, baseHostName); - - Function<String, NodeAgent> nodeAgentFactory = - (hostName) -> new NodeAgentImpl(hostName, nodeRepository, orchestrator, dockerOperations, - storageMaintainer, aclMaintainer, environment, clock); - NodeAdmin nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, - NODE_AGENT_SCAN_INTERVAL_MILLIS, metricReceiver, clock); - nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepository, nodeAdmin, storageMaintainer, clock, orchestrator, baseHostName); - nodeAdminStateUpdater.start(NODE_ADMIN_CONVERGE_STATE_INTERVAL_MILLIS); - - metricReceiverWrapper = metricReceiver; - - setCorePattern(docker); - initializeNodeAgentSecretAgent(docker); - } - - @Override - public NodeAdminStateUpdater getNodeAdminStateUpdater() { - return nodeAdminStateUpdater; - } - - @Override - public MetricReceiverWrapper getMetricReceiverWrapper() { - return metricReceiverWrapper; - } - - - private void setCorePattern(Docker docker) { - final String[] sysctlCorePattern = {"sysctl", "-w", "kernel.core_pattern=" + - getDefaults().underVespaHome("var/crash/%e.core.%p")}; - docker.executeInContainerAsRoot(NODE_ADMIN_CONTAINER_NAME, sysctlCorePattern); - } - - private void initializeNodeAgentSecretAgent(Docker docker) { - final Path yamasAgentFolder = Paths.get("/etc/yamas-agent/"); - docker.executeInContainerAsRoot(NODE_ADMIN_CONTAINER_NAME, "chmod", "a+w", yamasAgentFolder.toString()); - - Path nodeAdminCheckPath = Paths.get("/usr/bin/curl"); - SecretAgentScheduleMaker scheduleMaker = new SecretAgentScheduleMaker("node-admin", 60, nodeAdminCheckPath, - "localhost:4080/rest/metrics"); - - try { - scheduleMaker.writeTo(yamasAgentFolder); - docker.executeInContainerAsRoot(NODE_ADMIN_CONTAINER_NAME, "service", "yamas-agent", "restart"); - } catch (IOException e) { - throw new RuntimeException("Failed to write secret-agent schedules for node-admin", e); - } - } -} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java new file mode 100644 index 00000000000..5536ee1551b --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/provider/NodeAdminProvider.java @@ -0,0 +1,105 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.provider; + +import com.google.inject.Inject; +import com.yahoo.concurrent.lock.Lock; +import com.yahoo.concurrent.lock.Locking; +import com.yahoo.container.di.componentgraph.Provider; +import com.yahoo.log.LogLevel; +import com.yahoo.net.HostName; + +import com.yahoo.system.ProcessExecuter; +import com.yahoo.vespa.hosted.dockerapi.Docker; +import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; +import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations; +import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer; +import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer; +import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdmin; +import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminImpl; +import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater; +import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent; +import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl; +import com.yahoo.vespa.hosted.node.admin.docker.DockerOperationsImpl; +import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository; +import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepositoryImpl; +import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator; +import com.yahoo.vespa.hosted.node.admin.orchestrator.OrchestratorImpl; +import com.yahoo.vespa.hosted.node.admin.util.ConfigServerHttpRequestExecutor; +import com.yahoo.vespa.hosted.node.admin.util.Environment; + +import java.time.Clock; +import java.time.Duration; +import java.util.function.Function; +import java.util.logging.Logger; + +import static com.yahoo.vespa.defaults.Defaults.getDefaults; + +/** + * Set up node admin for production. + * + * @author dybis + */ +public class NodeAdminProvider implements Provider<NodeAdminStateUpdater> { + private static final int WEB_SERVICE_PORT = getDefaults().vespaWebServicePort(); + private static final Duration NODE_AGENT_SCAN_INTERVAL = Duration.ofSeconds(30); + private static final Duration NODE_ADMIN_CONVERGE_STATE_INTERVAL = Duration.ofSeconds(30); + + private final Logger log = Logger.getLogger(NodeAdminProvider.class.getName()); + private final NodeAdminStateUpdater nodeAdminStateUpdater; + private final Lock classLock; + + @Inject + public NodeAdminProvider(Docker docker, MetricReceiverWrapper metricReceiver, Locking locking) { + log.log(LogLevel.INFO, objectToString() + ": Creating object, acquiring lock..."); + classLock = locking.lock(this.getClass()); + try { + log.log(LogLevel.INFO, objectToString() + ": Lock acquired"); + + Clock clock = Clock.systemUTC(); + String dockerHostHostName = HostName.getLocalhost(); + ProcessExecuter processExecuter = new ProcessExecuter(); + Environment environment = new Environment(); + + ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(environment.getConfigServerHosts()); + NodeRepository nodeRepository = new NodeRepositoryImpl(requestExecutor, WEB_SERVICE_PORT); + Orchestrator orchestrator = new OrchestratorImpl(requestExecutor, WEB_SERVICE_PORT); + DockerOperations dockerOperations = new DockerOperationsImpl(docker, environment, processExecuter); + + StorageMaintainer storageMaintainer = new StorageMaintainer(docker, processExecuter, metricReceiver, environment, clock); + AclMaintainer aclMaintainer = new AclMaintainer(dockerOperations, nodeRepository, dockerHostHostName); + + Function<String, NodeAgent> nodeAgentFactory = + (hostName) -> new NodeAgentImpl(hostName, nodeRepository, orchestrator, dockerOperations, + storageMaintainer, aclMaintainer, environment, clock, NODE_AGENT_SCAN_INTERVAL); + NodeAdmin nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, + metricReceiver, clock); + + nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepository, orchestrator, storageMaintainer, nodeAdmin, + dockerHostHostName, clock, NODE_ADMIN_CONVERGE_STATE_INTERVAL); + nodeAdminStateUpdater.start(); + } catch (Exception e) { + classLock.close(); + throw e; + } + } + + @Override + public NodeAdminStateUpdater get() { + return nodeAdminStateUpdater; + } + + @Override + public void deconstruct() { + log.log(LogLevel.INFO, objectToString() + ": Stop called"); + + nodeAdminStateUpdater.stop(); + log.log(LogLevel.INFO, objectToString() + ": Stop complete"); + + classLock.close(); + log.log(LogLevel.INFO, objectToString() + ": Lock released"); + } + + private String objectToString() { + return this.getClass().getSimpleName() + "@" + Integer.toString(System.identityHashCode(this)); + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java index ff6ac9ce1e7..adfb937b8d7 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/restapi/RestApiHandler.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.restapi; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.yahoo.container.di.componentgraph.Provider; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.LoggingRequestHandler; @@ -10,8 +11,8 @@ import com.yahoo.container.logging.AccessLog; import com.yahoo.vespa.hosted.dockerapi.metrics.DimensionMetrics; import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater; -import com.yahoo.vespa.hosted.node.admin.provider.ComponentsProvider; +import javax.inject.Inject; import javax.ws.rs.core.MediaType; import java.io.IOException; import java.io.OutputStream; @@ -36,10 +37,13 @@ public class RestApiHandler extends LoggingRequestHandler{ private final NodeAdminStateUpdater refresher; private final MetricReceiverWrapper metricReceiverWrapper; - public RestApiHandler(Executor executor, AccessLog accessLog, ComponentsProvider componentsProvider) { + @Inject + public RestApiHandler(Executor executor, AccessLog accessLog, + Provider<NodeAdminStateUpdater> componentsProvider, + MetricReceiverWrapper metricReceiverWrapper) { super(executor, accessLog); - this.refresher = componentsProvider.getNodeAdminStateUpdater(); - this.metricReceiverWrapper = componentsProvider.getMetricReceiverWrapper(); + this.refresher = componentsProvider.get(); + this.metricReceiverWrapper = metricReceiverWrapper; } @Override diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java index 4434213989f..9c8dc198388 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/util/ConfigServerHttpRequestExecutor.java @@ -52,6 +52,10 @@ public class ConfigServerHttpRequestExecutor { } public static ConfigServerHttpRequestExecutor create(Set<String> configServerHosts) { + if (configServerHosts.isEmpty()) { + throw new IllegalStateException("Environment setting for config servers missing or empty."); + } + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); // Increase max total connections to 200, which should be enough cm.setMaxTotal(200); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java index 61658d4b03d..ab752bbe4c0 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerTester.java @@ -21,6 +21,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.file.Paths; import java.time.Clock; +import java.time.Duration; import java.util.function.Function; import static org.mockito.Matchers.any; @@ -32,6 +33,9 @@ import static org.mockito.Mockito.when; */ // Need to deconstruct nodeAdminStateUpdater public class DockerTester implements AutoCloseable { + private static final Duration NODE_AGENT_SCAN_INTERVAL = Duration.ofMillis(100); + private static final Duration NODE_ADMIN_CONVERGE_STATE_INTERVAL = Duration.ofMillis(10); + final CallOrderVerifier callOrderVerifier = new CallOrderVerifier(); final Docker dockerMock = new DockerMock(callOrderVerifier); final NodeRepoMock nodeRepositoryMock = new NodeRepoMock(callOrderVerifier); @@ -40,7 +44,7 @@ public class DockerTester implements AutoCloseable { private final OrchestratorMock orchestratorMock = new OrchestratorMock(callOrderVerifier); - public DockerTester() { + DockerTester() { InetAddressResolver inetAddressResolver = mock(InetAddressResolver.class); try { when(inetAddressResolver.getInetAddressForHost(any(String.class))).thenReturn(InetAddress.getByName("1.1.1.1")); @@ -57,21 +61,21 @@ public class DockerTester implements AutoCloseable { MetricReceiverWrapper mr = new MetricReceiverWrapper(MetricReceiver.nullImplementation); - final DockerOperations dockerOperations = new DockerOperationsImpl(dockerMock, environment, null); + DockerOperations dockerOperations = new DockerOperationsImpl(dockerMock, environment, null); Function<String, NodeAgent> nodeAgentFactory = (hostName) -> new NodeAgentImpl(hostName, nodeRepositoryMock, - orchestratorMock, dockerOperations, storageMaintainer, aclMaintainer, environment, clock); - nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, 100, mr, Clock.systemUTC()); - nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock, nodeAdmin, storageMaintainer, - clock, orchestratorMock, "basehostname"); - nodeAdminStateUpdater.start(5); + orchestratorMock, dockerOperations, storageMaintainer, aclMaintainer, environment, clock, NODE_AGENT_SCAN_INTERVAL); + nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, mr, Clock.systemUTC()); + nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock, orchestratorMock, storageMaintainer, + nodeAdmin, "basehostname", clock, NODE_ADMIN_CONVERGE_STATE_INTERVAL); + nodeAdminStateUpdater.start(); } - public void addContainerNodeSpec(ContainerNodeSpec containerNodeSpec) { + void addContainerNodeSpec(ContainerNodeSpec containerNodeSpec) { nodeRepositoryMock.updateContainerNodeSpec(containerNodeSpec); } @Override public void close() { - nodeAdminStateUpdater.deconstruct(); + nodeAdminStateUpdater.stop(); } } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeAdminProviderWithMocks.java index 518af5abe6b..674ee6b7d17 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/ComponentsProviderWithMocks.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeAdminProviderWithMocks.java @@ -1,6 +1,7 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.integrationTests; +import com.yahoo.container.di.componentgraph.Provider; import com.yahoo.metrics.simple.MetricReceiver; import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations; @@ -13,10 +14,10 @@ import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgent; import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAgentImpl; import com.yahoo.vespa.hosted.node.admin.noderepository.NodeRepository; import com.yahoo.vespa.hosted.node.admin.orchestrator.Orchestrator; -import com.yahoo.vespa.hosted.node.admin.provider.ComponentsProvider; import com.yahoo.vespa.hosted.node.admin.util.Environment; import java.time.Clock; +import java.time.Duration; import java.util.function.Function; import static org.mockito.Mockito.mock; @@ -26,7 +27,10 @@ import static org.mockito.Mockito.mock; * * @author dybis */ -public class ComponentsProviderWithMocks implements ComponentsProvider { +public class NodeAdminProviderWithMocks implements Provider<NodeAdminStateUpdater> { + private static final Duration NODE_AGENT_SCAN_INTERVAL = Duration.ofMillis(100); + private static final Duration NODE_ADMIN_CONVERGE_STATE_INTERVAL = Duration.ofMillis(5); + static final NodeRepository nodeRepositoryMock = mock(NodeRepository.class); static final Orchestrator orchestratorMock = mock(Orchestrator.class); static final DockerOperations dockerOperationsMock = mock(DockerOperations.class); @@ -36,22 +40,23 @@ public class ComponentsProviderWithMocks implements ComponentsProvider { private final Environment environment = new Environment.Builder().build(); private final MetricReceiverWrapper mr = new MetricReceiverWrapper(MetricReceiver.nullImplementation); private final Function<String, NodeAgent> nodeAgentFactory = - (hostName) -> new NodeAgentImpl(hostName, nodeRepositoryMock, orchestratorMock, - dockerOperationsMock, storageMaintainer, aclMaintainer, environment, Clock.systemUTC()); - private final NodeAdmin nodeAdmin = new NodeAdminImpl(dockerOperationsMock, nodeAgentFactory, storageMaintainer, aclMaintainer, 100, mr, Clock.systemUTC()); - private final NodeAdminStateUpdater nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock, nodeAdmin, storageMaintainer, Clock.systemUTC(), orchestratorMock, "localhost.test.yahoo.com"); - - public ComponentsProviderWithMocks() { - nodeAdminStateUpdater.start(10); + (hostName) -> new NodeAgentImpl(hostName, nodeRepositoryMock, orchestratorMock, dockerOperationsMock, + storageMaintainer, aclMaintainer, environment, Clock.systemUTC(), NODE_AGENT_SCAN_INTERVAL); + private final NodeAdmin nodeAdmin = new NodeAdminImpl(dockerOperationsMock, nodeAgentFactory, storageMaintainer, aclMaintainer, mr, Clock.systemUTC()); + private final NodeAdminStateUpdater nodeAdminStateUpdater = new NodeAdminStateUpdater(nodeRepositoryMock, + orchestratorMock, storageMaintainer, nodeAdmin, "localhost.test.yahoo.com", Clock.systemUTC(), NODE_ADMIN_CONVERGE_STATE_INTERVAL); + + public NodeAdminProviderWithMocks() { + nodeAdminStateUpdater.start(); } @Override - public NodeAdminStateUpdater getNodeAdminStateUpdater() { + public NodeAdminStateUpdater get() { return nodeAdminStateUpdater; } @Override - public MetricReceiverWrapper getMetricReceiverWrapper() { - return null; + public void deconstruct() { + nodeAdminStateUpdater.stop(); } } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java index 1442226a4c9..2d2a622f8f0 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/NodeRepoMock.java @@ -32,7 +32,7 @@ public class NodeRepoMock implements NodeRepository { } @Override - public List<ContainerNodeSpec> getContainersToRun() throws IOException { + public List<ContainerNodeSpec> getContainersToRun(String dockerHostHostname) throws IOException { synchronized (monitor) { return new ArrayList<>(containerNodeSpecsByHostname.values()); } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java index 0fbf7ff7751..63a7a3d1e4e 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/RunInContainerTest.java @@ -42,7 +42,7 @@ import static org.mockito.Mockito.when; */ public class RunInContainerTest { private final Logger logger = Logger.getLogger("RunInContainerTest"); - private final Orchestrator orchestrator = ComponentsProviderWithMocks.orchestratorMock; + private final Orchestrator orchestrator = NodeAdminProviderWithMocks.orchestratorMock; private final String parentHostname = "localhost.test.yahoo.com"; private JDisc container; private int port; @@ -118,7 +118,7 @@ public class RunInContainerTest { @Test public void testGetContainersToRunAPi() throws IOException, InterruptedException { doThrow(new OrchestratorException("Cannot suspend because...")).when(orchestrator).suspend(parentHostname); - when(ComponentsProviderWithMocks.nodeRepositoryMock.getContainersToRun()).thenReturn(Collections.emptyList()); + when(NodeAdminProviderWithMocks.nodeRepositoryMock.getContainersToRun(eq(parentHostname))).thenReturn(Collections.emptyList()); waitForJdiscContainerToServe(); assertTrue("The initial resume command should fail because it needs to converge first", @@ -144,7 +144,7 @@ public class RunInContainerTest { assertTrue(verifyWithRetries("resume", true)); // Lets try the same, but with an active container running on this host - when(ComponentsProviderWithMocks.nodeRepositoryMock.getContainersToRun()).thenReturn( + when(NodeAdminProviderWithMocks.nodeRepositoryMock.getContainersToRun(eq(parentHostname))).thenReturn( Collections.singletonList(new ContainerNodeSpec.Builder() .hostname("host1.test.yahoo.com") .wantedDockerImage(new DockerImage("dockerImage")) @@ -167,9 +167,9 @@ public class RunInContainerTest { assertTrue(verifyWithRetries("suspend/node-admin", true)); // Allow stopping services in active nodes - doNothing().when(ComponentsProviderWithMocks.dockerOperationsMock) + doNothing().when(NodeAdminProviderWithMocks.dockerOperationsMock) .trySuspendNode(eq(new ContainerName("host1"))); - doNothing().when(ComponentsProviderWithMocks.dockerOperationsMock) + doNothing().when(NodeAdminProviderWithMocks.dockerOperationsMock) .stopServicesOnNode(eq(new ContainerName("host1"))); assertTrue(verifyWithRetries("suspend", false)); @@ -191,7 +191,7 @@ public class RunInContainerTest { " <handler id=\"com.yahoo.vespa.hosted.node.admin.restapi.RestApiHandler\" bundle=\"node-admin\">\n" + " <binding>http://*/rest/*</binding>\n" + " </handler>\n" + - " <component id=\"node-admin\" class=\"com.yahoo.vespa.hosted.node.admin.integrationTests.ComponentsProviderWithMocks\" bundle=\"node-admin\"/>\n" + + " <component id=\"node-admin\" class=\"com.yahoo.vespa.hosted.node.admin.integrationTests.NodeAdminProviderWithMocks\" bundle=\"node-admin\"/>\n" + " <http>" + " <server id=\'myServer\' port=\'" + port + "\' />" + " </http>" + diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java index d3bce7919e6..582992869aa 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminImplTest.java @@ -31,7 +31,6 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -53,7 +52,7 @@ public class NodeAdminImplTest { private final ManualClock clock = new ManualClock(); private final NodeAdminImpl nodeAdmin = new NodeAdminImpl(dockerOperations, nodeAgentFactory, storageMaintainer, aclMaintainer, - 100, new MetricReceiverWrapper(MetricReceiver.nullImplementation), clock); + new MetricReceiverWrapper(MetricReceiver.nullImplementation), clock); @Test public void nodeAgentsAreProperlyLifeCycleManaged() throws Exception { @@ -72,12 +71,12 @@ public class NodeAdminImplTest { nodeAdmin.synchronizeNodeSpecsToNodeAgents(Collections.singletonList(hostName1), Collections.singletonList(containerName1)); inOrder.verify(nodeAgentFactory).apply(hostName1); - inOrder.verify(nodeAgent1).start(100); + inOrder.verify(nodeAgent1).start(); inOrder.verify(nodeAgent1, never()).stop(); nodeAdmin.synchronizeNodeSpecsToNodeAgents(Collections.singletonList(hostName1), Collections.singletonList(containerName1)); inOrder.verify(nodeAgentFactory, never()).apply(any(String.class)); - inOrder.verify(nodeAgent1, never()).start(anyInt()); + inOrder.verify(nodeAgent1, never()).start(); inOrder.verify(nodeAgent1, never()).stop(); nodeAdmin.synchronizeNodeSpecsToNodeAgents(Collections.emptyList(), Collections.singletonList(containerName1)); @@ -86,13 +85,13 @@ public class NodeAdminImplTest { nodeAdmin.synchronizeNodeSpecsToNodeAgents(Collections.singletonList(hostName2), Collections.singletonList(containerName1)); inOrder.verify(nodeAgentFactory).apply(hostName2); - inOrder.verify(nodeAgent2).start(100); + inOrder.verify(nodeAgent2).start(); inOrder.verify(nodeAgent2, never()).stop(); verify(nodeAgent1).stop(); nodeAdmin.synchronizeNodeSpecsToNodeAgents(Collections.emptyList(), Collections.emptyList()); inOrder.verify(nodeAgentFactory, never()).apply(any(String.class)); - inOrder.verify(nodeAgent2, never()).start(anyInt()); + inOrder.verify(nodeAgent2, never()).start(); inOrder.verify(nodeAgent2).stop(); verifyNoMoreInteractions(nodeAgent1); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java index f2d5c7e3ade..e1501cf59fe 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeadmin/NodeAdminStateUpdaterTest.java @@ -32,18 +32,20 @@ import static org.mockito.Mockito.when; /** * Basic test of NodeAdminStateUpdater + * * @author freva */ public class NodeAdminStateUpdaterTest { - private final String parentHostname = "basehost1.test.yahoo.com"; - - private final ManualClock clock = new ManualClock(); private final NodeRepository nodeRepository = mock(NodeRepository.class); - private final NodeAdmin nodeAdmin = mock(NodeAdmin.class); - private final StorageMaintainer storageMaintainer = mock(StorageMaintainer.class); private final Orchestrator orchestrator = mock(Orchestrator.class); + private final StorageMaintainer storageMaintainer = mock(StorageMaintainer.class); + private final NodeAdmin nodeAdmin = mock(NodeAdmin.class); + private final String parentHostname = "basehost1.test.yahoo.com"; + private final ManualClock clock = new ManualClock(); + private final Duration convergeStateInterval = Duration.ofSeconds(30); + private final NodeAdminStateUpdater refresher = spy(new NodeAdminStateUpdater( - nodeRepository, nodeAdmin, storageMaintainer, clock, orchestrator, parentHostname)); + nodeRepository, orchestrator, storageMaintainer, nodeAdmin, parentHostname, clock, convergeStateInterval)); @Test @@ -66,7 +68,7 @@ public class NodeAdminStateUpdaterTest { List<String> suspendHostnames = new ArrayList<>(activeHostnames); suspendHostnames.add(parentHostname); - when(nodeRepository.getContainersToRun()).thenReturn(containersToRun); + when(nodeRepository.getContainersToRun(eq(parentHostname))).thenReturn(containersToRun); // Initially everything is frozen to force convergence assertFalse(refresher.setResumeStateAndCheckIfResumed(NodeAdminStateUpdater.State.RESUMED)); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java index fd3e72d7eac..ceaa1d58f92 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/nodeagent/NodeAgentImplTest.java @@ -57,6 +57,7 @@ import static org.mockito.Mockito.when; * @author bakksjo */ public class NodeAgentImplTest { + private static final Duration NODE_AGENT_SCAN_INTERVAL = Duration.ofSeconds(30); private static final double MIN_CPU_CORES = 2; private static final double MIN_MAIN_MEMORY_AVAILABLE_GB = 16; private static final double MIN_DISK_AVAILABLE_GB = 250; @@ -621,6 +622,6 @@ public class NodeAgentImplTest { doNothing().when(storageMaintainer).writeMetricsConfig(any(), any()); return new NodeAgentImpl(hostName, nodeRepository, orchestrator, dockerOperations, - storageMaintainer, aclMaintainer, environment, clock); + storageMaintainer, aclMaintainer, environment, clock, NODE_AGENT_SCAN_INTERVAL); } } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java index bfabf0a4e4e..7456b07d2f4 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/noderepository/NodeRepositoryImplTest.java @@ -63,10 +63,10 @@ public class NodeRepositoryImplTest { private void waitForJdiscContainerToServe() throws InterruptedException { Instant start = Instant.now(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "foobar"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); while (Instant.now().minusSeconds(120).isBefore(start)) { try { - nodeRepositoryApi.getContainersToRun(); + nodeRepositoryApi.getContainersToRun("foobar"); return; } catch (Exception e) { Thread.sleep(100); @@ -85,8 +85,10 @@ public class NodeRepositoryImplTest { @Test public void testGetContainersToRunApi() throws IOException, InterruptedException { waitForJdiscContainerToServe(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); - final List<ContainerNodeSpec> containersToRun = nodeRepositoryApi.getContainersToRun(); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); + String dockerHostHostname = "dockerhost1.yahoo.com"; + + final List<ContainerNodeSpec> containersToRun = nodeRepositoryApi.getContainersToRun(dockerHostHostname); assertThat(containersToRun.size(), is(1)); final ContainerNodeSpec nodeSpec = containersToRun.get(0); assertThat(nodeSpec.hostname, is("host4.yahoo.com")); @@ -102,7 +104,7 @@ public class NodeRepositoryImplTest { @Test public void testGetContainer() throws InterruptedException, IOException { waitForJdiscContainerToServe(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); String hostname = "host4.yahoo.com"; Optional<ContainerNodeSpec> nodeSpec = nodeRepositoryApi.getContainerNodeSpec(hostname); assertThat(nodeSpec.isPresent(), is(true)); @@ -112,7 +114,7 @@ public class NodeRepositoryImplTest { @Test public void testGetContainerForNonExistingNode() throws InterruptedException, IOException { waitForJdiscContainerToServe(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); String hostname = "host-that-does-not-exist"; Optional<ContainerNodeSpec> nodeSpec = nodeRepositoryApi.getContainerNodeSpec(hostname); assertFalse(nodeSpec.isPresent()); @@ -121,7 +123,7 @@ public class NodeRepositoryImplTest { @Test public void testUpdateNodeAttributes() throws InterruptedException, IOException { waitForJdiscContainerToServe(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); String hostname = "host4.yahoo.com"; nodeRepositoryApi.updateNodeAttributes( hostname, @@ -134,7 +136,7 @@ public class NodeRepositoryImplTest { @Test(expected = RuntimeException.class) public void testUpdateNodeAttributesWithBadValue() throws InterruptedException, IOException { waitForJdiscContainerToServe(); - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); String hostname = "host4.yahoo.com"; nodeRepositoryApi.updateNodeAttributes( hostname, @@ -146,7 +148,7 @@ public class NodeRepositoryImplTest { @Test public void testMarkAsReady() throws InterruptedException, IOException { - NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port, "dockerhost1.yahoo.com"); + NodeRepository nodeRepositoryApi = new NodeRepositoryImpl(requestExecutor, port); waitForJdiscContainerToServe(); nodeRepositoryApi.markAsDirty("host5.yahoo.com"); diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java index 6f08016dff5..cef43a058c0 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/orchestrator/OrchestratorImplTest.java @@ -6,7 +6,6 @@ import com.yahoo.vespa.orchestrator.restapi.wire.BatchHostSuspendRequest; import com.yahoo.vespa.orchestrator.restapi.wire.BatchOperationResult; import com.yahoo.vespa.orchestrator.restapi.wire.HostStateChangeDenialReason; import com.yahoo.vespa.orchestrator.restapi.wire.UpdateHostResponse; -import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -20,20 +19,17 @@ import static org.mockito.Mockito.*; */ public class OrchestratorImplTest { private static final String hostName = "host123.yahoo.com"; - private ConfigServerHttpRequestExecutor requestExecutor; - private OrchestratorImpl orchestrator; - @Before - public void setup() { - requestExecutor = mock(ConfigServerHttpRequestExecutor.class); - orchestrator = new OrchestratorImpl(requestExecutor); - } + private final ConfigServerHttpRequestExecutor requestExecutor = mock(ConfigServerHttpRequestExecutor.class); + private final int port = 1234; + private final OrchestratorImpl orchestrator = new OrchestratorImpl(requestExecutor, port); + @Test public void testSuspendCall() { when(requestExecutor.put( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended", - OrchestratorImpl.WEB_SERVICE_PORT, + port, Optional.empty(), UpdateHostResponse.class )).thenReturn(new UpdateHostResponse(hostName, null)); @@ -45,7 +41,7 @@ public class OrchestratorImplTest { public void testSuspendCallWithFailureReason() { when(requestExecutor.put( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended", - OrchestratorImpl.WEB_SERVICE_PORT, + port, Optional.empty(), UpdateHostResponse.class )).thenReturn(new UpdateHostResponse(hostName, new HostStateChangeDenialReason("hostname", "fail"))); @@ -82,7 +78,7 @@ public class OrchestratorImplTest { public void testResumeCall() { when(requestExecutor.delete( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended", - OrchestratorImpl.WEB_SERVICE_PORT, + port, UpdateHostResponse.class )).thenReturn(new UpdateHostResponse(hostName, null)); @@ -93,7 +89,7 @@ public class OrchestratorImplTest { public void testResumeCallWithFailureReason() { when(requestExecutor.delete( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_API + "/" + hostName+ "/suspended", - OrchestratorImpl.WEB_SERVICE_PORT, + port, UpdateHostResponse.class )).thenReturn(new UpdateHostResponse(hostName, new HostStateChangeDenialReason("hostname", "fail"))); @@ -131,7 +127,7 @@ public class OrchestratorImplTest { when(requestExecutor.put( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API, - OrchestratorImpl.WEB_SERVICE_PORT, + port, Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)), BatchOperationResult.class )).thenReturn(BatchOperationResult.successResult()); @@ -147,7 +143,7 @@ public class OrchestratorImplTest { when(requestExecutor.put( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API, - OrchestratorImpl.WEB_SERVICE_PORT, + port, Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)), BatchOperationResult.class )).thenReturn(new BatchOperationResult(failureReason)); @@ -163,7 +159,7 @@ public class OrchestratorImplTest { when(requestExecutor.put( OrchestratorImpl.ORCHESTRATOR_PATH_PREFIX_HOST_SUSPENSION_API, - OrchestratorImpl.WEB_SERVICE_PORT, + port, Optional.of(new BatchHostSuspendRequest(parentHostName, hostNames)), BatchOperationResult.class )).thenThrow(new RuntimeException(exceptionMessage)); diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/Lock.java b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Lock.java index 3b1919a987d..1273a298592 100644 --- a/vespajlib/src/main/java/com/yahoo/concurrent/Lock.java +++ b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Lock.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.concurrent; +package com.yahoo.concurrent.lock; import java.util.concurrent.locks.ReentrantLock; diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/lock/Locking.java b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Locking.java new file mode 100644 index 00000000000..4d9f45e959e --- /dev/null +++ b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Locking.java @@ -0,0 +1,25 @@ +package com.yahoo.concurrent.lock; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author valerijf + */ +public class Locking { + private final Map<Class<?>, ReentrantLock> locks = new ConcurrentHashMap<>(); + + /** + * Locks class. This will block until the lock is acquired. + * Users of this <b>must</b> close any lock acquired. + * + * @param key the key to lock + * @return the acquired lock + */ + public Lock lock(Class<?> key) { + ReentrantLock lock = locks.computeIfAbsent(key, k -> new ReentrantLock(true)); + lock.lock(); + return new Lock(lock); + } +} diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/Locks.java b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Locks.java index ab167392329..0308b26c903 100644 --- a/vespajlib/src/main/java/com/yahoo/concurrent/Locks.java +++ b/vespajlib/src/main/java/com/yahoo/concurrent/lock/Locks.java @@ -1,5 +1,5 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.concurrent; +package com.yahoo.concurrent.lock; import com.google.common.util.concurrent.UncheckedTimeoutException; diff --git a/vespajlib/src/main/java/com/yahoo/concurrent/lock/package-info.java b/vespajlib/src/main/java/com/yahoo/concurrent/lock/package-info.java new file mode 100644 index 00000000000..326124e600f --- /dev/null +++ b/vespajlib/src/main/java/com/yahoo/concurrent/lock/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.concurrent.lock; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java index ccd02e5c6d6..d3950e4c221 100644 --- a/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java +++ b/zkfacade/src/main/java/com/yahoo/vespa/curator/mock/MockCurator.java @@ -4,8 +4,8 @@ package com.yahoo.vespa.curator.mock; import com.google.common.util.concurrent.UncheckedTimeoutException; import com.google.inject.Inject; import com.yahoo.collections.Pair; -import com.yahoo.concurrent.Lock; -import com.yahoo.concurrent.Locks; +import com.yahoo.concurrent.lock.Lock; +import com.yahoo.concurrent.lock.Locks; import com.yahoo.path.Path; import static com.yahoo.vespa.curator.mock.MemoryFileSystem.Node; |