All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jolokia.docker.maven.service.RunService Maven / Gradle / Ivy

The newest version!
package org.jolokia.docker.maven.service;/*
 * 
 * Copyright 2014 Roland Huss
 *
 * Licensed 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.
 */

import java.util.*;

import org.jolokia.docker.maven.access.*;
import org.jolokia.docker.maven.config.*;
import org.jolokia.docker.maven.log.LogOutputSpecFactory;
import org.jolokia.docker.maven.model.Container;
import org.jolokia.docker.maven.util.*;


/**
 * Service class for helping in running containers.
 *
 * @author roland
 * @since 16/06/15
 */
public class RunService {

    // logger delegated from top
    private Logger log;

    // Action to be used when doing a shutdown
    final private ContainerTracker tracker;

    // DAO for accessing the docker daemon
    private DockerAccess docker;

    private QueryService queryService;

    private final LogOutputSpecFactory logConfig;

    public RunService(DockerAccess docker,
                      QueryService queryService,
                      ContainerTracker tracker,
                      LogOutputSpecFactory logConfig,
                      Logger log) {
        this.docker = docker;
        this.queryService = queryService;
        this.tracker = tracker;
        this.log = log;
        this.logConfig = logConfig;
    }

    /**
     * Create and start a Exec container with the given image configuration.
     * @param containerId container id to run exec command against
     * @param command command to execute
     * @param imageConfiguration configuration of the container's image
     * @return the exec container id
     *
     * @throws DockerAccessException if access to the docker backend fails
     */
    public String execInContainer(String containerId, String command, ImageConfiguration imageConfiguration)
            throws DockerAccessException {
        Arguments arguments = new Arguments();
        arguments.setExec(Arrays.asList(EnvUtil.splitOnSpaceWithEscape(command)));
        String execContainerId = docker.createExecContainer(containerId, arguments);
        docker.startExecContainer(execContainerId, logConfig.createSpec(containerId, imageConfiguration));
        return execContainerId;
    }

    /**
     * Create and start a container with the given image configuration.
     * @param imageConfig image configuration holding the run information and the image name
     * @param mappedPorts container port mapping
     * @param mavenProps properties to fill in with dynamically assigned ports
     *
     * @return the container id
     *
     * @throws DockerAccessException if access to the docker backend fails
     */
    public String createAndStartContainer(ImageConfiguration imageConfig,
                                          PortMapping mappedPorts,
                                          Properties mavenProps) throws DockerAccessException {
        RunImageConfiguration runConfig = imageConfig.getRunConfiguration();
        String imageName = imageConfig.getName();
        String containerName = calculateContainerName(imageConfig.getAlias(), runConfig.getNamingStrategy());
        ContainerCreateConfig config = createContainerConfig(imageName, runConfig, mappedPorts, mavenProps);

        String id = docker.createContainer(config, containerName);
        startContainer(imageConfig, id);

        if (mappedPorts.containsDynamicPorts()) {
            updateMappedPorts(id, mappedPorts);
        }

        return id;
    }

    /**
     * Stop a container immediately by id.
     * @param containerId the container to stop
     * @param imageConfig image configuration for this container
     * @param keepContainer whether to keep container or to remove them after stoppings
     * @param removeVolumes whether to remove volumes after stopping
     */
    public void stopContainer(String containerId,
                              ImageConfiguration imageConfig,
                              boolean keepContainer,
                              boolean removeVolumes)
            throws DockerAccessException {
        ContainerTracker.ContainerShutdownDescriptor descriptor =
                new ContainerTracker.ContainerShutdownDescriptor(imageConfig,containerId);
        shutdown(docker, descriptor, log, keepContainer, removeVolumes);
    }

    /**
     * Lookup up whether a certain has been already started and registered. If so, stop it
     * @param containerId the container to stop
     * @param keepContainer whether to keep container or to remove them after stoppings
     * @param removeVolumes whether to remove volumes after stopping
     *
     * @throws DockerAccessException
     */
    public void stopContainer(String containerId,
                              boolean keepContainer,
                              boolean removeVolumes)
            throws DockerAccessException {
        synchronized (tracker) {
            ContainerTracker.ContainerShutdownDescriptor descriptor = tracker.getContainerShutdownDescriptor(containerId);
            if (descriptor != null) {
                shutdown(docker, descriptor, log, keepContainer, removeVolumes);
                tracker.removeContainerShutdownDescriptor(containerId);
            }
        }
    }

