blob: 0562a0b7bf278a637f7b19fa361cedcbdd6f5f7d [file] [log] [blame]
/*
* 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.taverna.activities.docker;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ContainerNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectImageResponse;
import com.github.dockerjava.api.model.Container;
import org.apache.log4j.Logger;
import org.apache.taverna.invocation.InvocationContext;
import org.apache.taverna.reference.ErrorDocument;
import org.apache.taverna.reference.ReferenceService;
import org.apache.taverna.reference.T2Reference;
import org.apache.taverna.workflowmodel.processor.activity.AbstractAsynchronousActivity;
import org.apache.taverna.workflowmodel.processor.activity.ActivityConfigurationException;
import org.apache.taverna.workflowmodel.processor.activity.AsynchronousActivityCallback;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Docker activity class responsible of handling tasks that are related to creating/invoking an external docker container.
*/
public class DockerActivity extends AbstractAsynchronousActivity<JsonNode> {
private JsonNode json;
private DockerContainerConfiguration containerConfiguration;
public static final String ACTION = "action";
public static final String INSPECT = "inspect";
public static final String CREATE_CONTAINER = "create-container";
public static final String START_CONTAINER = "start-container";
public static final String DELETE_CONTAINER = "delete-container";
public static final String STOP_CONTAINER = "stop-container";
public static final String LIST_CONTAINERS = "list-containers";
public static final String CONTAINER_ID = "container-id";
public static final String CONTAINER_NAME = "container-name";
public static final String OUT_IMAGE_ID = "image-id";
public static final String OUT_IMAGE_AUTHOR = "image-author";
public static final String OUT_IMAGE_CONTAINER = "image-container";
public static final String IN_IMAGE_NAME = "image-name";
public static final String IN_CONTAINER_START_CMD = "cnt-start-cmd";
public static final String RESPONSE_BODY_KEY = "response_body";
private static Logger LOG = Logger.getLogger(DockerActivity.class);
public DockerActivity(DockerContainerConfiguration containerConfiguration) {
this.containerConfiguration = containerConfiguration;
}
@Override
public void configure(JsonNode json) throws ActivityConfigurationException {
this.json = json;
}
@Override
public JsonNode getConfiguration() {
return json;
}
@Override
public void executeAsynch(final Map<String, T2Reference> map, final AsynchronousActivityCallback callback) {
callback.requestRun(new Runnable() {
@Override
public void run() {
Map<String, T2Reference> outputs = new HashMap<String, T2Reference>();
T2Reference responseBodyRef = null;
InvocationContext context = callback.getContext();
ReferenceService referenceService = context.getReferenceService();
String action = getRenderedParam(referenceService, context, map.get(ACTION));
JsonNodeFactory factory = new ObjectMapper().getNodeFactory();
ContainerNode outJson = null;
RemoteClient remoteClient = new RemoteClient(containerConfiguration);
if (CREATE_CONTAINER.equalsIgnoreCase(action)) {
outJson = factory.objectNode();
CreateContainerResponse response = remoteClient.createContainer();
((ObjectNode)outJson).put(CONTAINER_ID, response.getId());
} else if (INSPECT.equalsIgnoreCase(action)) {
outJson = factory.objectNode();
String imageName = getRenderedParam(referenceService, context, map.get(IN_IMAGE_NAME));
InspectImageResponse response = remoteClient.inspect(imageName);
((ObjectNode)outJson).put(OUT_IMAGE_ID, response.getId());
((ObjectNode)outJson).put(OUT_IMAGE_AUTHOR, response.getAuthor());
((ObjectNode)outJson).put(OUT_IMAGE_CONTAINER, response.getContainer());
} else if (LIST_CONTAINERS.equalsIgnoreCase(action)) {
List<Container> containerList = remoteClient.listContainers();
outJson = factory.arrayNode();
for(Container container:containerList){
((ArrayNode)outJson).add(createOutputJson(container,factory.objectNode()));
}
} else if (START_CONTAINER.equalsIgnoreCase(action)) {
String name = getRenderedParam(referenceService, context, map.get(CONTAINER_NAME));
Container container = getContainerFromName(remoteClient, name);
if(!isStarted(container)) {
remoteClient.startContainer(container.getId());
outJson = factory.objectNode();
((ObjectNode) outJson).put("started", container.getId());
} else {
outJson = factory.objectNode();
((ObjectNode) outJson).put("already-started", container.getId());
}
} else if (STOP_CONTAINER.equalsIgnoreCase(action)) {
String name = getRenderedParam(referenceService, context, map.get(CONTAINER_NAME));
Container container = getContainerFromName(remoteClient, name);
if(isStarted(container)) {
remoteClient.stopContainer(container.getId());
outJson = factory.objectNode();
((ObjectNode) outJson).put("stopped", container.getId());
} else {
outJson = factory.objectNode();
((ObjectNode) outJson).put("already-stopped", container.getId());
}
} else if (DELETE_CONTAINER.equalsIgnoreCase(action)) {
String name = getRenderedParam(referenceService, context, map.get(CONTAINER_NAME));
Container container = getContainerFromName(remoteClient, name);
if(container != null) {
remoteClient.deleteContainer(container.getId());
outJson = factory.objectNode();
((ObjectNode) outJson).put("deleted", container.getId());
} else {
outJson = factory.objectNode();
((ObjectNode) outJson).put("container-not-found", name);
}
} else {
// Creates empty node
outJson = factory.objectNode();
}
responseBodyRef = referenceService.register(outJson.toString(), 0, true, context);
outputs.put(RESPONSE_BODY_KEY, responseBodyRef);
callback.receiveResult(outputs, new int[0]);
}
});
}
public DockerContainerConfiguration getContainerConfiguration() {
return containerConfiguration;
}
private String getRenderedParam(ReferenceService referenceService, InvocationContext context, T2Reference key) {
return (String) referenceService.renderIdentifier(key, String.class, context);
}
private JsonNode createOutputJson(Container container, ObjectNode out){
out.put("id",container.getId());
out.put("command",container.getCommand());
out.put("created",container.getCreated());
out.put("image",container.getImage());
out.put("image-id",container.getImageId());
out.put("status",container.getStatus());
out.put("network-mode",container.getHostConfig().getNetworkMode());
return out;
}
private Container getContainerFromName(RemoteClient remoteClient, String containerName){
List<Container> containerList = remoteClient.listContainers();
for(Container container : containerList){
if(container.getNames().length > 0){
for(String name : container.getNames()){
if(name.endsWith(containerName)){
return container;
}
}
}
}
return null;
}
private boolean isStarted(Container container){
return container.getStatus() != null
&& container.getStatus().startsWith("Up");
}
}