diff options
author | HÃ¥kon Hallingstad <hakon@oath.com> | 2017-12-20 15:19:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-20 15:19:05 +0100 |
commit | 84401aab45cabddb3530b9d20ecd7cc7a92f21be (patch) | |
tree | edad8ec4614ec03f4c92603d5b6e01d80619f104 | |
parent | a8662ee3e07e3608eea25d8312080ca4e12e865a (diff) |
Revert "Revert "Prepare for making node admin components optional""
9 files changed, 236 insertions, 32 deletions
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java index 2bf3f0f8d84..bc94c39d135 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/Docker.java @@ -12,6 +12,11 @@ import java.util.Optional; * and to avoid OSGi exporting those classes. */ public interface Docker { + /** + * Must be called before any other method. May be called more than once. + */ + void start(); + interface CreateContainerCommand { CreateContainerCommand withLabel(String name, String value); CreateContainerCommand withEnvironment(String name, String value); diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java index 9de2cae604f..054f466657f 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerImpl.java @@ -59,7 +59,10 @@ public class DockerImpl implements Docker { static final String LABEL_NAME_MANAGEDBY = "com.yahoo.vespa.managedby"; private final int SECONDS_TO_WAIT_BEFORE_KILLING; + private final boolean fallbackTo123OnErrors; private static final String FRAMEWORK_CONTAINER_PREFIX = "/"; + private final DockerConfig config; + private final boolean inProduction; private Optional<DockerImageGarbageCollector> dockerImageGC = Optional.empty(); private CounterWrapper numberOfDockerDaemonFails; @@ -68,39 +71,61 @@ public class DockerImpl implements Docker { private final Set<DockerImage> scheduledPulls = new HashSet<>(); // Exposed for testing. - final DockerClient dockerClient; + DockerClient dockerClient; + + @Inject + public DockerImpl(final DockerConfig config, MetricReceiverWrapper metricReceiver) { + this(config, + true, /* fallback to 1.23 on errors */ + metricReceiver, + !config.isRunningLocally()); + } + + private DockerImpl(final DockerConfig config, + boolean fallbackTo123OnErrors, + MetricReceiverWrapper metricReceiverWrapper, + boolean inProduction) { + this.config = config; + this.fallbackTo123OnErrors = fallbackTo123OnErrors; + this.inProduction = inProduction; + if (config == null) { + this.SECONDS_TO_WAIT_BEFORE_KILLING = 10; + } else { + SECONDS_TO_WAIT_BEFORE_KILLING = config.secondsToWaitBeforeKillingContainer(); + } + if (metricReceiverWrapper != null) { + setMetrics(metricReceiverWrapper); + } + } // For testing DockerImpl(final DockerClient dockerClient) { + this(null, false, null, false); this.dockerClient = dockerClient; - this.SECONDS_TO_WAIT_BEFORE_KILLING = 10; } - DockerImpl( - final DockerConfig config, - boolean fallbackTo123OnErrors, - MetricReceiverWrapper metricReceiverWrapper) { - SECONDS_TO_WAIT_BEFORE_KILLING = config.secondsToWaitBeforeKillingContainer(); - - dockerClient = initDockerConnection(config, fallbackTo123OnErrors); - setMetrics(metricReceiverWrapper); + // For testing + DockerImpl(final DockerConfig config, + boolean fallbackTo123OnErrors, + MetricReceiverWrapper metricReceiverWrapper) { + this(config, fallbackTo123OnErrors, metricReceiverWrapper, false); } - @Inject - public DockerImpl(final DockerConfig config, MetricReceiverWrapper metricReceiver) { - this( - config, - true, /* fallback to 1.23 on errors */ - metricReceiver); - - if (!config.isRunningLocally()) { - Duration minAgeToDelete = Duration.ofMinutes(config.imageGCMinTimeToLiveMinutes()); - dockerImageGC = Optional.of(new DockerImageGarbageCollector(minAgeToDelete)); - - try { - setupDockerNetworkIfNeeded(); - } catch (Exception e) { - throw new DockerException("Could not setup docker network", e); + @Override + public void start() { + if (config != null) { + if (dockerClient == null) { + dockerClient = initDockerConnection(); + } + if (inProduction) { + Duration minAgeToDelete = Duration.ofMinutes(config.imageGCMinTimeToLiveMinutes()); + dockerImageGC = Optional.of(new DockerImageGarbageCollector(minAgeToDelete)); + + try { + setupDockerNetworkIfNeeded(); + } catch (Exception e) { + throw new DockerException("Could not setup docker network", e); + } } } } @@ -489,7 +514,7 @@ public class DockerImpl implements Docker { } } - private DockerClient initDockerConnection(final DockerConfig config, boolean fallbackTo123orErrors) { + private DockerClient initDockerConnection() { JerseyDockerCmdExecFactory dockerFactory = new JerseyDockerCmdExecFactory() .withMaxPerRouteConnections(config.maxPerRouteConnections()) .withMaxTotalConnections(config.maxTotalConnections()) @@ -508,7 +533,7 @@ public class DockerImpl implements Docker { logger.info("Found version 1.24 or newer of remote API, using 1.23."); } } catch (Exception e) { - if (!fallbackTo123orErrors) { + if (!fallbackTo123OnErrors) { throw e; } logger.log(LogLevel.ERROR, "Failed when trying to figure out remote API version of docker, using 1.23", e); diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerTestUtils.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerTestUtils.java index 079d6876043..549af0d85cb 100644 --- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerTestUtils.java +++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerTestUtils.java @@ -46,11 +46,13 @@ public class DockerTestUtils { public static DockerImpl getDocker() { if (docker == null) { - docker = new DockerImpl( + DockerImpl tmpDocker = new DockerImpl( dockerConfig, false, /* fallback to 1.23 on errors */ new MetricReceiverWrapper(MetricReceiver.nullImplementation)); - createDockerTestNetworkIfNeeded(docker); + tmpDocker.start(); + createDockerTestNetworkIfNeeded(tmpDocker); + docker = tmpDocker; } return docker; diff --git a/node-admin/src/main/application/services.xml b/node-admin/src/main/application/services.xml index 030c42ac8c5..b39a9bce30a 100644 --- a/node-admin/src/main/application/services.xml +++ b/node-admin/src/main/application/services.xml @@ -6,7 +6,7 @@ <handler id="com.yahoo.vespa.hosted.node.admin.restapi.RestApiHandler" bundle="node-admin"> <binding>http://*/rest/*</binding> </handler> - <component id="node-admin" class="com.yahoo.vespa.hosted.node.admin.provider.NodeAdminProvider" bundle="node-admin"/> + <component id="node-admin" class="com.yahoo.vespa.hosted.node.admin.bootstrap.NodeAdminComponent" bundle="node-admin"/> <component id="docker-api" class="com.yahoo.vespa.hosted.dockerapi.DockerImpl" bundle="docker-api"/> <component id="metrics-wrapper" class="com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper" bundle="docker-api"/> diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminComponent.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminComponent.java new file mode 100644 index 00000000000..fbcee36b724 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminComponent.java @@ -0,0 +1,29 @@ +// 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.bootstrap; + +import com.google.inject.Inject; +import com.yahoo.concurrent.classlock.ClassLocking; +import com.yahoo.container.di.componentgraph.Provider; +import com.yahoo.vespa.hosted.dockerapi.Docker; +import com.yahoo.vespa.hosted.dockerapi.metrics.MetricReceiverWrapper; + +public class NodeAdminComponent implements Provider<NodeAdminMain> { + private final NodeAdminMain nodeAdminMain; + + @Inject + NodeAdminComponent(Docker docker, + MetricReceiverWrapper metricReceiver, + ClassLocking classLocking) { + this.nodeAdminMain = new NodeAdminMain(docker, metricReceiver, classLocking); + } + + @Override + public NodeAdminMain get() { + return nodeAdminMain; + } + + @Override + public void deconstruct() { + nodeAdminMain.close(); + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminConfig.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminConfig.java new file mode 100644 index 00000000000..f8d5962fc3f --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminConfig.java @@ -0,0 +1,26 @@ +// 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.bootstrap; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(value = JsonInclude.Include.NON_NULL) +public class NodeAdminConfig { + private static final ObjectMapper mapper = new ObjectMapper(); + + enum Mode { + TENANT, + CONFIG_SERVER_HOST + } + + @JsonProperty("mode") + public Mode mode = Mode.TENANT; + + public static NodeAdminConfig get() { + // TODO: Get config from file + return new NodeAdminConfig(); + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminMain.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminMain.java new file mode 100644 index 00000000000..87c3f5452ed --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/bootstrap/NodeAdminMain.java @@ -0,0 +1,113 @@ +// 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.bootstrap; + +import com.yahoo.concurrent.classlock.ClassLocking; +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.docker.DockerOperationsImpl; +import com.yahoo.vespa.hosted.node.admin.maintenance.StorageMaintainer; +import com.yahoo.vespa.hosted.node.admin.maintenance.acl.AclMaintainer; +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.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; + +/** + * NodeAdminMain is the main component of the node admin JDisc application: + * - It will read config and check its environment to figure out its responsibilities + * - It will "start" (only) the necessary components. + * - Other components MUST NOT try to start (typically in constructor) since the features + * they provide is NOT WANTED and possibly destructive, and/or the environment may be + * incompatible. For instance, trying to contact the Docker daemon too early will + * be fatal: the node admin may not have installed and started the docker daemon. + */ +public class NodeAdminMain implements AutoCloseable { + 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 Docker docker; + private final MetricReceiverWrapper metricReceiver; + private final ClassLocking classLocking; + + private NodeAdminStateUpdater nodeAdminStateUpdater; + + NodeAdminMain(Docker docker, MetricReceiverWrapper metricReceiver, ClassLocking classLocking) { + this.docker = docker; + this.metricReceiver = metricReceiver; + this.classLocking = classLocking; + start(); + } + + @Override + public void close() { + nodeAdminStateUpdater.stop(); + } + + public NodeAdminStateUpdater getNodeAdminStateUpdater() { + return nodeAdminStateUpdater; + } + + public void start() { + NodeAdminConfig config = NodeAdminConfig.get(); + + switch (config.mode) { + case TENANT: + setupTenantHostNodeAdmin(); + break; + case CONFIG_SERVER_HOST: + setupConfigServerHostNodeAdmin(); + break; + default: + throw new IllegalStateException( + "Unknown bootstrap mode: " + config.mode.name()); + } + } + + private void setupTenantHostNodeAdmin() { + Clock clock = Clock.systemUTC(); + String dockerHostHostName = HostName.getLocalhost(); + ProcessExecuter processExecuter = new ProcessExecuter(); + Environment environment = new Environment(); + + ConfigServerHttpRequestExecutor requestExecutor = ConfigServerHttpRequestExecutor.create(environment.getConfigServerUris()); + NodeRepository nodeRepository = new NodeRepositoryImpl(requestExecutor); + Orchestrator orchestrator = new OrchestratorImpl(requestExecutor); + 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, classLocking); + + nodeAdminStateUpdater.start(); + } + + private void setupConfigServerHostNodeAdmin() { + // TODO: + // - install and start docker daemon + // - Read config that specifies which containers to start how + // - use thin static backends for node repo, orchestrator, and others + // - Start core node admin. + } +} 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 42b36d95374..b7377ff172c 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 @@ -9,6 +9,7 @@ import com.yahoo.container.jdisc.LoggingRequestHandler; 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.bootstrap.NodeAdminMain; import com.yahoo.vespa.hosted.node.admin.nodeadmin.NodeAdminStateUpdater; import javax.inject.Inject; @@ -38,10 +39,10 @@ public class RestApiHandler extends LoggingRequestHandler{ @Inject public RestApiHandler(Executor executor, AccessLog accessLog, - NodeAdminStateUpdater nodeAdminStateUpdater, + NodeAdminMain nodeAdminMain, MetricReceiverWrapper metricReceiverWrapper) { super(executor, accessLog); - this.refresher = nodeAdminStateUpdater; + this.refresher = nodeAdminMain.getNodeAdminStateUpdater(); this.metricReceiverWrapper = metricReceiverWrapper; } diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java index a1cc1850d23..7a5d713936d 100644 --- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java +++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/integrationTests/DockerMock.java @@ -33,6 +33,9 @@ public class DockerMock implements Docker { } @Override + public void start() { } + + @Override public CreateContainerCommand createContainerCommand( DockerImage dockerImage, ContainerResources containerResources, |