Package brooklyn.entity.container.docker

Source Code of brooklyn.entity.container.docker.DockerHostSshDriver

/*
* Copyright 2014 by Cloudsoft Corporation Limited
*
* 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 brooklyn.entity.container.docker;

import static brooklyn.util.ssh.BashCommands.*;
import static java.lang.String.format;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import org.jclouds.net.domain.IpPermission;
import org.jclouds.net.domain.IpProtocol;

import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.lifecycle.ScriptHelper;
import brooklyn.entity.container.DockerUtils;
import brooklyn.entity.container.weave.WeaveContainer;
import brooklyn.entity.container.weave.WeaveInfrastructure;
import brooklyn.entity.software.SshEffectorTasks;
import brooklyn.location.OsDetails;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.location.jclouds.JcloudsSshMachineLocation;
import brooklyn.location.jclouds.networking.JcloudsLocationSecurityGroupCustomizer;
import brooklyn.util.collections.MutableList;
import brooklyn.util.collections.MutableMap;
import brooklyn.util.file.ArchiveUtils;
import brooklyn.util.net.Cidr;
import brooklyn.util.net.Networking;
import brooklyn.util.net.Urls;
import brooklyn.util.os.Os;
import brooklyn.util.repeat.Repeater;
import brooklyn.util.task.DynamicTasks;
import brooklyn.util.task.system.ProcessTaskWrapper;
import brooklyn.util.text.Identifiers;
import brooklyn.util.text.Strings;
import brooklyn.util.time.Duration;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

public class DockerHostSshDriver extends AbstractSoftwareProcessSshDriver implements DockerHostDriver {

    public DockerHostSshDriver(DockerHostImpl entity, SshMachineLocation machine) {
        super(entity, machine);
    }

    protected Map<String, Integer> getPortMap() {
        Map<String, Integer> ports = MutableMap.of();
        ports.put("dockerPort", getDockerPort());
        if (getEntity().getConfig(DockerInfrastructure.WEAVE_ENABLED)) {
            // Best guess at available port, as Weave is started _after_ the DockerHost
            Integer weavePort = getEntity()
                    .getAttribute(DockerHost.DOCKER_INFRASTRUCTURE)
                    .getAttribute(DockerInfrastructure.WEAVE_INFRASTRUCTURE)
                    .getConfig(WeaveInfrastructure.WEAVE_PORT);
            ports.put("weavePort", weavePort);
        }
        return ports;
    }

    @Override
    public Integer getDockerPort() {
        return getEntity().getAttribute(DockerHost.DOCKER_PORT);
    }

    @Override
    public String getRepository() {
        return getEntity().getAttribute(DockerHost.DOCKER_REPOSITORY);
    }

    /** {@inheritDoc} */
    @Override
    public String buildImage(String dockerFile, String name) {
        if (ArchiveUtils.ArchiveType.UNKNOWN != ArchiveUtils.ArchiveType.of(dockerFile) || Urls.isDirectory(dockerFile)) {
            ArchiveUtils.deploy(dockerFile, getMachine(), Os.mergePaths(getRunDir(), name));
            String baseImageId = buildDockerfileDirectory(name);
            log.info("Created base Dockerfile image with ID {}", baseImageId);
        } else {
            ProcessTaskWrapper<Integer> task = SshEffectorTasks.ssh(format("mkdir -p %s", Os.mergePaths(getRunDir(), name)))
                    .machine(getMachine())
                    .newTask();
            DynamicTasks.queueIfPossible(task).executionContext(getEntity()).orSubmitAndBlock();
            int result = task.get();
            if (result != 0) throw new IllegalStateException("Error creating image directory: " + name);

            // Build an image from the base Dockerfile
            copyTemplate(dockerFile, Os.mergePaths(name, "Base" + DockerUtils.DOCKERFILE));
            String baseImageId = buildDockerfile("Base" + DockerUtils.DOCKERFILE, name);
            log.info("Created base Dockerfile image with ID {}", baseImageId);
        }

        // Update the image with the Clocker sshd Dockerfile
        copyTemplate(DockerUtils.SSHD_DOCKERFILE, Os.mergePaths(name, "Sshd" + DockerUtils.DOCKERFILE), false, MutableMap.of("repository", getRepository(), "imageName", name));
        String sshdImageId = buildDockerfile("Sshd" + DockerUtils.DOCKERFILE, name);
        log.info("Created SSHable Dockerfile image with ID {}", sshdImageId);

        return sshdImageId;
    }

    private String buildDockerfileDirectory(String name) {
        String build = format("build --rm -t %s %s", Os.mergePaths(getRepository(), name), Os.mergePaths(getRunDir(), name));
        String stdout = ((DockerHost) getEntity()).runDockerCommandTimeout(build, Duration.minutes(15));
        String prefix = Strings.getFirstWordAfter(stdout, "Successfully built");

        return getImageId(prefix, name);
    }

    private String buildDockerfile(String dockerfile, String name) {
        String build = format("build --rm -t %s - < %s", Os.mergePaths(getRepository(), name), Os.mergePaths(getRunDir(), name, dockerfile));
        String stdout = ((DockerHost) getEntity()).runDockerCommandTimeout(build, Duration.minutes(15));
        String prefix = Strings.getFirstWordAfter(stdout, "Successfully built");

        return getImageId(prefix, name);
    }

    // Inspect the Docker image with this prefix
    private String getImageId(String prefix, String name) {
        String inspect = format("inspect --format={{.Id}} %s", prefix);
        String imageId = ((DockerHost) getEntity()).runDockerCommand(inspect);
        return DockerUtils.checkId(imageId);
    }

    public String getEpelRelease() {
        return getEntity().getConfig(DockerHost.EPEL_RELEASE);
    }

    @Override
    public boolean isRunning() {
        ScriptHelper helper = newScript(CHECK_RUNNING)
                .body.append(alternatives(
                        ifExecutableElse1("boot2docker", sudo("boot2docker status")),
                        ifExecutableElse1("service", sudo("service docker status"))))
                .failOnNonZeroResultCode()
                .gatherOutput();
        helper.execute();
        return helper.getResultStdout().contains("running");
    }

    @Override
    public void stop() {
        newScript(STOPPING)
                .body.append(alternatives(
                        ifExecutableElse1("boot2docker", sudo("boot2docker down")),
                        ifExecutableElse1("service", sudo("service docker stop"))))
                .failOnNonZeroResultCode()
                .execute();
    }

    @Override
    public void preInstall() {
        resolver = Entities.newDownloader(this);
        setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(format("docker-%s", getVersion()))));
    }

    @Override
    public void install() {
        OsDetails osDetails = getMachine().getMachineDetails().getOsDetails();
        String osVersion = osDetails.getVersion();
        String arch = osDetails.getArch();
        if (!osDetails.is64bit()) {
            throw new IllegalStateException("Docker supports only 64bit OS");
        }
        if (osDetails.getName().equalsIgnoreCase("ubuntu") && osVersion.equals("12.04")) {
            List<String> commands = ImmutableList.<String>builder().add(installPackage("linux-image-generic-lts-raring"))
                    .add(installPackage("linux-headers-generic-lts-raring"))
                    .add(sudo("reboot"))
                    .build();
            executeKernelInstallation(commands);
        }
        if (osDetails.getName().equalsIgnoreCase("centos")) {
            List<String> commands = ImmutableList.<String>builder()
                    .add(sudo("yum -y --nogpgcheck upgrade kernel"))
                    .add(sudo("reboot"))
                    .build();
            executeKernelInstallation(commands);
        }

        log.info("waiting for Docker host {} to be sshable", getLocation());
        boolean isSshable = Repeater.create()
                .every(Duration.TEN_SECONDS)
                .until(new Callable<Boolean>() {
                    public Boolean call() {
                        return getLocation().isSshable();
                    }
                })
                .limitTimeTo(Duration.minutes(15)) // Because of the reboot
                .run();
        if (!isSshable) {
            throw new IllegalStateException(String.format("The entity %s is not ssh'able after reboot", entity));
        }
        log.info("Docker host {} is now sshable; continuing with setup", getLocation());

        List<String> commands = Lists.newArrayList();
        if (osDetails.getName().equalsIgnoreCase("ubuntu")) {
            commands.add(installDockerOnUbuntu());
        } else if (osDetails.getName().equalsIgnoreCase("centos")) {
            commands.add(ifExecutableElse0("yum", useYum(osVersion, arch, getEpelRelease())));
            commands.add(installPackage(ImmutableMap.of("yum", "docker-io"), null));
        } else {
            commands.add(installDockerFallback());
        }
        newScript(INSTALLING)
                .failOnNonZeroResultCode()
                .body.append(commands)
                .execute();
    }

    private void executeKernelInstallation(List<String> commands) {
        newScript(INSTALLING + "kernel")
                .body.append(commands)
                .execute();
    }

    private String useYum(String osVersion, String arch, String epelRelease) {
            String osMajorVersion = osVersion.substring(0, osVersion.lastIndexOf("."));
        return chainGroup(
                INSTALL_WGET,
                alternatives(
                        sudo("rpm -qa | grep epel-release"),
                        sudo(format("rpm -Uvh http://dl.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, arch, epelRelease)))
        );
    }

    private String installDockerOnUbuntu() {
        String version = getVersion();
        if (version.matches("^[0-9]+\\.[0-9]+$")) {
            version += ".0"; // Append minor version
        }
        if (log.isDebugEnabled()) log.debug("Installing Docker version {} on Ubuntu", version);
        return chainGroup(
                INSTALL_CURL,
                installPackage("apt-transport-https"),
                executeCommandThenAsUserTeeOutputToFile("echo deb https://get.docker.com/ubuntu docker main", "root", "/etc/apt/sources.list.d/docker.list"),
                sudo("apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9"),
                installPackage("lxc-docker-" + version));
    }

    /**
     * Uses the curl-able install.sh script provided at {@code get.docker.com}.
     * This will install the latest version, which may be incompatible with the jclouds driver.
     */
    private String installDockerFallback() {
        return chainGroup(INSTALL_CURL, "curl -s https://get.docker.com/ | " + sudo("sh"));
    }

    @Override
    public void customize() {
        Networking.checkPortsValid(getPortMap());

        stop();

        newScript(CUSTOMIZING)
                .failOnNonZeroResultCode()
                .body.append(
                        ifExecutableElse0("apt-get", chainGroup(
                                executeCommandThenAsUserTeeOutputToFile(format("echo 'DOCKER_OPTS=\"-H tcp://0.0.0.0:%s -H unix:///var/run/docker.sock\"'", getDockerPort()), "root", "/etc/default/docker"),
                                sudo("groupadd -f docker"),
                                sudo(format("gpasswd -a %s docker", getMachine().getUser())),
                                sudo("newgrp docker"))),
                        ifExecutableElse0("yum",
                                executeCommandThenAsUserTeeOutputToFile(format("echo 'other_args=\"--selinux-enabled -H tcp://0.0.0.0:%s -H unix:///var/run/docker.sock -e lxc\"'", getDockerPort()), "root", "/etc/sysconfig/docker")))
                .execute();

        // Configure volume mappings for the host
        Map<String, String> mapping = MutableMap.of();
        Map<String, String> volumes = getEntity().getConfig(DockerHost.DOCKER_HOST_VOLUME_MAPPING);
        if (volumes != null) {
            for (String source : volumes.keySet()) {
                if (Urls.isUrlWithProtocol(source)) {
                    String path = deployArchive(source);
                    mapping.put(path, volumes.get(source));
                } else {
                    mapping.put(source, volumes.get(source));
                }
            }
        }
        getEntity().setAttribute(DockerHost.DOCKER_HOST_VOLUME_MAPPING, mapping);
    }

    @Override
    public String deployArchive(String url) {
        String volumeId = Identifiers.makeIdFromHash(url.hashCode());
        String path = Os.mergePaths(getRunDir(), volumeId);
        ArchiveUtils.deploy(url, getMachine(), path);
        return path;
    }

    @Override
    public void configureSecurityGroups() {
        String securityGroup = getEntity().getConfig(DockerInfrastructure.SECURITY_GROUP);
        if (Strings.isBlank(securityGroup)) {
            if (!(getLocation() instanceof JcloudsSshMachineLocation)) {
                log.info("{} not running in a JcloudsSshMachineLocation, not configuring extra security groups", entity);
                return;
            }
            JcloudsSshMachineLocation location = (JcloudsSshMachineLocation) getLocation();
            JcloudsLocationSecurityGroupCustomizer customizer = JcloudsLocationSecurityGroupCustomizer.getInstance(getEntity().getApplicationId());
            Collection<IpPermission> permissions = getIpPermissions(customizer);
            log.debug("Applying custom security groups to {}: {}", location, permissions);
            customizer.addPermissionsToLocation(location, permissions);
        }
    }

    /**
     * @return Extra IP permissions to be configured on this entity's location.
     */
    protected Collection<IpPermission> getIpPermissions(JcloudsLocationSecurityGroupCustomizer customizer) {
        IpPermission dockerPort = IpPermission.builder()
                .ipProtocol(IpProtocol.TCP)
                .fromPort(getEntity().getAttribute(DockerHost.DOCKER_PORT))
                .toPort(getEntity().getAttribute(DockerHost.DOCKER_PORT))
                .cidrBlock(customizer.getBrooklynCidrBlock())
                .build();
        IpPermission dockerSslPort = IpPermission.builder()
                .ipProtocol(IpProtocol.TCP)
                .fromPort(getEntity().getAttribute(DockerHost.DOCKER_SSL_PORT))
                .toPort(getEntity().getAttribute(DockerHost.DOCKER_SSL_PORT))
                .cidrBlock(customizer.getBrooklynCidrBlock())
                .build();
        IpPermission dockerPortForwarding = IpPermission.builder()
                .ipProtocol(IpProtocol.TCP)
                .fromPort(49000)
                .toPort(49900)
                .cidrBlock(Cidr.UNIVERSAL.toString())
                .build();
        List<IpPermission> permissions = MutableList.of(dockerPort, dockerSslPort, dockerPortForwarding);

        if (getEntity().getConfig(DockerInfrastructure.WEAVE_ENABLED)) {
            Integer weavePort = ((DockerHost) getEntity()).getInfrastructure()
                    .getAttribute(DockerInfrastructure.WEAVE_INFRASTRUCTURE)
                    .getConfig(WeaveContainer.WEAVE_PORT);
            IpPermission weaveTcpPort = IpPermission.builder()
                    .ipProtocol(IpProtocol.TCP)
                    .fromPort(weavePort)
                    .toPort(weavePort)
                    .cidrBlock(Cidr.UNIVERSAL.toString()) // TODO could be tighter restricted?
                    .build();
            permissions.add(weaveTcpPort);
            IpPermission weaveUdpPort = IpPermission.builder()
                    .ipProtocol(IpProtocol.UDP)
                    .fromPort(weavePort)
                    .toPort(weavePort)
                    .cidrBlock(Cidr.UNIVERSAL.toString()) // TODO could be tighter restricted?
                    .build();
            permissions.add(weaveUdpPort);
        }

        return permissions;
    }

    public String getPidFile() { return "/var/run/docker.pid"; }

    @Override
    public void launch() {
        newScript(LAUNCHING)
                .body.append(alternatives(
                        ifExecutableElse1("boot2docker", sudo("boot2docker up")),
                        ifExecutableElse1("service", sudo("service docker start"))))
                .failOnNonZeroResultCode()
                .uniqueSshConnection()
                .execute();
    }

}
TOP

Related Classes of brooklyn.entity.container.docker.DockerHostSshDriver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.