Skip to content

Commit

Permalink
Merge pull request #100 from brooklyncentral/feature/docker-yaml-serv…
Browse files Browse the repository at this point in the history
…ice-type

Added Docker service type resolver
  • Loading branch information
grkvlt committed Apr 13, 2015
2 parents 854d64f + 381a250 commit c81f3b2
Show file tree
Hide file tree
Showing 21 changed files with 253 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ private DockerAttributes() { }
public static final AttributeSensorAndConfigKey<String, String> DOCKER_HARDWARE_ID = ConfigKeys.newStringSensorAndConfigKey(
"docker.hardwareId", "The ID of a Docker hardware type to use for a container", "small");

public static final AttributeSensorAndConfigKey<String, String> DOCKER_CONTAINER_NAME = ConfigKeys.newStringSensorAndConfigKey(
"docker.container.name", "The name of the Docker container");

public static final ConfigKey<String> DOCKER_PASSWORD = ConfigKeys.newConfigKeyWithPrefix("docker.", SshTool.PROP_PASSWORD);

public static final ConfigKey<Boolean> DOCKER_USE_HOST_DNS_NAME = ConfigKeys.newBooleanConfigKey(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ public static String checkId(String input) {
}
}

public static String imageName(Entity entity, String dockerfile, String repository) {
public static String imageName(Entity entity, String dockerfile) {
String simpleName = entity.getEntityType().getSimpleName();
String version = entity.config().get(SoftwareProcess.SUGGESTED_VERSION);

String label = Joiner.on(":").skipNulls().join(simpleName, version, dockerfile, repository);
String label = Joiner.on(":").skipNulls().join(simpleName, version, dockerfile);
return Identifiers.makeIdFromHash(Hashing.md5().hashString(label, Charsets.UTF_8).asLong()).toLowerCase(Locale.ENGLISH);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import brooklyn.entity.basic.MethodEffector;
import brooklyn.entity.basic.SoftwareProcess;
import brooklyn.entity.container.DockerAttributes;
import brooklyn.entity.container.DockerUtils;
import brooklyn.entity.proxying.ImplementedBy;
import brooklyn.entity.trait.HasShortName;
import brooklyn.event.AttributeSensor;
Expand Down Expand Up @@ -59,6 +58,9 @@ public interface DockerContainer extends BasicStartable, HasNetworkAddresses, Ha
@SetFromFlag("infrastructure")
AttributeSensorAndConfigKey<Entity, Entity> DOCKER_INFRASTRUCTURE = DockerHost.DOCKER_INFRASTRUCTURE;

@SetFromFlag("containerName")
AttributeSensorAndConfigKey<String, String> DOCKER_CONTAINER_NAME = DockerAttributes.DOCKER_CONTAINER_NAME;

@SetFromFlag("managed")
ConfigKey<Boolean> MANAGED = DockerAttributes.MANAGED;

Expand Down Expand Up @@ -98,10 +100,8 @@ public interface DockerContainer extends BasicStartable, HasNetworkAddresses, Ha
AttributeSensorAndConfigKey<Entity, Entity> ENTITY = ConfigKeys.newSensorAndConfigKey(Entity.class,
"docker.container.entity", "The entity running in this Docker container");

ConfigKey<String> DOCKER_CONTAINER_NAME_FORMAT = ConfigKeys.newStringConfigKey("docker.container.nameFormat",
"Format for generating Docker container names", DockerUtils.DEFAULT_DOCKER_CONTAINER_NAME_FORMAT);

AttributeSensor<String> DOCKER_CONTAINER_NAME = Sensors.newStringSensor("docker.container.name", "The name of the Docker container");
ConfigKey<String> DOCKER_CONTAINER_NAME_FORMAT = ConfigKeys.newStringConfigKey(
"docker.container.nameFormat", "Format for generating Docker container names");

AttributeSensor<String> IMAGE_ID = Sensors.newStringSensor("docker.container.image.id", "The Docker container image ID");
AttributeSensor<String> IMAGE_NAME = Sensors.newStringSensor("docker.container.image.name", "The Docker container image name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import brooklyn.entity.basic.ServiceStateLogic;
import brooklyn.entity.basic.SoftwareProcess;
import brooklyn.entity.container.DockerAttributes;
import brooklyn.entity.container.DockerUtils;
import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
import brooklyn.event.basic.Sensors;
import brooklyn.event.feed.ConfigToAttributes;
Expand Down Expand Up @@ -84,8 +85,11 @@
import brooklyn.util.text.Strings;
import brooklyn.util.time.Duration;

import com.google.common.base.CaseFormat;
import com.google.common.base.CharMatcher;
import com.google.common.base.Functions;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;

Expand All @@ -104,20 +108,38 @@ public void init() {
super.init();

AtomicInteger counter = config().get(DOCKER_INFRASTRUCTURE).getAttribute(DockerInfrastructure.DOCKER_CONTAINER_COUNTER);
String dockerContainerName = format(config().get(DockerContainer.DOCKER_CONTAINER_NAME_FORMAT), getId(), counter.incrementAndGet());
setDisplayName(dockerContainerName);
setAttribute(DOCKER_CONTAINER_NAME, dockerContainerName);
String dockerContainerName = config().get(DOCKER_CONTAINER_NAME);
String dockerContainerNameFormat = config().get(DOCKER_CONTAINER_NAME_FORMAT);
if (Strings.isBlank(dockerContainerName) && Strings.isNonBlank(dockerContainerNameFormat)) {
dockerContainerName = format(dockerContainerNameFormat, getId(), counter.incrementAndGet());
}
if (Strings.isNonBlank(dockerContainerName)) {
dockerContainerName = CharMatcher.BREAKING_WHITESPACE.trimAndCollapseFrom(dockerContainerName, '-');
setDisplayName(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, dockerContainerName));
setAttribute(DOCKER_CONTAINER_NAME, dockerContainerName);
}

ConfigToAttributes.apply(this, DOCKER_INFRASTRUCTURE);
ConfigToAttributes.apply(this, DOCKER_HOST);
ConfigToAttributes.apply(this, ENTITY);
}

protected void connectSensors() {
// is this swappable for a feed that runs one command rather than three?
status = FunctionFeed.builder()
.entity(this)
.period(Duration.seconds(15))
.poll(new FunctionPollConfig<String, String>(DOCKER_CONTAINER_NAME)
.period(Duration.minutes(1))
.callable(new Callable<String>() {
@Override
public String call() throws Exception {
String containerId = getContainerId();
if (containerId == null) return "";
String name = getDockerHost().runDockerCommand("inspect -f {{.Name}} " + containerId);
return Strings.removeFromStart(name, "/");
}
})
.onFailureOrException(Functions.constant("")))
.poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_UP)
.callable(new Callable<Boolean>() {
@Override
Expand Down Expand Up @@ -397,6 +419,15 @@ public DockerContainerLocation createLocation(Map flags) {
// Configure the container options based on the host and the running entity
DockerTemplateOptions options = getDockerTemplateOptions();

// Check the running entity for alternative container name
String containerName = getRunningEntity().config().get(DOCKER_CONTAINER_NAME);
if (Strings.isBlank(containerName)) {
containerName = getAttribute(DOCKER_CONTAINER_NAME);
}
if (Strings.isNonBlank(containerName)) {
options.nodeNames(ImmutableList.of(DockerUtils.allowed(containerName)));
}

// put these fields on the location so it has the info it needs to create the subnet
Map<?, ?> dockerFlags = MutableMap.<Object, Object>builder()
.put(JcloudsLocationConfig.TEMPLATE_BUILDER, new PortableTemplateBuilder().options(options))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,6 @@ public interface DockerHost extends MachineEntity, Resizable, HasShortName, Loca
ConfigKey<String> DOCKER_HOST_NAME_FORMAT = ConfigKeys.newStringConfigKey("docker.host.nameFormat",
"Format for generating Docker host names", DockerUtils.DEFAULT_DOCKER_HOST_NAME_FORMAT);

@SetFromFlag("repository")
AttributeSensorAndConfigKey<String, String> DOCKER_REPOSITORY = ConfigKeys.newStringSensorAndConfigKey("docker.repository",
"The name of the Docker repository for images");

ConfigKey<String> EPEL_RELEASE = ConfigKeys.newStringConfigKey("docker.host.epel.release",
"EPEL release for yum based OS", "6-8");

Expand Down Expand Up @@ -136,8 +132,6 @@ public interface DockerHost extends MachineEntity, Resizable, HasShortName, Loca
AttributeSensor<SubnetTier> DOCKER_HOST_SUBNET_TIER = Sensors.newSensor(SubnetTier.class,
"docker.subnetTier", "The SubnetTier for Docker port mapping");

String getRepository();

String getPassword();

Integer getDockerPort();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ public interface DockerHostDriver extends SoftwareProcessDriver {

Integer getDockerPort();

String getRepository();

/**
* Build a Docker image from the given Dockerfile.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,6 @@ public void init() {
String dockerHostName = String.format(getConfig(DockerHost.DOCKER_HOST_NAME_FORMAT), getId(), counter.incrementAndGet());
setDisplayName(dockerHostName);
setAttribute(DOCKER_HOST_NAME, dockerHostName);
String repository = getConfig(DOCKER_REPOSITORY);
if (Strings.isBlank(repository)) {
repository = getId();
}
repository = DockerUtils.allowed(repository);
setAttribute(DOCKER_REPOSITORY, repository);

// Set a password for this host's containers
String password = config().get(DOCKER_PASSWORD);
Expand Down Expand Up @@ -322,16 +316,11 @@ public String getPassword() {
return config().get(DOCKER_PASSWORD);
}

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

/** {@inheritDoc} */
@Override
public String createSshableImage(String dockerFile, String name) {
String imageId = getDriver().buildImage(dockerFile, name);
LOG.debug("Successfully created image {} ({}/{})", new Object[] { imageId, getRepository(), name });
LOG.debug("Successfully created image {} ({})", new Object[] { imageId, name });
return imageId;
}

Expand Down Expand Up @@ -488,7 +477,6 @@ protected void preStart() {
.put("machine", found.get())
.put("jcloudsLocation", jcloudsLocation)
.put("portForwarder", portForwarder)
.put("repository", getRepository())
.build();
createLocation(flags);
}
Expand All @@ -508,7 +496,7 @@ public void postStart() {

if (Strings.isBlank(imageId)) {
String dockerfileUrl = config().get(DockerInfrastructure.DOCKERFILE_URL);
String imageName = DockerUtils.imageName(this, dockerfileUrl, getRepository());
String imageName = DockerUtils.imageName(this, dockerfileUrl);
imageId = createSshableImage(dockerfileUrl, imageName);
setAttribute(DOCKER_IMAGE_NAME, imageName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ public Integer getDockerPort() {
return getEntity().getAttribute(DockerHost.DOCKER_SSL_PORT);
}

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

/** {@inheritDoc} */
@Override
public String buildImage(String dockerFile, String name) {
Expand Down Expand Up @@ -149,25 +144,23 @@ public String layerSshableImageOn(String name, String tag) {
checkNotNull(name, "name");
checkNotNull(tag, "tag");
copyTemplate(DockerUtils.SSHD_DOCKERFILE, Os.mergePaths(name, "Sshd" + DockerUtils.DOCKERFILE),
true, ImmutableMap.<String, Object>of("fullyQualifiedImageName", Os.mergePaths(name) + ":" + tag));
true, ImmutableMap.<String, Object>of("fullyQualifiedImageName", name + ":" + tag));
String sshdImageId = buildDockerfile("Sshd" + DockerUtils.DOCKERFILE, name);
log.info("Created SSH-based image from {} with ID {}", name, sshdImageId);

return sshdImageId;
}

private Map<String, Object> getExtraTemplateSubstitutions(String imageName) {
Map<String, Object> templateSubstitutions = MutableMap.<String, Object>of(
"fullyQualifiedImageName", Os.mergePaths(getRepository(), imageName));
Map<String, Object> templateSubstitutions = MutableMap.<String, Object>of("fullyQualifiedImageName", imageName);
DockerHost host = (DockerHost) getEntity();
templateSubstitutions.putAll(host.getInfrastructure().config().get(DockerInfrastructure.DOCKERFILE_SUBSTITUTIONS));
return templateSubstitutions;
}

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

Expand All @@ -176,8 +169,7 @@ private String buildDockerfileDirectory(String 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));
name, Os.mergePaths(getRunDir(), name, dockerfile));
String stdout = ((DockerHost) getEntity()).runDockerCommandTimeout(build, Duration.minutes(20));
String prefix = Strings.getFirstWordAfter(stdout, "Successfully built");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import brooklyn.entity.group.DynamicMultiGroup;
import brooklyn.entity.machine.MachineAttributes;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.entity.trait.Resizable;
import brooklyn.entity.trait.Startable;
import brooklyn.location.Location;
import brooklyn.location.LocationDefinition;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 brooklyn.entity.container.docker.application;

import io.brooklyn.camp.brooklyn.spi.creation.BrooklynComponentTemplateResolver;
import io.brooklyn.camp.brooklyn.spi.creation.service.BrooklynServiceTypeResolver;
import io.brooklyn.camp.brooklyn.spi.creation.service.ServiceTypeResolver;
import io.brooklyn.camp.spi.PlatformComponentTemplate;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import brooklyn.catalog.CatalogItem;
import brooklyn.entity.Entity;
import brooklyn.entity.container.DockerAttributes;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.util.text.Strings;

import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;

/**
* This converts {@link PlatformComponentTemplate} instances whose type is prefixed {@code docker:}
* to Brooklyn {@link EntitySpec} instances.
*/
public class DockerServiceTypeResolver extends BrooklynServiceTypeResolver {

private static final Logger log = LoggerFactory.getLogger(ServiceTypeResolver.class);

public static final String PREFIX = "docker";

@Override
public String getTypePrefix() { return PREFIX; }

@Override
public String getBrooklynType(String serviceType) {
return VanillaDockerApplication.class.getName();
}

/** Docker items are not in catalog. */
@Override
public CatalogItem<Entity, EntitySpec<?>> getCatalogItem(BrooklynComponentTemplateResolver resolver, String serviceType) {
return null;
}

@Override
public <T extends Entity> void decorateSpec(BrooklynComponentTemplateResolver resolver, EntitySpec<T> spec) {
String dockerServiceType = Strings.removeFromStart(resolver.getDeclaredType(), PREFIX + ":");
List<String> parts = Splitter.on(":").splitToList(dockerServiceType);
if (parts.isEmpty() || parts.size() > 2) {
throw new IllegalArgumentException("Docker serviceType cannot be parsed: " + dockerServiceType);
}
String imageName = Iterables.get(parts, 0);
String imageTag = Iterables.get(parts, 1, "latest");
log.debug("Creating Docker service entity with image {} and tag {}", imageName, imageTag);
spec.configure(DockerAttributes.DOCKER_IMAGE_NAME, imageName);
if (parts.size() == 2) {
spec.configure(DockerAttributes.DOCKER_IMAGE_TAG, imageTag);
}
if (resolver.getAttrs().containsKey("id")) {
String containerName = (String) resolver.getAttrs().getStringKey("id");
spec.configure(DockerAttributes.DOCKER_CONTAINER_NAME, containerName);
}
super.decorateSpec(resolver, spec);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public interface VanillaDockerApplication extends VanillaSoftwareProcess {
@SetFromFlag("dockerfileUrl")
ConfigKey<String> DOCKERFILE_URL = DockerAttributes.DOCKERFILE_URL;

@SetFromFlag("containerName")
ConfigKey<String> CONTAINER_NAME = DockerContainer.DOCKER_CONTAINER_NAME.getConfigKey();

@SetFromFlag("imageName")
ConfigKey<String> IMAGE_NAME = DockerAttributes.DOCKER_IMAGE_NAME.getConfigKey();

Expand Down
Loading

0 comments on commit c81f3b2

Please sign in to comment.