brooklyn.entity.container.docker.DockerHostImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of brooklyn-clocker-docker Show documentation
Show all versions of brooklyn-clocker-docker Show documentation
Clocker Brooklyn entities and locations for Docker integration.
/*
* Copyright 2014-2015 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 java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.compute.config.ComputeServiceProperties;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.domain.Volume;
import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
import org.jclouds.softlayer.reference.SoftLayerConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brooklyn.config.ConfigKey;
import brooklyn.config.render.RendererHints;
import brooklyn.enricher.Enrichers;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver;
import brooklyn.entity.basic.DelegateEntity;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityFunctions;
import brooklyn.entity.basic.EntityLocal;
import brooklyn.entity.basic.SoftwareProcess;
import brooklyn.entity.container.DockerAttributes;
import brooklyn.entity.container.DockerUtils;
import brooklyn.entity.group.Cluster;
import brooklyn.entity.group.DynamicCluster;
import brooklyn.entity.machine.MachineEntityImpl;
import brooklyn.entity.nosql.etcd.EtcdNode;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.entity.software.SshEffectorTasks;
import brooklyn.entity.trait.Startable;
import brooklyn.event.feed.ConfigToAttributes;
import brooklyn.event.feed.function.FunctionFeed;
import brooklyn.event.feed.function.FunctionPollConfig;
import brooklyn.location.Location;
import brooklyn.location.LocationDefinition;
import brooklyn.location.MachineProvisioningLocation;
import brooklyn.location.PortRange;
import brooklyn.location.basic.BasicLocationDefinition;
import brooklyn.location.basic.Machines;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.location.docker.DockerHostLocation;
import brooklyn.location.docker.DockerLocation;
import brooklyn.location.docker.DockerResolver;
import brooklyn.location.jclouds.JcloudsLocation;
import brooklyn.location.jclouds.JcloudsLocationConfig;
import brooklyn.location.jclouds.networking.JcloudsLocationSecurityGroupCustomizer;
import brooklyn.location.jclouds.templates.PortableTemplateBuilder;
import brooklyn.management.LocationManager;
import brooklyn.networking.portforwarding.DockerPortForwarder;
import brooklyn.networking.sdn.SdnAgent;
import brooklyn.networking.sdn.SdnAttributes;
import brooklyn.networking.sdn.calico.CalicoNode;
import brooklyn.networking.sdn.ibm.SdnVeNetwork;
import brooklyn.networking.sdn.weave.WeaveNetwork;
import brooklyn.networking.subnet.SubnetTier;
import brooklyn.networking.subnet.SubnetTierImpl;
import brooklyn.policy.PolicySpec;
import brooklyn.policy.ha.ServiceFailureDetector;
import brooklyn.policy.ha.ServiceReplacer;
import brooklyn.policy.ha.ServiceRestarter;
import brooklyn.util.collections.MutableMap;
import brooklyn.util.collections.QuorumCheck.QuorumChecks;
import brooklyn.util.exceptions.Exceptions;
import brooklyn.util.guava.Maybe;
import brooklyn.util.net.Cidr;
import brooklyn.util.ssh.BashCommands;
import brooklyn.util.task.DynamicTasks;
import brooklyn.util.task.Tasks;
import brooklyn.util.task.system.ProcessTaskStub.ScriptReturnType;
import brooklyn.util.task.system.ProcessTaskWrapper;
import brooklyn.util.text.Identifiers;
import brooklyn.util.text.StringPredicates;
import brooklyn.util.text.Strings;
import brooklyn.util.time.Duration;
import com.google.common.base.CharMatcher;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* The host running the Docker service.
*/
public class DockerHostImpl extends MachineEntityImpl implements DockerHost {
private static final Logger LOG = LoggerFactory.getLogger(DockerHost.class);
private transient FunctionFeed scan;
@Override
public void init() {
LOG.info("Starting Docker host id {}", getId());
super.init();
AtomicInteger counter = config().get(DOCKER_INFRASTRUCTURE).getAttribute(DockerInfrastructure.DOCKER_HOST_COUNTER);
String dockerHostName = String.format(config().get(DockerHost.DOCKER_HOST_NAME_FORMAT), getId(), counter.incrementAndGet());
setDisplayName(dockerHostName);
setAttribute(DOCKER_HOST_NAME, dockerHostName);
// Set a password for this host's containers
String password = config().get(DOCKER_PASSWORD);
if (Strings.isBlank(password)) {
password = Identifiers.makeRandomId(8);
config().set(DOCKER_PASSWORD, password);
}
ConfigToAttributes.apply(this, DOCKER_INFRASTRUCTURE);
EntitySpec> dockerContainerSpec = EntitySpec.create(config().get(DOCKER_CONTAINER_SPEC))
.configure(DockerContainer.DOCKER_HOST, this)
.configure(DockerContainer.DOCKER_INFRASTRUCTURE, getInfrastructure());
if (config().get(DockerInfrastructure.HA_POLICY_ENABLE)) {
dockerContainerSpec.policy(PolicySpec.create(ServiceRestarter.class)
.configure(ServiceRestarter.FAILURE_SENSOR_TO_MONITOR, ServiceFailureDetector.ENTITY_FAILED));
}
setAttribute(DOCKER_CONTAINER_SPEC, dockerContainerSpec);
DynamicCluster containers = addChild(EntitySpec.create(DynamicCluster.class)
.configure(Cluster.INITIAL_SIZE, 0)
.configure(DynamicCluster.QUARANTINE_FAILED_ENTITIES, false)
.configure(DynamicCluster.MEMBER_SPEC, dockerContainerSpec)
.configure(DynamicCluster.RUNNING_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
.configure(DynamicCluster.UP_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty())
.displayName("Docker Containers"));
if (config().get(DockerInfrastructure.HA_POLICY_ENABLE)) {
containers.addPolicy(PolicySpec.create(ServiceReplacer.class)
.configure(ServiceReplacer.FAILURE_SENSOR_TO_MONITOR, ServiceRestarter.ENTITY_RESTART_FAILED));
}
setAttribute(DOCKER_CONTAINER_CLUSTER, containers);
if (Entities.isManaged(this)) Entities.manage(containers);
addEnricher(Enrichers.builder()
.propagating(ImmutableMap.of(DynamicCluster.GROUP_SIZE, DockerAttributes.DOCKER_CONTAINER_COUNT))
.from(containers)
.build());
}
@Override
protected Collection getRequiredOpenPorts() {
Collection ports = super.getRequiredOpenPorts();
if (config().get(DockerInfrastructure.SDN_ENABLE)) {
Entity sdn = getAttribute(DockerHost.DOCKER_INFRASTRUCTURE)
.getAttribute(DockerInfrastructure.SDN_PROVIDER);
if (DockerUtils.isSdnProvider(this, "WeaveNetwork")) {
Integer weavePort = sdn.config().get(WeaveNetwork.WEAVE_PORT);
if (weavePort != null) ports.add(weavePort);
}
if (DockerUtils.isSdnProvider(this, "CalicoNetwork")) {
PortRange etcdPort = sdn.config().get(EtcdNode.ETCD_CLIENT_PORT);
if (etcdPort != null) ports.add(etcdPort.iterator().next());
Integer powerstripPort = sdn.config().get(CalicoNode.POWERSTRIP_PORT);
if (powerstripPort != null) ports.add(powerstripPort);
}
}
return ports;
}
@Override
protected Map obtainProvisioningFlags(MachineProvisioningLocation location) {
Map flags = MutableMap.copyOf(super.obtainProvisioningFlags(location));
flags.putAll(config().get(PROVISIONING_FLAGS));
// Configure template for host virtual machine
if (location instanceof JcloudsLocation) {
Set> imageChoiceToRespect = ImmutableSet.>of(
JcloudsLocationConfig.TEMPLATE_BUILDER,
JcloudsLocationConfig.IMAGE_CHOOSER,
JcloudsLocationConfig.IMAGE_ID,
JcloudsLocationConfig.IMAGE_NAME_REGEX,
JcloudsLocationConfig.IMAGE_DESCRIPTION_REGEX,
JcloudsLocationConfig.OS_FAMILY,
JcloudsLocationConfig.OS_VERSION_REGEX,
JcloudsLocationConfig.OS_64_BIT);
Set> hardwareChoiceToRespect = ImmutableSet.>of(
JcloudsLocationConfig.HARDWARE_ID,
JcloudsLocationConfig.MIN_RAM,
JcloudsLocationConfig.MIN_CORES,
JcloudsLocationConfig.MIN_DISK);
Map existingConfigOptions = ((JcloudsLocation) location).config().getBag().getAllConfig();
TemplateBuilder template = (TemplateBuilder) flags.get(JcloudsLocationConfig.TEMPLATE_BUILDER.getName());
boolean overrideImageChoice = true;
for (ConfigKey> key : imageChoiceToRespect) {
if (existingConfigOptions.get(key.getName()) != null || flags.get(key.getName()) != null) {
overrideImageChoice = false;
break;
}
}
boolean overrideHardwareChoice = true;
for (ConfigKey> key : hardwareChoiceToRespect) {
if (existingConfigOptions.get(key.getName()) != null || flags.get(key.getName()) != null) {
overrideHardwareChoice = false;
break;
}
}
if (overrideImageChoice) {
LOG.debug("Customising image choice for {}", this);
template = new PortableTemplateBuilder();
if (isJcloudsLocation(location, "google-compute-engine")) {
template.osFamily(OsFamily.CENTOS).osVersionMatches("6");
} else {
template.osFamily(OsFamily.UBUNTU).osVersionMatches("14.04");
}
template.os64Bit(true);
flags.put(JcloudsLocationConfig.TEMPLATE_BUILDER.getName(), template);
} else {
LOG.debug("Not modifying existing image configuration for {}", this);
}
if (overrideHardwareChoice) {
LOG.debug("Customising hardware choice for {}", this);
if (template != null) {
template.minRam(2048);
} else {
flags.put(JcloudsLocationConfig.MIN_RAM.getName(), 2048);
}
} else {
LOG.debug("Not modifying existing hardware configuration for {}", this);
}
// Configure security groups for host virtual machine
String securityGroup = config().get(DockerInfrastructure.SECURITY_GROUP);
if (Strings.isNonBlank(securityGroup)) {
if (isJcloudsLocation(location, "google-compute-engine")) {
flags.put("networkName", securityGroup);
} else {
flags.put("securityGroups", securityGroup);
}
} else {
flags.put(JcloudsLocationConfig.JCLOUDS_LOCATION_CUSTOMIZERS.getName(),
ImmutableList.of(JcloudsLocationSecurityGroupCustomizer.getInstance(getApplicationId())));
}
// Setup SoftLayer template options required for IBM SDN VE
// TODO Move this into a callback on the SdnProvider interface
if (isJcloudsLocation(location, SoftLayerConstants.SOFTLAYER_PROVIDER_NAME) && DockerUtils.isSdnProvider(this, "SdnVeNetwork")) {
if (template == null) template = new PortableTemplateBuilder();
template.osFamily(OsFamily.CENTOS).osVersionMatches("6").os64Bit(true);
Integer vlanId = getAttribute(DOCKER_INFRASTRUCTURE)
.getAttribute(DockerInfrastructure.SDN_PROVIDER)
.config().get(SdnVeNetwork.VLAN_ID);
template.options(SoftLayerTemplateOptions.Builder
.diskType(Volume.Type.LOCAL.name()) // FIXME Temporary setting overriding capacity limitation on account
.primaryBackendNetworkComponentNetworkVlanId(vlanId)
.portSpeed(1000)); // TODO Make configurable
flags.put(JcloudsLocationConfig.TEMPLATE_BUILDER.getName(), template);
}
}
return flags;
}
private boolean isJcloudsLocation(MachineProvisioningLocation location, String providerName) {
return location instanceof JcloudsLocation
&& ((JcloudsLocation) location).getProvider().equals(providerName);
}
@Override
public Integer resize(Integer desiredSize) {
return getDockerContainerCluster().resize(desiredSize);
}
@Override
public String getShortName() {
return "Docker Host";
}
@Override
public Integer getCurrentSize() {
return getDockerContainerCluster().getCurrentSize();
}
@Override
public Class> getDriverInterface() {
return DockerHostDriver.class;
}
@Override
public DockerHostDriver getDriver() {
return (DockerHostDriver) super.getDriver();
}
@Override
public Integer getDockerPort() {
return getAttribute(DOCKER_SSL_PORT);
}
@Override
public String getDockerHostName() {
return getAttribute(DOCKER_HOST_NAME);
}
@Override
public List getDockerContainerList() {
return ImmutableList.copyOf(getDockerContainerCluster().getMembers());
}
@Override
public DockerInfrastructure getInfrastructure() {
return (DockerInfrastructure) config().get(DOCKER_INFRASTRUCTURE);
}
@Override
public String getPassword() {
return config().get(DOCKER_PASSWORD);
}
/** {@inheritDoc} */
@Override
public String createSshableImage(String dockerFile, String name) {
String imageId = getDriver().buildImage(dockerFile, name);
LOG.debug("Successfully created image {} ({})", new Object[] { imageId, name });
return imageId;
}
@Override
public String layerSshableImageOn(String baseImage, String tag) {
String imageId = getDriver().layerSshableImageOn(baseImage, tag);
LOG.debug("Successfully created SSHable image {} from {}", imageId, baseImage);
return imageId;
}
/** {@inheritDoc} */
@Override
public String runDockerCommand(String command) {
return runDockerCommandTimeout(command, Duration.FIVE_MINUTES);
}
/** {@inheritDoc} */
@Override
public String runDockerCommandTimeout(String command, Duration timeout) {
// FIXME Set DOCKER_OPTS values in command-line for when running on localhost
String stdout = execCommandTimeout(BashCommands.sudo(String.format("docker %s", command)), timeout);
if (LOG.isDebugEnabled()) LOG.debug("Successfully executed Docker {}: {}", Strings.getFirstWord(command), Strings.getFirstLine(stdout));
return stdout;
}
/** {@inheritDoc} */
@Override
public String deployArchive(String url) {
Tasks.setBlockingDetails("Deploy " + url);
try {
return getDriver().deployArchive(url);
} finally {
Tasks.resetBlockingDetails();
}
}
@Override
public DockerHostLocation getDynamicLocation() {
return (DockerHostLocation) getAttribute(DYNAMIC_LOCATION);
}
@Override
public boolean isLocationAvailable() {
return getDynamicLocation() != null;
}
@Override
public DynamicCluster getDockerContainerCluster() { return getAttribute(DOCKER_CONTAINER_CLUSTER); }
@Override
public JcloudsLocation getJcloudsLocation() { return getAttribute(JCLOUDS_DOCKER_LOCATION); }
@Override
public SubnetTier getSubnetTier() { return getAttribute(DOCKER_HOST_SUBNET_TIER); }
@Override
public int execCommandStatus(String command) {
return execCommandStatusTimeout(command, Duration.seconds(15));
}
@Override
public int execCommandStatusTimeout(String command, Duration timeout) {
ProcessTaskWrapper