    /**
     * Stop all registered container
     * @param keepContainer whether to keep container or to remove them after stoppings
     * @param removeVolumes whether to remove volumes after stopping
     *
     * @throws DockerAccessException if during stopping of a container sth fails
     */
    public void stopStartedContainers(boolean keepContainer,
                                      boolean removeVolumes)
            throws DockerAccessException {
        synchronized (tracker) {
            for (ContainerTracker.ContainerShutdownDescriptor descriptor : tracker.getAllContainerShutdownDescriptors()) {
                shutdown(docker, descriptor, log, keepContainer, removeVolumes);
            }
            tracker.resetContainers();
        }
    }

    /**
     * Lookup a container that has been started
     *
     * @param lookup a container by id or alias
     * @return the container id if the container exists, null otherwise.
     */
    public String lookupContainer(String lookup) {
        return tracker.lookupContainer(lookup);
    }

    /**
     * Get the proper order for images to start
     * @param images list of images for which the order should be created
     * @return list of images in the right startup order
     */
    public List getImagesConfigsInOrder(QueryService queryService, List images) {
        return StartOrderResolver.resolve(queryService, convertToResolvables(images));
    }

    /**
     * Create port mapping for a specific configuration as it can be used when creating containers
     *
     * @param runConfig the cun configuration
     * @param properties properties to lookup variables
     * @return the portmapping
     */
    public PortMapping getPortMapping(RunImageConfiguration runConfig, Properties properties) {
        try {
            return new PortMapping(runConfig.getPorts(), properties);
        } catch (IllegalArgumentException exp) {
            throw new IllegalArgumentException("Cannot parse port mapping", exp);
        }
    }

    /**
     * Add a shutdown hook in order to stop all registered containers
     */
    public void addShutdownHookForStoppingContainers(final boolean keepContainer, final boolean removeVolumes) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    stopStartedContainers(keepContainer, removeVolumes);
                } catch (DockerAccessException e) {
                    log.error("Error while stopping containers: " + e);
                }
            }
        });
    }

    private List convertToResolvables(List images) {
        List ret = new ArrayList<>();
        for (ImageConfiguration config : images) {
            if (config.getRunConfiguration().skip()) {
                log.info(config.getDescription() + ": Skipped running");
            } else {
                ret.add(config);
            }
        }
        return ret;
    }

    // visible for testing
    ContainerCreateConfig createContainerConfig(String imageName, RunImageConfiguration runConfig, PortMapping mappedPorts,
                                                Properties mavenProps)
            throws DockerAccessException {
        try {
            ContainerCreateConfig config = new ContainerCreateConfig(imageName)
                    .hostname(runConfig.getHostname())
                    .domainname(runConfig.getDomainname())
                    .user(runConfig.getUser())
                    .workingDir(runConfig.getWorkingDir())
                    .memory(runConfig.getMemory())
                    .memorySwap(runConfig.getMemorySwap())
                    .entrypoint(runConfig.getEntrypoint())
                    .exposedPorts(mappedPorts.getContainerPorts())
                    .environment(runConfig.getEnvPropertyFile(), runConfig.getEnv(), mavenProps)
                    .labels(runConfig.getLabels())
                    .command(runConfig.getCmd())
                    .hostConfig(createContainerHostConfig(runConfig, mappedPorts));
            VolumeConfiguration volumeConfig = runConfig.getVolumeConfiguration();
            if (volumeConfig != null) {
                config.binds(volumeConfig.getBind());
            }
            return config;
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(String.format("Failed to create contained configuration for [%s]", imageName), e);
        }
    }

    ContainerHostConfig createContainerHostConfig(RunImageConfiguration runConfig, PortMapping mappedPorts)
            throws DockerAccessException {
        RestartPolicy restartPolicy = runConfig.getRestartPolicy();

        List links = findLinksContainers(runConfig.getLinks());

        ContainerHostConfig config = new ContainerHostConfig()
                .extraHosts(runConfig.getExtraHosts())
                .links(links)
                .portBindings(mappedPorts)
                .privileged(runConfig.getPrivileged())
                .dns(runConfig.getDns())
                .dnsSearch(runConfig.getDnsSearch())
                .capAdd(runConfig.getCapAdd())
                .capDrop(runConfig.getCapDrop())
                .restartPolicy(restartPolicy.getName(), restartPolicy.getRetry());
        VolumeConfiguration volConfig = runConfig.getVolumeConfiguration();
        if (volConfig != null) {
            config.binds(volConfig.getBind())
                  .volumesFrom(findVolumesFromContainers(volConfig.getFrom()));
        }
        return config;
    }

    List findLinksContainers(List links) throws DockerAccessException {
        List ret = new ArrayList<>();
        for (String[] link : EnvUtil.splitOnLastColon(links)) {
            String id = findContainerId(link[0], false);
            if (id == null) {
                throw new DockerAccessException("No container found for image/alias '%s', unable to link", link[0]);
            }
            ret.add(queryService.getContainerName(id) + ":" + link[1]);
        }
        return ret.size() != 0 ? ret : null;
    }

    // visible for testing
    List findVolumesFromContainers(List images) throws DockerAccessException {
        List list = new ArrayList<>();
        if (images != null) {
            for (String image : images) {
                String id = findContainerId(image, true);
                if (id == null) {
                    throw new DockerAccessException("No container found for image/alias '%s', unable to mount volumes", image);
                }

                list.add(queryService.getContainerName(id));
            }
        }
        return list;
    }


    private String calculateContainerName(String alias, RunImageConfiguration.NamingStrategy namingStrategy) {
        if (namingStrategy == RunImageConfiguration.NamingStrategy.none) {
            return null;
        }
        if (alias == null) {
            throw new IllegalArgumentException("A naming scheme 'alias' requires an image alias to be set");
        }
        return alias;
    }

    // checkAllContainers: false = only running containers are considered
    private String findContainerId(String imageNameOrAlias, boolean checkAllContainers) throws DockerAccessException {
        String id = lookupContainer(imageNameOrAlias);

        // check for external container. The image name is interpreted as a *container name* for that case ...
        if (id == null) {
            Container container = queryService.getContainerByName(imageNameOrAlias);
            if (container != null && (checkAllContainers || container.isRunning())) {
                id = container.getId();
            }
        }
        return id;
    }

    private void startContainer(ImageConfiguration imageConfig, String id) throws DockerAccessException {
        log.info(imageConfig.getDescription() + ": Start container " + id);
        docker.startContainer(id);
        tracker.registerContainer(id, imageConfig);
    }

    private void updateMappedPorts(String containerId, PortMapping mappedPorts) throws DockerAccessException {
        Container container = queryService.getContainer(containerId);
        mappedPorts.updateVariablesWithDynamicPorts(container.getPortBindings());
    }

    private void shutdown(DockerAccess access,
                          ContainerTracker.ContainerShutdownDescriptor descriptor,
                          Logger log, boolean keepContainer, boolean removeVolumes)
            throws DockerAccessException {

        String containerId = descriptor.getContainerId();
        if (descriptor.getPreStop() != null) {
            try {
                execInContainer(containerId, descriptor.getPreStop(), descriptor.getImageConfiguration());
            } catch (DockerAccessException e) {
                log.error(e.getMessage());
            }
        }
        // Stop the container
        int killGracePeriod = descriptor.getKillGracePeriod();
        int killGracePeriodInSeconds = (killGracePeriod + 500) / 1000;
        if (killGracePeriod != 0 && killGracePeriodInSeconds == 0) {
            log.warn("A kill grace period of " + killGracePeriod + "ms leads to no wait at all since its rounded to seconds. " +
                     "Please use at least 500 as value for wait.kill");
        }
        access.stopContainer(containerId, killGracePeriodInSeconds);
        if (killGracePeriod > 0) {
            log.debug("Shutdown: Wait " + killGracePeriodInSeconds + " s after stopping and before killing container");
            WaitUtil.sleep(killGracePeriodInSeconds * 1000);
        }
        if (!keepContainer) {
            int shutdownGracePeriod = descriptor.getShutdownGracePeriod();
            if (shutdownGracePeriod != 0) {
                log.debug("Shutdown: Wait " + shutdownGracePeriod + " ms before removing container");
                WaitUtil.sleep(shutdownGracePeriod);
            }
            // Remove the container
            access.removeContainer(containerId, removeVolumes);
        }
        log.info(descriptor.getDescription() + ": Stop" + (keepContainer ? "" : " and remove") + " container " +
                 containerId.substring(0, 12));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy