Commit e507ba49 authored by Joerg Domaschka's avatar Joerg Domaschka

Merge pull request #17 from cloudiator/PS-176

Ps 176: support for custom docker registry and snapshots being pushed to this registry.
parents abd60978 38ddc5d0
package de.uniulm.omi.cloudiator.lance.lca.containers.docker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
enum DockerConfiguration {
INSTANCE{
},
;
private final String hostname;
private final int port;
private final boolean useRegistry;
DockerConfiguration(){
useRegistry = DockerConfigurationFields.isEnabled();
hostname = DockerConfigurationFields.getHostname();
port = DockerConfigurationFields.getPort();
if(shouldBeUsedButCant()) {
DockerConfigurationFields.LOGGER.error("cannot make use of registry@ " + hostname + ":" + port);
}
}
private boolean shouldBeUsedButCant() {
if(!useRegistry) return false;
if(hostname == null || hostname.isEmpty() || port < 0 || port > 65555) // whatever the right number is
return true;
return false;
}
static class DockerConfigurationFields {
private static final Logger LOGGER = LoggerFactory.getLogger(DockerConfigurationFields.class);
public static final String DOCKER_REGISTRY_USE_KEY = "host.docker.registry.use";
public static final String DOCKER_REGISTRY_HOST_KEY = "host.docker.registry.host";
public static final String DOCKER_REGISTRY_PORT_KEY = "host.docker.registry.port";
public static final Boolean DOCKER_REGISTRY_USE_DEFAULT = Boolean.FALSE;
public static final String DOCKER_REGISTRY_HOST_DEFAULT = null;
public static final int DOCKER_REGISTRY_PORT_DEFAULT = 5000;
static boolean isEnabled() {
System.out.println(System.getProperties());
String s = System.getProperty(DockerConfigurationFields.DOCKER_REGISTRY_USE_KEY, DockerConfigurationFields.DOCKER_REGISTRY_USE_DEFAULT.toString());
if("true".equals(s)) {
LOGGER.debug("use of docker registry enabled.");
return true;
}
LOGGER.debug("use of docker registry disabled.");
return false;
}
static String getHostname() {
return System.getProperty(DockerConfigurationFields.DOCKER_REGISTRY_HOST_KEY, DockerConfigurationFields.DOCKER_REGISTRY_HOST_DEFAULT);
}
static int getPort() {
String s = System.getProperty(DockerConfigurationFields.DOCKER_REGISTRY_PORT_KEY, Integer.toString(DockerConfigurationFields.DOCKER_REGISTRY_PORT_DEFAULT));
try {
return Integer.parseInt(s);
} catch(NumberFormatException ex) {
if(s != null && !s.isEmpty())
LOGGER.warn("cannot set port nunmber");
return -1;
}
}
}
public boolean registryCanBeUsed() {
return registryEnabled() && !shouldBeUsedButCant();
}
public boolean registryEnabled() {
return useRegistry;
}
public String prependRegistry(String pre) {
if(useRegistry && !shouldBeUsedButCant()) {
return hostname + ":" + port + "/" + pre;
}
return pre;
}
}
......@@ -61,20 +61,21 @@ public class DockerContainerLogic implements ContainerLogic, LifecycleActionInte
DockerContainerLogic(ComponentInstanceId id, DockerConnector client, DeployableComponent comp,
DeploymentContext ctx, OperatingSystem os, NetworkHandler network,
DockerShellFactory shellFactoryParam) {
this(id, client, os, ctx, comp, network, shellFactoryParam);
DockerShellFactory shellFactoryParam, DockerConfiguration dockerConfig) {
this(id, client, os, ctx, comp, network, shellFactoryParam, dockerConfig);
}
private DockerContainerLogic(ComponentInstanceId id, DockerConnector clientParam, OperatingSystem osParam,
DeploymentContext ctx, DeployableComponent componentParam,
NetworkHandler networkParam, DockerShellFactory shellFactoryParam) {
NetworkHandler networkParam, DockerShellFactory shellFactoryParam,
DockerConfiguration dockerConfigParam) {
if(osParam == null)
throw new NullPointerException("operating system has to be set.");
myId = id;
client = clientParam;
imageHandler = new DockerImageHandler(osParam, new DockerOperatingSystemTranslator(), clientParam, componentParam);
imageHandler = new DockerImageHandler(osParam, new DockerOperatingSystemTranslator(), clientParam, componentParam, dockerConfigParam);
deploymentContext = ctx;
shellFactory = shellFactoryParam;
myComponent = componentParam;
......@@ -111,7 +112,7 @@ public class DockerContainerLogic implements ContainerLogic, LifecycleActionInte
@Override
public void doDestroy(boolean force) throws ContainerException {
/* docker ignores the flag */
/* currently docker ignores the flag */
try {
client.stopContainer(myId);
} catch(DockerException de) {
......
......@@ -52,6 +52,7 @@ public class DockerContainerManager implements ContainerManager {
private final String hostname;
private final DockerConnector client;
private final ContainerRegistry registry = new ContainerRegistry();
private final DockerConfiguration dockerConfig = DockerConfiguration.INSTANCE;
public DockerContainerManager(HostContext vmId) {
this(vmId, LcaConstants.LOCALHOST_IP, false);
......@@ -89,7 +90,7 @@ public class DockerContainerManager implements ContainerManager {
GlobalRegistryAccessor accessor = new GlobalRegistryAccessor(ctx, comp, id);
NetworkHandler networkHandler = new NetworkHandler(accessor, comp, hostContext);
DockerContainerLogic logic = new DockerContainerLogic(id, client, comp, ctx, os, networkHandler, shellFactory);
DockerContainerLogic logic = new DockerContainerLogic(id, client, comp, ctx, os, networkHandler, shellFactory, dockerConfig);
// DockerLifecycleInterceptor interceptor = new DockerLifecycleInterceptor(accessor, id, networkHandler, comp, shellFactory);
ExecutionContext ec = new ExecutionContext(os, shellFactory);
LifecycleController controller = new LifecycleController(comp.getLifecycleStore(), logic, accessor, ec);
......
......@@ -35,14 +35,16 @@ final class DockerImageHandler {
private final OperatingSystem os;
private final DockerConnector client;
private final DeployableComponent myComponent;
private final DockerConfiguration dockerConfig;
private volatile ImageCreationType initSource;
DockerImageHandler(OperatingSystem osParam, DockerOperatingSystemTranslator translatorParam,
DockerConnector clientParam, DeployableComponent componentParam) {
DockerConnector clientParam, DeployableComponent componentParam, DockerConfiguration dockerConfigParam) {
if(osParam == null)
throw new NullPointerException("operating system has to be set.");
dockerConfig = dockerConfigParam;
os = osParam;
translator = translatorParam;
client = clientParam;
......@@ -79,56 +81,87 @@ final class DockerImageHandler {
String tmpkey = componentInstallId;
String ostag = os.toString();
ostag = ostag.replaceAll(":", "_");
return tmpkey.toLowerCase() + ":" + ostag.toLowerCase();
String tmp = tmpkey.toLowerCase() + ":" + ostag.toLowerCase();
if(!dockerConfig.registryCanBeUsed())
return tmp;
return dockerConfig.prependRegistry(tmp);
}
private String doGetSingleImage(String key) throws DockerException {
// TODO: remove this as soon as access to a private registry is set
if(client.findImage(key) != null) {
return key;
}
try {
try {
client.pullImage(key);
return key;
} catch(DockerException de) {
LOGGER.debug("could not pull image.", de);
LOGGER.debug("could not pull image: " + key + " creating a new one.");
return null;
}
}
/**
*
* @param myId the instance id of the container
* @return
* @throws DockerException
*/
String doPullImages(ComponentInstanceId myId) throws DockerException {
String componentInstallId = createComponentInstallId();
// first step: try to find matching image for configured component
// currently not implemented; TODO: implement
// second step: try to find matching image for prepared component
String result = searchImageInLocalCache();
if(result == null){
// second step: try to find matching image for prepared component
// in case a custom docker registry is configured
result = getImageFromPrivateRepository();
if(result == null) {
// third step: fall back to the operating system //
result = getImageFromDefaultLocation();
}
}
if(result != null)
return result;
throw new DockerException("cannot pull image: " + myId);
}
private String searchImageInLocalCache() {
// currently not implemented;
return null;
}
private String getImageFromPrivateRepository() throws DockerException {
String componentInstallId = createComponentInstallId();
String target = buildImageTagName(ImageCreationType.COMPONENT, componentInstallId);
String result = doGetSingleImage(target);
if(result != null) {
LOGGER.info("pulled prepared image: " + result);
initSource = ImageCreationType.COMPONENT;
return result; //FIXME: set in component lifecycle stage
return result;
}
// third step
target = buildImageTagName(ImageCreationType.OPERATING_SYSTEM, null);
result = doGetSingleImage(target);
if(result == null) {
throw new DockerException("cannot pull image: " + myId + " for key " + target);
return null;
}
private String getImageFromDefaultLocation() throws DockerException {
String target = buildImageTagName(ImageCreationType.OPERATING_SYSTEM, null);
String result = doGetSingleImage(target);
if(result != null) {
LOGGER.info("pulled default image: " + result);
initSource = ImageCreationType.OPERATING_SYSTEM;
return result;
}
initSource = ImageCreationType.OPERATING_SYSTEM;
return target;
return null;
}
/** here, we may want to run a snapshotting action
* @throws DockerException */
void runPostInstallAction(ComponentInstanceId myId) throws DockerException {
String componentInstallId = createComponentInstallId();
if(initSource == ImageCreationType.OPERATING_SYSTEM) {
String componentInstallId = createComponentInstallId();
String target = buildImageTagName(ImageCreationType.COMPONENT, componentInstallId);
// we probably will not need this return value
// let's keep it for debugging purposes, though
// @SuppressWarnings("unused") String imageSnapshot =
client.createImageSnapshot(myId, componentInstallId, os);
client.createSnapshotImage(myId, target);
client.pushImage(target);
}
}
......
......@@ -34,13 +34,12 @@ public interface DockerConnector {
/**
*
* @param key should be the id to use; either component id after initialisation
* of the component instance id after configuration
* @param key should be the id to use including the registry
* @param os will be used as a tag in both cases
* @return the name of the container
* @throws DockerException
*/
String createImageSnapshot(ComponentInstanceId containerId, String key, OperatingSystem os) throws DockerException;
String createSnapshotImage(ComponentInstanceId containerId, String key) throws DockerException;
String createContainer(String image, ComponentInstanceId myId, Map<Integer, Integer> portsToSet) throws DockerException;
......@@ -53,4 +52,6 @@ public interface DockerConnector {
DockerShell getSideShell(ComponentInstanceId myId) throws DockerException;
void stopContainer(ComponentInstanceId myId) throws DockerException;
void pushImage(String target) throws DockerException;;
}
......@@ -99,7 +99,9 @@ final class ProcessBasedConnector implements DockerConnector {
@Override
public String findImage(String target) throws DockerException {
String[] split = target.split(":");
ExecResult result = ProcessWrapper.singleDockerCommand("images", "--no-trunc=true", split[0]);
String imageName = split.length == 2 ? split[0] : (split[0] + split[1]);
String tagName = split.length == 2 ? split[1] : split[2];
ExecResult result = ProcessWrapper.singleDockerCommand("images", "--no-trunc=true", imageName);
if(!result.isSuccess()) {
return null;
}
......@@ -112,7 +114,7 @@ final class ProcessBasedConnector implements DockerConnector {
line = reader.readLine();
if(line == null)
break;
String id = findTag(split[1], line);
String id = findTag(tagName, line);
if(id != null)
return id;
}
......@@ -185,7 +187,7 @@ final class ProcessBasedConnector implements DockerConnector {
}
@Override
public String createImageSnapshot(ComponentInstanceId containerId, String key, OperatingSystem os) throws DockerException {
public String createSnapshotImage(ComponentInstanceId containerId, String key) throws DockerException {
final String author = "--author=" + "\"Cloudiator LifecylceAgent\"";
final String message = "--message=" + "\"automatic snapshot after initialisation\"";
......@@ -196,8 +198,16 @@ final class ProcessBasedConnector implements DockerConnector {
return result.getOutput();
}
throw new DockerException(result.getError());
}
@Override
public void pushImage(String imageId) throws DockerException {
ExecResult result = ProcessWrapper.singleDockerCommand("push", imageId);
if(result.isSuccess()) {
return;
}
throw new DockerException(result.getError());
}
@Override
public DockerShell getSideShell(ComponentInstanceId myId) throws DockerException {
......
package de.uniulm.omi.cloudiator.lance.lca.containers.docker;
import static org.junit.Assert.*;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import de.uniulm.omi.cloudiator.lance.container.spec.os.OperatingSystem;
import de.uniulm.omi.cloudiator.lance.lca.HostContext;
import de.uniulm.omi.cloudiator.lance.lca.container.ComponentInstanceId;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerException;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.connector.ConnectorFactory;
import de.uniulm.omi.cloudiator.lance.lca.containers.docker.connector.DockerConnector;
import de.uniulm.omi.cloudiator.lance.lca.containers.dummy.DummyInterceptor;
import de.uniulm.omi.cloudiator.lance.lifecycle.ExecutionContext;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleController;
import de.uniulm.omi.cloudiator.lance.lifecycle.LifecycleHandlerType;
import de.uniulm.omi.cloudiator.lance.lifecycles.CoreElements;
import de.uniulm.omi.cloudiator.lance.lifecycles.LifecycleStoreCreator;
public class DockerSnapshottingTest {
public volatile DummyInterceptor interceptor;
public volatile LifecycleController lcc;
public volatile CoreElements core;
public volatile LifecycleStoreCreator creator;
private volatile ExecutionContext ctx;
private volatile Map<ComponentInstanceId, Map<String, String>> dumb;
private volatile DockerContainerManager manager;
static class FakeHostContext implements HostContext {
@Override
public String getPublicIp() {
throw new UnsupportedOperationException();
}
@Override
public String getInternalIp() {
throw new UnsupportedOperationException();
}
@Override
public String getCloudIdentifier() {
throw new UnsupportedOperationException();
}
@Override
public void close() throws InterruptedException {
throw new UnsupportedOperationException();
}
@Override
public void run(Runnable runner) {
throw new UnsupportedOperationException();
}
@Override
public ScheduledFuture<?> scheduleAction(Runnable runner) {
throw new UnsupportedOperationException();
}
}
static String hostname = "localhost";
@BeforeClass
public static void configureHostContext() {
CoreElements.initStatic();
}
@Before
public void init() {
System.setProperty("host.docker.registry.port", Integer.toString(5000));
System.setProperty("host.docker.registry.host", "134.60.64.242");
System.setProperty("host.docker.registry.use", Boolean.toString(true));
core = new CoreElements(false);
manager = new DockerContainerManager(new FakeHostContext());
}
@Test
public void testCreation() throws ContainerException {
DockerConfiguration dockerConfig = DockerConfiguration.INSTANCE;
DockerConnector client = ConnectorFactory.INSTANCE.createConnector(hostname);
DockerShellFactory shellFactory = new DockerShellFactory();
DockerContainerLogic logic = new DockerContainerLogic(core.componentInstanceId,
client, core.comp, core.ctx, OperatingSystem.UBUNTU_14_04,
core.networkHandler, shellFactory, dockerConfig);
logic.doCreate();
logic.doDestroy(true);
}
@Test
public void testSnapshot() throws ContainerException {
DockerConfiguration dockerConfig = DockerConfiguration.INSTANCE;
DockerConnector client = ConnectorFactory.INSTANCE.createConnector(hostname);
DockerShellFactory shellFactory = new DockerShellFactory();
DockerContainerLogic logic = new DockerContainerLogic(core.componentInstanceId,
client, core.comp, core.ctx, OperatingSystem.UBUNTU_14_04,
core.networkHandler, shellFactory, dockerConfig);
logic.doCreate();
logic.prepare(LifecycleHandlerType.PRE_INSTALL);
logic.postprocess(LifecycleHandlerType.PRE_INSTALL);
logic.doDestroy(true);
logic = new DockerContainerLogic(core.componentInstanceId,
client, core.comp, core.ctx, OperatingSystem.UBUNTU_14_04,
core.networkHandler, shellFactory, dockerConfig);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment