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

org.opennms.gizmo.docker.GizmoDockerStacker Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016, The OpenNMS Group
 * 
 * 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.
 */
package org.opennms.gizmo.docker;

import com.google.common.base.Throwables;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.exceptions.DockerCertificateException;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.exceptions.ImageNotFoundException;
import com.spotify.docker.client.messages.Container;
import com.spotify.docker.client.messages.ContainerConfig;
import com.spotify.docker.client.messages.ContainerCreation;
import com.spotify.docker.client.messages.ContainerInfo;
import com.spotify.docker.client.messages.PortBinding;
import org.opennms.gizmo.GizmoStacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;

public class GizmoDockerStacker implements GizmoStacker {
    private static final Logger LOG = LoggerFactory.getLogger(GizmoDockerStacker.class);

    private DockerClient docker;
    private final boolean skipPull;
    private final boolean useExistingStacks;

    /**
     * Keeps track of the IDs for all the created containers so we can
     * (possibly) tear them down later.
     */
    private final Set createdContainerIds = new LinkedHashSet<>();

    /**
     * Keeps track of container meta-data.
     */
    private final Map containerInfoByAlias = new HashMap<>();

    public GizmoDockerStacker(GizmoDockerRuleBuilder builder) {
        docker = builder.docker;
        skipPull = builder.skipPull;
        useExistingStacks = builder.useExistingStacks;
    }

    @Override
    public void init() throws DockerCertificateException {
        if (docker == null) {
            // Initialize the default client
            docker = DefaultDockerClient.fromEnv().build();
        }
    }

    @Override
    public void stack(GizmoDockerStack stack) throws DockerException, InterruptedException {
        for (GizmoDockerStack dependency : stack.getDependencies()) {
            LOG.info("Stacking dependency: {}", dependency);
            stack(dependency);
        }

        stack.beforeStack(this);

        for (Entry> entry : stack.getContainersByAlias().entrySet()) {
            final String alias = entry.getKey();
            final Function containerFunc = entry.getValue();

            ContainerConfig container = containerFunc.apply(this);

            if (!skipPull) {
                try {
                    docker.inspectImage(container.image());
                } catch (ImageNotFoundException infe) {
                    LOG.info("Pulling image for alias {}: {}", alias, container.image());
                    docker.pull(container.image());
                    LOG.info("Done pulling image.");
                }
            }

            final String containerId;
            if (!useExistingStacks) {
                final ContainerCreation containerCreation = docker.createContainer(container);
                containerId = containerCreation.id();
                createdContainerIds.add(containerId);

                docker.startContainer(containerId);
            } else {
                containerId = docker.listContainers(DockerClient.ListContainersParam.withStatusRunning()).stream()
                        .filter(c -> Objects.equals(container.image(), c.image()))
                        .findFirst()
                        .orElseThrow(() -> new IllegalStateException("Could not find runnign container with image: "
                                + container.image()))
                        .id();
            }

            final ContainerInfo containerInfo = docker.inspectContainer(containerId);
            LOG.info("{} has container id: {}", alias, containerId);
            if (!containerInfo.state().running()) {
                throw new IllegalStateException("Could not start the " + alias + " container");
            }

            containerInfoByAlias.put(alias, containerInfo);
        }

        // TODO: Verify container's status'es before firing off the waiting rules
        // i.e. avoid waiting for L7 check if application image cannot be found
        for (Consumer waitingRule : stack.getWaitingRules()) {
            try {
                waitingRule.accept(this);
            } catch (Throwable t) {
                LOG.error("waitFor() rule failed. Tearing down.", t);
                throw Throwables.propagate(t);
            }
        }

        stack.afterStack(this);
    }

    @Override
    public void tearDown() {
        // Kill and remove all of the containers we created
        for (String containerId : createdContainerIds) {
            try {
                LOG.info("Killing container with id: {}", containerId);
                docker.killContainer(containerId);
                try {
                    LOG.info("Removing container with id: {}", containerId);
                    docker.removeContainer(containerId);
                } catch (Throwable t) {
                    // If autoremove is set, removeContainer will throw an NPE
                    LOG.info("Failed to remove container with id: {}. The container is likely already be removed.", containerId);
                }
            } catch (DockerException | InterruptedException e) {
                LOG.error("Failed to kill and/or remove container with id: {}", containerId, e);
            }
        }
    }

    public Set getAliases() {
        return containerInfoByAlias.keySet();
    }

    public ContainerInfo getContainerInfo(final String alias) {
        return containerInfoByAlias.get(alias);
    }

    public InetSocketAddress getServiceAddress(String alias, int port) {
        return getServiceAddress(alias, port, "tcp");
    }

    public InetSocketAddress getServiceAddress(String alias, int port, String type) {
        final ContainerInfo info = getContainerInfo(alias);
        if (info == null) {
            throw new IllegalArgumentException(String.format("No container found with alias: %s. Available containers include: ",
                    alias, containerInfoByAlias.keySet()));
        }
        final String portKey = port + "/" + type;
        final List bindings = info.networkSettings().ports().get(portKey);
        if (bindings == null) {
            throw new IllegalArgumentException(String.format("No bindings found for port %s on alias: %s. Available ports include: %s",
                    portKey, alias, info.networkSettings().ports().keySet()));
        }
        final PortBinding binding = bindings.iterator().next();
        final String host = "0.0.0.0".equals(binding.hostIp()) ? docker.getHost() : binding.hostIp();
        return new InetSocketAddress(host, Integer.valueOf(binding.hostPort()));
    }

    @Override
    public void close() throws IOException {
        if (docker == null) {
            LOG.warn("Docker client instance is null. Skipping tear down.");
            return;
        }

        docker.close();
    }

    protected DockerClient getDocker() {
        return docker;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy