Add very basic initial implementation for local docker usage.
Should work on Linux and on systems with docker-machine
diff --git a/containers-api/src/main/java/org/apache/aries/containers/Container.java b/containers-api/src/main/java/org/apache/aries/containers/Container.java
new file mode 100644
index 0000000..9c4d569
--- /dev/null
+++ b/containers-api/src/main/java/org/apache/aries/containers/Container.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers;
+
+import java.util.Map;
+
+public interface Container extends Comparable<Container> {
+ @Override
+ default int compareTo(Container other) {
+ return getID().compareTo(other.getID());
+ }
+
+ void destroy();
+ String getID();
+ String getIPAddress();
+
+ /* The maps of ports exposed to the outside by the container.
+ * It maps an internal port to an outside port.
+ * @return a map of exposed ports.
+ */
+ Map<Integer, Integer> getExposedPorts();
+
+ Service getService();
+}
diff --git a/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java b/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java
index 3bdaf8d..ec69264 100644
--- a/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java
+++ b/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java
@@ -4,5 +4,5 @@
@ProviderType
public interface ContainerFactory {
- Service getService(ServiceConfig config);
+ Service getService(ServiceConfig config) throws Exception;
}
diff --git a/containers-api/src/main/java/org/apache/aries/containers/Service.java b/containers-api/src/main/java/org/apache/aries/containers/Service.java
index 0132ac4..6b334d0 100644
--- a/containers-api/src/main/java/org/apache/aries/containers/Service.java
+++ b/containers-api/src/main/java/org/apache/aries/containers/Service.java
@@ -32,4 +32,6 @@
* @return The instance count.
*/
int getActualInstanceCount();
+
+ void setInstanceCount(int count);
}
\ No newline at end of file
diff --git a/containers-api/src/main/java/org/apache/aries/containers/ServiceConfig.java b/containers-api/src/main/java/org/apache/aries/containers/ServiceConfig.java
index 85c28b2..8523fca 100644
--- a/containers-api/src/main/java/org/apache/aries/containers/ServiceConfig.java
+++ b/containers-api/src/main/java/org/apache/aries/containers/ServiceConfig.java
@@ -33,7 +33,6 @@
private final String entryPoint;
private final Map<String, String> envVars;
// private final List<HealthCheck> healthChecks;
- private final int mainPort;
private final double requestedCPUunits;
private final int requestedInstances;
private final double requestedMemory; // in MiB
@@ -41,14 +40,13 @@
private ServiceConfig(String[] commandLine, String containerImage, List<Integer> containerPorts, String entryPoint,
- Map<String, String> envVars, int mainPort, double requestedCPUunits, int requestedInstances, double requestedMemory,
+ Map<String, String> envVars, double requestedCPUunits, int requestedInstances, double requestedMemory,
String serviceName) {
this.commandLine = commandLine;
this.containerImage = containerImage;
this.containerPorts = containerPorts;
this.entryPoint = entryPoint;
this.envVars = envVars;
- this.mainPort = mainPort;
this.requestedCPUunits = requestedCPUunits;
this.requestedInstances = requestedInstances;
this.requestedMemory = requestedMemory;
@@ -75,10 +73,6 @@
return envVars;
}
- public int getMainPort() {
- return mainPort;
- }
-
public double getRequestedCpuUnits() {
return requestedCPUunits;
}
@@ -91,6 +85,10 @@
return requestedMemory;
}
+ /**
+ * The name of the service deployment. This has to be unique in the system.
+ * @return The name of the service.
+ */
public String getServiceName() {
return serviceName;
}
@@ -108,7 +106,6 @@
private double requestedCpuUnits = 0.5;
private int requestedInstances = 1;
private double requestedMemory = 64;
- private int mainPort = -1;
private List<Integer> ports = new ArrayList<>();
private String serviceName;
@@ -154,23 +151,13 @@
}
public Builder port(int port) {
- return port(port, false);
- }
-
- public Builder port(int port, boolean main) {
this.ports.add(port);
- if (main) {
- if (this.mainPort != -1)
- throw new IllegalStateException("A main port has already been set: " + mainPort);
-
- this.mainPort = port;
- }
return this;
}
public ServiceConfig build() {
return new ServiceConfig(commandLine, containerImage, ports, entryPoint,
- envMap, mainPort, requestedCpuUnits, requestedInstances, requestedMemory,
+ envMap, requestedCpuUnits, requestedInstances, requestedMemory,
serviceName);
}
}
diff --git a/containers-api/src/test/java/org/apache/aries/containers/api/ServiceConfigTest.java b/containers-api/src/test/java/org/apache/aries/containers/api/ServiceConfigTest.java
index d8b7ef2..752872d 100644
--- a/containers-api/src/test/java/org/apache/aries/containers/api/ServiceConfigTest.java
+++ b/containers-api/src/test/java/org/apache/aries/containers/api/ServiceConfigTest.java
@@ -28,7 +28,7 @@
env("a", "b c d").
instances(17).
memory(5.5).
- port(8080, true).
+ port(8080).
port(9090).
build();
@@ -42,7 +42,6 @@
assertEquals(expectedEnv, sc.getEnvVars());
assertEquals(17, sc.getRequestedInstances());
assertEquals(5.5, sc.getRequestedMemory(), 0.01);
- assertEquals(8080, sc.getMainPort());
assertEquals(Arrays.asList(8080, 9090), sc.getContainerPorts());
}
diff --git a/containers-docker-local/pom.xml b/containers-docker-local/pom.xml
new file mode 100644
index 0000000..ae7cada
--- /dev/null
+++ b/containers-docker-local/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.aries.containers</groupId>
+ <artifactId>org.apache.aries.containers.parent</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <relativePath>../containers-parent</relativePath>
+ </parent>
+
+ <artifactId>org.apache.aries.containers.docker.local</artifactId>
+ <packaging>jar</packaging>
+ <name>Apache Aries Containers impl for local Docker use</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>org.apache.aries.containers.api</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
+
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ContainerImpl.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ContainerImpl.java
new file mode 100644
index 0000000..66f3f83
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ContainerImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.aries.containers.Container;
+import org.apache.aries.containers.Service;
+
+public class ContainerImpl implements Container {
+ private final String id;
+ private final String ip;
+ private final Map<Integer, Integer> ports;
+ private ServiceImpl service;
+
+ ContainerImpl(String id, String ip, Map<Integer, Integer> ports) {
+ this.id = id;
+ this.ip = ip;
+ this.ports = Collections.unmodifiableMap(new HashMap<>(ports));
+ }
+
+ @Override
+ public void destroy() {
+ service.killAndReplaceContainer(this);
+ }
+
+ @Override
+ public Map<Integer, Integer> getExposedPorts() {
+ return ports;
+ }
+
+ @Override
+ public String getID() {
+ return id;
+ }
+
+ @Override
+ public String getIPAddress() {
+ return ip;
+ }
+
+ @Override
+ public Service getService() {
+ return service;
+ }
+
+ void setService(ServiceImpl svc) {
+ service = svc;
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/DockerContainerInfo.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/DockerContainerInfo.java
new file mode 100644
index 0000000..8caa6af
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/DockerContainerInfo.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+class DockerContainerInfo {
+ private final String id;
+ private final String ip;
+
+ public DockerContainerInfo(String id, String ipAddress) {
+ this.id = id;
+ this.ip = ipAddress;
+ }
+
+ public String getID() {
+ return id;
+ }
+
+ public String getIP() {
+ return ip;
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerContainerFactory.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerContainerFactory.java
new file mode 100644
index 0000000..a7c68d1
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerContainerFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import org.apache.aries.containers.ContainerFactory;
+import org.apache.aries.containers.Service;
+import org.apache.aries.containers.ServiceConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LocalDockerContainerFactory implements ContainerFactory {
+ static final Logger LOG = LoggerFactory.getLogger(LocalDockerContainerFactory.class);
+ private static final String SERVICE_NAME = "service.name";
+
+ private static final String DOCKER_MACHINE_VM_NAME = System.getenv("DOCKER_MACHINE_NAME");
+ private static final boolean CHECK_DOCKER_MACHINE = Stream
+ .of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator)))
+ .map(Paths::get)
+ .anyMatch(path -> Files.exists(path.resolve("docker-machine")));
+
+ private static final boolean USE_DOCKER_MACHINE = (DOCKER_MACHINE_VM_NAME != null) && CHECK_DOCKER_MACHINE;
+ private static final String CONTAINER_HOST = USE_DOCKER_MACHINE
+ ? ProcessRunner.waitFor(ProcessRunner.run("docker-machine", "ip", DOCKER_MACHINE_VM_NAME))
+ : "localhost";
+
+
+ private volatile LocalDockerController docker;
+ private final AtomicBoolean initialized = new AtomicBoolean(false);
+ private final ConcurrentMap<String, Service> services =
+ new ConcurrentHashMap<>();
+
+ private void init() {
+ if (!initialized.compareAndSet(false, true))
+ return;
+
+ if (docker == null)
+ docker = new LocalDockerController();
+
+ // TODO discover any running docker containers.
+ }
+
+ @Override
+ public Service getService(ServiceConfig config) throws Exception {
+ init();
+
+ Service existingService = services.get(config.getServiceName());
+ if (existingService != null)
+ return existingService;
+
+ // TODO return discovered containers if it contains the requested one.
+
+ List<ContainerImpl> containers = createContainers(config);
+ ServiceImpl svc = new ServiceImpl(config, this, containers);
+ for (ContainerImpl c : containers) {
+ c.setService(svc);
+ }
+
+ services.put(config.getServiceName(), svc);
+
+ return svc;
+ }
+
+ private List<ContainerImpl> createContainers(ServiceConfig config) throws Exception {
+ List<ContainerImpl> containers = new ArrayList<>();
+
+ for (int i=0; i<config.getRequestedInstances(); i++) {
+ containers.add(createDockerContainer(config));
+ }
+
+ return containers;
+ }
+
+ private ContainerImpl createDockerContainer(ServiceConfig config) throws Exception {
+ List<String> command = new ArrayList<>();
+ command.add("-d");
+ command.add("-l");
+ command.add(SERVICE_NAME + "=" + config.getServiceName());
+
+ String ep = config.getEntryPoint();
+ if (ep != null) {
+ command.add("--entrypoint");
+ command.add(ep);
+ }
+
+ Map<Integer, Integer> ports = new HashMap<>();
+ for (Integer p : config.getContainerPorts()) {
+ command.add("-p");
+ int freePort = getFreePort();
+ command.add(freePort + ":" + p);
+ ports.put(p, freePort);
+
+ }
+
+ for(Map.Entry<String, String> entry : config.getEnvVars().entrySet()) {
+ command.add("-e");
+ command.add(entry.getKey() + '=' + entry.getValue());
+ }
+
+ command.add("--cpus");
+ command.add("" + config.getRequestedCpuUnits() + "");
+
+ command.add("-m");
+ command.add("" + ((int) config.getRequestedMemory()) + "m");
+
+ command.add(config.getContainerImage());
+ command.addAll(Arrays.asList(config.getCommandLine()));
+
+ DockerContainerInfo info = docker.run(command);
+
+ return new ContainerImpl(info.getID(), info.getIP(), ports);
+ }
+
+ public void destroyDockerContainer(String id, boolean remove) throws Exception {
+ if (remove) {
+ docker.remove(id);
+ } else {
+ docker.kill(id);
+ }
+ }
+
+ private int getFreePort() throws IOException {
+ try (ServerSocket ss = new ServerSocket(0)) {
+ return ss.getLocalPort();
+ }
+ }
+
+ public static String getContainerHost() {
+ return CONTAINER_HOST;
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java
new file mode 100644
index 0000000..2fecfa7
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.io.LineNumberReader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class LocalDockerController {
+
+ public String kill(String id) throws Exception {
+ return kill(id, "KILL");
+ }
+
+ private String kill(String id, String signal) throws Exception {
+ return runCommandExpectSingleID("docker", "kill", "-s", signal, id);
+ }
+
+ public String remove(String id) throws Exception {
+ // Kill the docker container if its still running
+ return runCommandExpectSingleID("docker", "rm", "-f", id);
+ }
+
+ public DockerContainerInfo run(List<String> command) throws Exception {
+ List<String> execCmd = new ArrayList<>();
+ execCmd.add("docker");
+ execCmd.add("run");
+ execCmd.addAll(command);
+
+ String id = runCommandExpectSingleID(execCmd.toArray(new String [] {}));
+ return new DockerContainerInfo(id, LocalDockerContainerFactory.getContainerHost());
+ }
+
+ String runCommandExpectSingleID(String ... command) throws Exception {
+ String res = ProcessRunner.waitFor(ProcessRunner.run(command));
+ if (res != null) {
+ res = res.trim();
+ String lastLine = res;
+ try ( final LineNumberReader lnr = new LineNumberReader(new StringReader(res)) ) {
+ String line;
+ while ( ( line = lnr.readLine()) != null ) {
+ lastLine = line;
+ }
+ }
+ if ( lastLine.indexOf(' ') != -1 ) {
+ throw new Exception("Unable to execute docker command: " + res);
+ }
+ res = lastLine;
+ }
+
+ return res;
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java
new file mode 100644
index 0000000..b145026
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ProcessRunner.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProcessRunner {
+ // For testing purposes
+ public static final String SKIP_RUN = ProcessRunner.class.getName() + ".skiprun";
+
+ private static final Logger LOG = LoggerFactory.getLogger(ProcessRunner.class);
+
+ private ProcessRunner() {
+ // Util class do not instantiate
+ }
+
+ public static Process run(String ... args) {
+ try {
+ return run(Collections.emptyMap(), args);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Process run(Map<String, String> envVars, String ... args) throws IOException, InterruptedException {
+ return run(envVars, null, args);
+ }
+
+ public static Process run(Map<String, String> envVars, final File dir, String ... args) throws IOException {
+ if ("true".equals(System.getProperty(SKIP_RUN))) {
+ LOG.debug("Skipping the external command run as configured.");
+ return null;
+ }
+
+ LOG.info("Executing shell command: {} with environment {}", args, envVars);
+
+ try {
+ ProcessBuilder builder = new ProcessBuilder(args);
+ if ( dir != null ) {
+ builder.directory(dir);
+ }
+ builder.redirectErrorStream(true);
+ Map<String, String> environ = builder.environment();
+ environ.putAll(envVars);
+
+ Process process = builder.start();
+
+ return process;
+ } catch (IOException e) {
+ LOG.error("Problem executing command: " + Arrays.toString(args), e);
+ throw e;
+ }
+ }
+
+ public static String waitFor(final Process process) {
+ try {
+ process.waitFor();
+ String res = new String(Streams.suck(process.getInputStream())).trim();
+ LOG.debug("Result: {}", res);
+ return res;
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java
new file mode 100644
index 0000000..4577f70
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.aries.containers.Service;
+import org.apache.aries.containers.ServiceConfig;
+
+public class ServiceImpl implements Service {
+ private final ServiceConfig config;
+ private final List<ContainerImpl> containers;
+ private final LocalDockerContainerFactory factory;
+
+ public ServiceImpl(ServiceConfig config,
+ LocalDockerContainerFactory factory,
+ List<ContainerImpl> containers) {
+ this.config = config;
+ this.factory = factory;
+ this.containers = new CopyOnWriteArrayList<>(containers);
+ }
+
+ @Override
+ public void destroy() {
+ setInstanceCount(0);
+ }
+
+ @Override
+ public int getActualInstanceCount() {
+ return containers.size(); // TODO obtain live
+ }
+
+ @Override
+ public void setInstanceCount(int count) {
+ try {
+ int curSize = containers.size();
+ if (count < curSize) {
+ for (int i=0 ; i < curSize - count; i++) {
+ killContainer(containers.remove(0));
+ }
+ } else {
+ // TODO implement scaling up
+ }
+ } catch (Exception e) {
+ LocalDockerContainerFactory.LOG.error("Problem changing instance count of service {} to {}",
+ config.getServiceName(), count, e);
+ }
+ }
+
+ private void killContainer(ContainerImpl container) throws Exception {
+ factory.destroyDockerContainer(container.getID(), true);
+ }
+
+ public void killAndReplaceContainer(ContainerImpl containerImpl) {
+ // TODO implement
+ }
+}
diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Streams.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Streams.java
new file mode 100644
index 0000000..37a4860
--- /dev/null
+++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Streams.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIESOR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.containers.docker.local.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public final class Streams {
+ private Streams() {
+ // Util class do not instantiate
+ }
+
+ public static void pump(InputStream is, OutputStream os) throws IOException {
+ byte[] bytes = new byte[16384];
+
+ int length = 0;
+ int offset = 0;
+
+ while ((length = is.read(bytes, offset, bytes.length - offset)) != -1) {
+ offset += length;
+
+ if (offset == bytes.length) {
+ os.write(bytes, 0, bytes.length);
+ offset = 0;
+ }
+ }
+ if (offset != 0) {
+ os.write(bytes, 0, offset);
+ }
+ }
+
+ public static byte [] suck(InputStream is) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ pump(is, baos);
+ return baos.toByteArray();
+ } finally {
+ is.close();
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 752b467..881f9c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
<modules>
<module>containers-parent</module>
<module>containers-api</module>
+ <module>containers-docker-local</module>
</modules>
</project>