Repository: aries-containers Updated Branches: refs/heads/master 5d0a7ed62 -> 56d7ed550
Rename ContainerFactory to ServiceManager and add some Javadoc. Project: http://git-wip-us.apache.org/repos/asf/aries-containers/repo Commit: http://git-wip-us.apache.org/repos/asf/aries-containers/commit/56d7ed55 Tree: http://git-wip-us.apache.org/repos/asf/aries-containers/tree/56d7ed55 Diff: http://git-wip-us.apache.org/repos/asf/aries-containers/diff/56d7ed55 Branch: refs/heads/master Commit: 56d7ed55046cc1ff3014b7eaf771d401dd32a3ca Parents: 5d0a7ed Author: David Bosschaert <[email protected]> Authored: Fri May 26 16:19:11 2017 +0100 Committer: David Bosschaert <[email protected]> Committed: Fri May 26 16:19:11 2017 +0100 ---------------------------------------------------------------------- .../aries/containers/ContainerFactory.java | 32 --- .../apache/aries/containers/ServiceManager.java | 62 +++++ .../containers/docker/local/impl/Activator.java | 8 +- .../docker/local/impl/ContainerImpl.java | 2 +- .../local/impl/LocalDockerContainerFactory.java | 247 ------------------- .../local/impl/LocalDockerController.java | 2 +- .../local/impl/LocalDockerServiceManager.java | 247 +++++++++++++++++++ .../docker/local/impl/ServiceImpl.java | 6 +- .../impl/MarathonConfigManagedService.java | 10 +- .../marathon/impl/MarathonContainerFactory.java | 151 ------------ .../marathon/impl/MarathonServiceManager.java | 151 ++++++++++++ 11 files changed, 474 insertions(+), 444 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java ---------------------------------------------------------------------- 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 deleted file mode 100644 index d6f988b..0000000 --- a/containers-api/src/main/java/org/apache/aries/containers/ContainerFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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. - */ -package org.apache.aries.containers; - -import java.util.Set; - -import org.osgi.annotation.versioning.ProviderType; - -@ProviderType -public interface ContainerFactory { - public static final String BINDING = "container.factory.binding"; - - Service getService(ServiceConfig config) throws Exception; - - Set<String> listServices() throws Exception; -} http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-api/src/main/java/org/apache/aries/containers/ServiceManager.java ---------------------------------------------------------------------- diff --git a/containers-api/src/main/java/org/apache/aries/containers/ServiceManager.java b/containers-api/src/main/java/org/apache/aries/containers/ServiceManager.java new file mode 100644 index 0000000..8c86370 --- /dev/null +++ b/containers-api/src/main/java/org/apache/aries/containers/ServiceManager.java @@ -0,0 +1,62 @@ +/* + * 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. + */ +package org.apache.aries.containers; + +import java.util.Set; + +import org.osgi.annotation.versioning.ProviderType; + +/** The service manager creates services and otherwise manages service. + * Multiple implementations of this inteface can co-exist binding to + * various back-end service containers. <p> + * + * To find a service manager for a specific binding select this service + * on the {@code container.factory.binding} service property. + */ +@ProviderType +public interface ServiceManager { + /** + * Services should register this property to declare the back-end that + * they bind to. For example {@code marathon} or {@code docker.local}. + */ + public static final String BINDING = "container.factory.binding"; + + /** + * Obtain a service for the specified configuration. If the service + * already exists it should be returned. Also if the service was created + * during previous runs of the manager it should be discovered and used. + * <p> + * Otherwise a new service should be created for the specified + * configuration. <p> + * Services can outlive the life cycle of the Service Manager. + * + * @param config The service configuration for the service. + * @return A {@link Service} instance representing this service. + * @throws Exception + */ + Service getService(ServiceConfig config) throws Exception; + + /** + * List available services by name. + * @return A set with the service names. If no services are found an empty + * set is returned. + * @throws Exception + */ + Set<String> listServices() throws Exception; +} http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Activator.java ---------------------------------------------------------------------- diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Activator.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Activator.java index a43c5a5..3b54050 100644 --- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Activator.java +++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/Activator.java @@ -21,7 +21,7 @@ package org.apache.aries.containers.docker.local.impl; import java.util.Dictionary; import java.util.Hashtable; -import org.apache.aries.containers.ContainerFactory; +import org.apache.aries.containers.ServiceManager; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -29,9 +29,9 @@ public class Activator implements BundleActivator { @Override public void start(BundleContext context) throws Exception { Dictionary<String, Object> props = new Hashtable<>(); - props.put(ContainerFactory.BINDING, "docker.local"); - context.registerService(ContainerFactory.class, - new LocalDockerContainerFactory(), props); + props.put(ServiceManager.BINDING, "docker.local"); + context.registerService(ServiceManager.class, + new LocalDockerServiceManager(), props); } @Override http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ContainerImpl.java ---------------------------------------------------------------------- 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 index 6584ab6..2909f37 100644 --- 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 @@ -42,7 +42,7 @@ class ContainerImpl implements Container { try { service.killContainer(this); } catch (Exception e) { - LocalDockerContainerFactory.LOG.warn("Problem killing container {}", this, e); + LocalDockerServiceManager.LOG.warn("Problem killing container {}", this, e); } } http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerContainerFactory.java ---------------------------------------------------------------------- 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 deleted file mode 100644 index 673f664..0000000 --- a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerContainerFactory.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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. - */ -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.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -import org.apache.aries.containers.Container; -import org.apache.aries.containers.ContainerFactory; -import org.apache.aries.containers.Service; -import org.apache.aries.containers.ServiceConfig; -import org.apache.felix.utils.json.JSONParser; -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 = "org.apache.aries.containers.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 ConcurrentMap<String, Service> services = - new ConcurrentHashMap<>(); - - public LocalDockerContainerFactory() { - if (docker == null) - docker = new LocalDockerController(); - } - - List<String> getDockerIDs(ServiceConfig config) { - return docker.ps(SERVICE_NAME + "=" + config.getServiceName()); - } - - @Override - public Service getService(ServiceConfig config) throws Exception { - Service existingService = services.get(config.getServiceName()); - if (existingService != null) - return existingService; - - List<ContainerImpl> containers = discoverContainers(config); - if (containers.size() == 0) - 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; - } - - 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); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - List<ContainerImpl> discoverContainers(ServiceConfig config) { - List<ContainerImpl> res = new ArrayList<>(); - List<String> ids = getDockerIDs(config); - if (ids.size() == 0) - return Collections.emptyList(); - - String infoJSON = docker.inspect(ids); - List<Object> data = new JSONParser(infoJSON).getParsedList(); - for (Object d : data) { - if (!(d instanceof Map)) - continue; - - Map m = (Map) d; - Object ns = m.get("NetworkSettings"); - Map<Integer, Integer> ports = new HashMap<>(); - if (ns instanceof Map) { - Object pd = ((Map) ns).get("Ports"); - if (pd instanceof Map) { - Map pm = (Map) pd; - for(Map.Entry entry : (Set<Map.Entry>) pm.entrySet()) { - try { - String key = entry.getKey().toString(); - int idx = key.indexOf('/'); - if (idx > 0) - key = key.substring(0, idx); - int containerPort = Integer.parseInt(key); - int hostPort = -1; - for (Object val : (List) entry.getValue()) { - if (val instanceof Map) { - hostPort = Integer.parseInt(((Map) val).get("HostPort").toString()); - } - } - - if (hostPort != -1) { - ports.put(containerPort, hostPort); - } - } catch (Exception nfe) { - // ignore parsing exceptions, try next one - } - } - } - } - // TODO check that the settings match! - res.add(new ContainerImpl(m.get("Id").toString(), LocalDockerContainerFactory.getContainerHost(), ports)); - } - return res; - } - - private int getFreePort() throws IOException { - try (ServerSocket ss = new ServerSocket(0)) { - return ss.getLocalPort(); - } - } - - public static String getContainerHost() { - return CONTAINER_HOST; - } - - @Override - @SuppressWarnings("rawtypes") - public Set<String> listServices() throws Exception { - Set<String> res = new HashSet<>(); - List<String> ids = docker.ps(SERVICE_NAME); - - for (Service svc : services.values()) { - res.add(svc.getConfiguration().getServiceName()); - for (Container c : svc.listContainers()) { - ids.remove(c.getID()); - } - } - - String json = docker.inspect(ids); - for (Object data : new JSONParser(json).getParsedList()) { - // These are services that have been launched previously and are not internally synced yet - if (!(data instanceof Map)) { - continue; - } - - Object cd = ((Map) data).get("Config"); - if (cd instanceof Map) { - Object ld = ((Map) cd).get("Labels"); - if (ld instanceof Map) { - Object serviceName = ((Map) ld).get(SERVICE_NAME); - if (serviceName instanceof String) { - res.add((String) serviceName); - } - } - } - } - - return res; - } -} http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerController.java ---------------------------------------------------------------------- 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 index 6b0e8c7..cf3ede1 100644 --- 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 @@ -44,7 +44,7 @@ class LocalDockerController { execCmd.addAll(command); String id = runCommandExpectSingleID(execCmd.toArray(new String [] {})); - return new DockerContainerInfo(id, LocalDockerContainerFactory.getContainerHost()); + return new DockerContainerInfo(id, LocalDockerServiceManager.getContainerHost()); } public List<String> ps(String labelFilter) { http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java ---------------------------------------------------------------------- diff --git a/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java new file mode 100644 index 0000000..4639e25 --- /dev/null +++ b/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/LocalDockerServiceManager.java @@ -0,0 +1,247 @@ +/* + * 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. + */ +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.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.apache.aries.containers.Container; +import org.apache.aries.containers.ServiceManager; +import org.apache.aries.containers.Service; +import org.apache.aries.containers.ServiceConfig; +import org.apache.felix.utils.json.JSONParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LocalDockerServiceManager implements ServiceManager { + static final Logger LOG = LoggerFactory.getLogger(LocalDockerServiceManager.class); + private static final String SERVICE_NAME = "org.apache.aries.containers.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 ConcurrentMap<String, Service> services = + new ConcurrentHashMap<>(); + + public LocalDockerServiceManager() { + if (docker == null) + docker = new LocalDockerController(); + } + + List<String> getDockerIDs(ServiceConfig config) { + return docker.ps(SERVICE_NAME + "=" + config.getServiceName()); + } + + @Override + public Service getService(ServiceConfig config) throws Exception { + Service existingService = services.get(config.getServiceName()); + if (existingService != null) + return existingService; + + List<ContainerImpl> containers = discoverContainers(config); + if (containers.size() == 0) + 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; + } + + 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); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + List<ContainerImpl> discoverContainers(ServiceConfig config) { + List<ContainerImpl> res = new ArrayList<>(); + List<String> ids = getDockerIDs(config); + if (ids.size() == 0) + return Collections.emptyList(); + + String infoJSON = docker.inspect(ids); + List<Object> data = new JSONParser(infoJSON).getParsedList(); + for (Object d : data) { + if (!(d instanceof Map)) + continue; + + Map m = (Map) d; + Object ns = m.get("NetworkSettings"); + Map<Integer, Integer> ports = new HashMap<>(); + if (ns instanceof Map) { + Object pd = ((Map) ns).get("Ports"); + if (pd instanceof Map) { + Map pm = (Map) pd; + for(Map.Entry entry : (Set<Map.Entry>) pm.entrySet()) { + try { + String key = entry.getKey().toString(); + int idx = key.indexOf('/'); + if (idx > 0) + key = key.substring(0, idx); + int containerPort = Integer.parseInt(key); + int hostPort = -1; + for (Object val : (List) entry.getValue()) { + if (val instanceof Map) { + hostPort = Integer.parseInt(((Map) val).get("HostPort").toString()); + } + } + + if (hostPort != -1) { + ports.put(containerPort, hostPort); + } + } catch (Exception nfe) { + // ignore parsing exceptions, try next one + } + } + } + } + // TODO check that the settings match! + res.add(new ContainerImpl(m.get("Id").toString(), LocalDockerServiceManager.getContainerHost(), ports)); + } + return res; + } + + private int getFreePort() throws IOException { + try (ServerSocket ss = new ServerSocket(0)) { + return ss.getLocalPort(); + } + } + + public static String getContainerHost() { + return CONTAINER_HOST; + } + + @Override + @SuppressWarnings("rawtypes") + public Set<String> listServices() throws Exception { + Set<String> res = new HashSet<>(); + List<String> ids = docker.ps(SERVICE_NAME); + + for (Service svc : services.values()) { + res.add(svc.getConfiguration().getServiceName()); + for (Container c : svc.listContainers()) { + ids.remove(c.getID()); + } + } + + String json = docker.inspect(ids); + for (Object data : new JSONParser(json).getParsedList()) { + // These are services that have been launched previously and are not internally synced yet + if (!(data instanceof Map)) { + continue; + } + + Object cd = ((Map) data).get("Config"); + if (cd instanceof Map) { + Object ld = ((Map) cd).get("Labels"); + if (ld instanceof Map) { + Object serviceName = ((Map) ld).get(SERVICE_NAME); + if (serviceName instanceof String) { + res.add((String) serviceName); + } + } + } + } + + return res; + } +} http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-docker-local/src/main/java/org/apache/aries/containers/docker/local/impl/ServiceImpl.java ---------------------------------------------------------------------- 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 index 2c594a5..94aba89 100644 --- 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 @@ -29,10 +29,10 @@ import org.apache.aries.containers.ServiceConfig; class ServiceImpl implements Service { private final ServiceConfig config; private final List<ContainerImpl> containers; - private final LocalDockerContainerFactory factory; + private final LocalDockerServiceManager factory; public ServiceImpl(ServiceConfig config, - LocalDockerContainerFactory factory, + LocalDockerServiceManager factory, List<ContainerImpl> containers) { this.config = config; this.factory = factory; @@ -71,7 +71,7 @@ class ServiceImpl implements Service { } } } catch (Exception e) { - LocalDockerContainerFactory.LOG.error("Problem changing instance count of service {} to {}", + LocalDockerServiceManager.LOG.error("Problem changing instance count of service {} to {}", config.getServiceName(), count, e); } } http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonConfigManagedService.java ---------------------------------------------------------------------- diff --git a/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonConfigManagedService.java b/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonConfigManagedService.java index 87c1baf..d4b8746 100644 --- a/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonConfigManagedService.java +++ b/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonConfigManagedService.java @@ -21,7 +21,7 @@ package org.apache.aries.containers.marathon.impl; import java.util.Dictionary; import java.util.Hashtable; -import org.apache.aries.containers.ContainerFactory; +import org.apache.aries.containers.ServiceManager; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; @@ -34,7 +34,7 @@ public class MarathonConfigManagedService implements ManagedService { private final BundleContext bundleContext; volatile String marathonURL; - volatile ServiceRegistration<ContainerFactory> reg; + volatile ServiceRegistration<ServiceManager> reg; MarathonConfigManagedService(BundleContext bc) { bundleContext = bc; @@ -60,10 +60,10 @@ public class MarathonConfigManagedService implements ManagedService { reg.unregister(); marathonURL = marURL; - ContainerFactory cf = new MarathonContainerFactory(marathonURL); + ServiceManager cf = new MarathonServiceManager(marathonURL); Dictionary<String, Object> props = new Hashtable<>(); - props.put(ContainerFactory.BINDING, "marathon"); - reg = bundleContext.registerService(ContainerFactory.class, cf, props); + props.put(ServiceManager.BINDING, "marathon"); + reg = bundleContext.registerService(ServiceManager.class, cf, props); } } http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonContainerFactory.java ---------------------------------------------------------------------- diff --git a/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonContainerFactory.java b/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonContainerFactory.java deleted file mode 100644 index e5c6b16..0000000 --- a/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonContainerFactory.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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. - */ -package org.apache.aries.containers.marathon.impl; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.aries.containers.ContainerFactory; -import org.apache.aries.containers.Service; -import org.apache.aries.containers.ServiceConfig; - -import mesosphere.dcos.client.DCOSClient; -import mesosphere.dcos.client.model.DCOSAuthCredentials; -import mesosphere.marathon.client.Marathon; -import mesosphere.marathon.client.MarathonClient; -import mesosphere.marathon.client.model.v2.App; -import mesosphere.marathon.client.model.v2.Container; -import mesosphere.marathon.client.model.v2.Docker; -import mesosphere.marathon.client.model.v2.GetAppsResponse; -import mesosphere.marathon.client.model.v2.Port; - -public class MarathonContainerFactory implements ContainerFactory { - private static final String SERVICE_NAME = "org.apache.aries.containers.service.name"; - - private final Marathon marathonClient; - - public MarathonContainerFactory(String marathonURL) { - marathonClient = MarathonClient.getInstance(marathonURL); - } - - public MarathonContainerFactory(String marathonURL, String dcosUser, String passToken, boolean serviceAcct) { - DCOSAuthCredentials authCredentials; - if (serviceAcct) { - authCredentials = DCOSAuthCredentials.forServiceAccount(dcosUser, passToken); - } else { - authCredentials = DCOSAuthCredentials.forUserAccount(dcosUser, passToken); - } - marathonClient = DCOSClient.getInstance(marathonURL, authCredentials); - } - - @Override - public Service getService(ServiceConfig config) throws Exception { - GetAppsResponse existing = marathonClient.getApps( - Collections.singletonMap("label", SERVICE_NAME + "==" + config.getServiceName())); - if (existing.getApps().size() > 0) { - return createServiceFromExistingApp(existing.getApps(), config); - } - - App app = new App(); - app.setId(config.getServiceName()); - app.setCpus(config.getRequestedCpuUnits()); - app.setMem(config.getRequestedMemory()); - app.setInstances(config.getRequestedInstances()); - app.setEnv(Collections.unmodifiableMap(config.getEnvVars())); - - StringBuilder cmd = new StringBuilder(); - if (config.getEntryPoint() != null) { - // TODO is this right? - cmd.append(config.getEntryPoint()); - } - - if (config.getCommandLine().length > 0) { - for (String c : config.getCommandLine()) { - if (cmd.length() > 0) - cmd.append(' '); - - if (c.contains(" ")) - c = "'" + c + "'"; - - cmd.append(c); - } - } - if (cmd.length() > 0) - app.setCmd(cmd.toString()); - - Docker docker = new Docker(); - docker.setImage(config.getContainerImage()); - docker.setNetwork("BRIDGE"); // TODO is this correct? - List<Port> ports = new ArrayList<>(); - for (int p : config.getContainerPorts()) { - Port port = new Port(); - port.setContainerPort(p); - ports.add(port); - } - docker.setPortMappings(ports); - - Container container = new Container(); - container.setType("DOCKER"); - container.setDocker(docker); - - app.setContainer(container); - app.addLabel(SERVICE_NAME, config.getServiceName()); - - App res = marathonClient.createApp(app); - - return createServiceFromApp(res, config); - } - - private Service createServiceFromExistingApp(List<App> apps, ServiceConfig config) { - if (apps.size() != 1) - throw new IllegalStateException("More than one existing app found for service " + - config.getServiceName() + " " + apps); - - return createServiceFromApp(apps.get(0), config); - } - - private Service createServiceFromApp(App app, ServiceConfig cfg) { - // TODO make this check more thorough - if (!cfg.getServiceName().equals(app.getLabels().get(SERVICE_NAME))) - throw new IllegalStateException("Application and configuration don't match"); - - ServiceImpl svc = new ServiceImpl(marathonClient, app, cfg); - return svc; - } - - @Override - public Set<String> listServices() throws Exception { - GetAppsResponse services = marathonClient.getApps( - Collections.singletonMap("label", SERVICE_NAME)); - - Set<String> serviceNames = new HashSet<>(); - for (App app : services.getApps()) { - Map<String, String> labels = app.getLabels(); - String name = labels.get(SERVICE_NAME); - if (name != null && name.length() > 0) - serviceNames.add(name); - } - return serviceNames; - } - -} http://git-wip-us.apache.org/repos/asf/aries-containers/blob/56d7ed55/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonServiceManager.java ---------------------------------------------------------------------- diff --git a/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonServiceManager.java b/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonServiceManager.java new file mode 100644 index 0000000..f39ef61 --- /dev/null +++ b/containers-marathon/src/main/java/org/apache/aries/containers/marathon/impl/MarathonServiceManager.java @@ -0,0 +1,151 @@ +/* + * 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. + */ +package org.apache.aries.containers.marathon.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.aries.containers.ServiceManager; +import org.apache.aries.containers.Service; +import org.apache.aries.containers.ServiceConfig; + +import mesosphere.dcos.client.DCOSClient; +import mesosphere.dcos.client.model.DCOSAuthCredentials; +import mesosphere.marathon.client.Marathon; +import mesosphere.marathon.client.MarathonClient; +import mesosphere.marathon.client.model.v2.App; +import mesosphere.marathon.client.model.v2.Container; +import mesosphere.marathon.client.model.v2.Docker; +import mesosphere.marathon.client.model.v2.GetAppsResponse; +import mesosphere.marathon.client.model.v2.Port; + +public class MarathonServiceManager implements ServiceManager { + private static final String SERVICE_NAME = "org.apache.aries.containers.service.name"; + + private final Marathon marathonClient; + + public MarathonServiceManager(String marathonURL) { + marathonClient = MarathonClient.getInstance(marathonURL); + } + + public MarathonServiceManager(String marathonURL, String dcosUser, String passToken, boolean serviceAcct) { + DCOSAuthCredentials authCredentials; + if (serviceAcct) { + authCredentials = DCOSAuthCredentials.forServiceAccount(dcosUser, passToken); + } else { + authCredentials = DCOSAuthCredentials.forUserAccount(dcosUser, passToken); + } + marathonClient = DCOSClient.getInstance(marathonURL, authCredentials); + } + + @Override + public Service getService(ServiceConfig config) throws Exception { + GetAppsResponse existing = marathonClient.getApps( + Collections.singletonMap("label", SERVICE_NAME + "==" + config.getServiceName())); + if (existing.getApps().size() > 0) { + return createServiceFromExistingApp(existing.getApps(), config); + } + + App app = new App(); + app.setId(config.getServiceName()); + app.setCpus(config.getRequestedCpuUnits()); + app.setMem(config.getRequestedMemory()); + app.setInstances(config.getRequestedInstances()); + app.setEnv(Collections.unmodifiableMap(config.getEnvVars())); + + StringBuilder cmd = new StringBuilder(); + if (config.getEntryPoint() != null) { + // TODO is this right? + cmd.append(config.getEntryPoint()); + } + + if (config.getCommandLine().length > 0) { + for (String c : config.getCommandLine()) { + if (cmd.length() > 0) + cmd.append(' '); + + if (c.contains(" ")) + c = "'" + c + "'"; + + cmd.append(c); + } + } + if (cmd.length() > 0) + app.setCmd(cmd.toString()); + + Docker docker = new Docker(); + docker.setImage(config.getContainerImage()); + docker.setNetwork("BRIDGE"); // TODO is this correct? + List<Port> ports = new ArrayList<>(); + for (int p : config.getContainerPorts()) { + Port port = new Port(); + port.setContainerPort(p); + ports.add(port); + } + docker.setPortMappings(ports); + + Container container = new Container(); + container.setType("DOCKER"); + container.setDocker(docker); + + app.setContainer(container); + app.addLabel(SERVICE_NAME, config.getServiceName()); + + App res = marathonClient.createApp(app); + + return createServiceFromApp(res, config); + } + + private Service createServiceFromExistingApp(List<App> apps, ServiceConfig config) { + if (apps.size() != 1) + throw new IllegalStateException("More than one existing app found for service " + + config.getServiceName() + " " + apps); + + return createServiceFromApp(apps.get(0), config); + } + + private Service createServiceFromApp(App app, ServiceConfig cfg) { + // TODO make this check more thorough + if (!cfg.getServiceName().equals(app.getLabels().get(SERVICE_NAME))) + throw new IllegalStateException("Application and configuration don't match"); + + ServiceImpl svc = new ServiceImpl(marathonClient, app, cfg); + return svc; + } + + @Override + public Set<String> listServices() throws Exception { + GetAppsResponse services = marathonClient.getApps( + Collections.singletonMap("label", SERVICE_NAME)); + + Set<String> serviceNames = new HashSet<>(); + for (App app : services.getApps()) { + Map<String, String> labels = app.getLabels(); + String name = labels.get(SERVICE_NAME); + if (name != null && name.length() > 0) + serviceNames.add(name); + } + return serviceNames; + } + +}
