no_entry: Move docker classes to new location Signed-off-by: Andrew Donald Kennedy <[email protected]>
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/e890c003 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/e890c003 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/e890c003 Branch: refs/heads/master Commit: e890c0037068423e573cd77d1ffae30ebe350d47 Parents: 2a1b1fa Author: CloudsoftOps <[email protected]> Authored: Thu May 18 17:42:53 2017 +0100 Committer: Andrew Donald Kennedy <[email protected]> Committed: Fri May 19 14:03:37 2017 +0100 ---------------------------------------------------------------------- .../entity/docker/DockerContainer.java | 73 ++++++ .../entity/docker/DockerContainerImpl.java | 71 ++++++ .../location/docker/DockerJcloudsLocation.java | 185 ++++++++++++++ .../location/docker/DockerLocationResolver.java | 71 ++++++ .../docker/DockerJcloudsLocationLiveTest.java | 252 +++++++++++++++++++ .../docker/DockerLocationResolverTest.java | 100 ++++++++ .../dockercontainer/DockerContainer.java | 73 ------ .../dockercontainer/DockerContainerImpl.java | 71 ------ .../dockerlocation/DockerJcloudsLocation.java | 185 -------------- .../dockerlocation/DockerLocationResolver.java | 71 ------ .../DockerJcloudsLocationLiveTest.java | 252 ------------------- .../DockerLocationResolverTest.java | 100 -------- 12 files changed, 752 insertions(+), 752 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java new file mode 100644 index 0000000..ef012c3 --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java @@ -0,0 +1,73 @@ +package io.cloudsoft.amp.containerservice.dockercontainer; + +import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.BasicConfigInheritance; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.config.MapConfigKey; +import org.apache.brooklyn.entity.software.base.SoftwareProcess; + +import com.google.common.collect.ImmutableMap; +import com.google.common.reflect.TypeToken; + +/** + * The DockerContainer type is for easily deploying any docker image from the + * image repository set on the target swarm or docker-engine based location + * <p> + * Example YAML is shown below. Note the different types of the {@code env} + * key in the location config and the {@code docker.container.environment} + * key on the entity. The entity environment variables will override any + * environment configured on the location. To specify environment variables + * that will be set when executing SSH commands against the container you + * should use the {@link SoftwareProcess#SHELL_ENVIRONMENT shell.env} key. + * <p> + * <pre>{@code location: + * docker: + * endpoint: "https://52.29.59.193:3376" + * identity: "~/.certs/cert.pem" + * credential: "~/.certs/key.pem" + * templateOptions: + * networkMode: "brooklyn" + * env: + * - "HTTP_CONFIG_ROOT=/var/httpd" + * - "USE_DEFAULTS=true" + * services: + * - type: io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer + * brooklyn.config: + * docker.container.imageName: "apache/httpd:latest" + * docker.container.disableSsh: true + * docker.container.inboundPorts: + * - "8080-8081" + * docker.container.environment: + * ENABLE_JMX: false + * ENABLE_SHUTDOWN: false + * }</pre> + */ +@ImplementedBy(DockerContainerImpl.class) +public interface DockerContainer extends SoftwareProcess { + + ConfigKey<Boolean> DISABLE_SSH = + ConfigKeys.newBooleanConfigKey( + "docker.container.disableSsh", + "Skip checks such as ssh for when docker image doesn't allow ssh", + Boolean.TRUE); + + ConfigKey<String> IMAGE_NAME = + ConfigKeys.newStringConfigKey( + "docker.container.imageName", + "Image name to pull from docker hub"); + + @SuppressWarnings("serial") + ConfigKey<Iterable<String>> INBOUND_TCP_PORTS = + ConfigKeys.newConfigKey( + new TypeToken<Iterable<String>>() {}, + "docker.container.inboundPorts", + "List of ports, that the docker image opens, to be made public"); + + MapConfigKey<Object> CONTAINER_ENVIRONMENT = new MapConfigKey.Builder<Object>(Object.class, "docker.container.environment") + .description("Environment variables to set on container startup") + .defaultValue(ImmutableMap.<String, Object>of()) + .typeInheritance(BasicConfigInheritance.DEEP_MERGE) + .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED_ELSE_DEEP_MERGE) + .build(); +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java new file mode 100644 index 0000000..790985f --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java @@ -0,0 +1,71 @@ +package io.cloudsoft.amp.containerservice.dockercontainer; + +import java.util.Iterator; +import java.util.List; + +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.api.sensor.EnricherSpec; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.location.PortRanges; +import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; +import org.apache.brooklyn.core.sensor.Sensors; +import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl; +import org.apache.brooklyn.util.collections.MutableList; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; + +public class DockerContainerImpl extends EmptySoftwareProcessImpl implements DockerContainer { + + @Override + public void init() { + super.init(); + + String imageName = config().get(DockerContainer.IMAGE_NAME); + if (!Strings.isNullOrEmpty(imageName)) { + config().set(PROVISIONING_PROPERTIES.subKey("imageId"), imageName); + } + + if (Boolean.TRUE.equals(config().get(DockerContainer.DISABLE_SSH))) { + config().set(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true); + config().set(PROVISIONING_PROPERTIES.subKey("useJcloudsSshInit"), false); + config().set(PROVISIONING_PROPERTIES.subKey("waitForSshable"), false); + config().set(PROVISIONING_PROPERTIES.subKey("pollForFirstReachableAddress"), false); + config().set(EmptySoftwareProcessImpl.USE_SSH_MONITORING, false); + } + + ImmutableSet.Builder<AttributeSensor<Integer>> builder = ImmutableSet.builder(); + List<String> portRanges = MutableList.copyOf(config().get(DockerContainer.INBOUND_TCP_PORTS)); + for (String portRange : portRanges) { + Iterator<Integer> iterator = PortRanges.fromString(portRange).iterator(); + while (iterator.hasNext()) { + Integer port = iterator.next(); + AttributeSensor<Integer> element = Sensors.newIntegerSensor("docker.port." + port); + sensors().set(element, port); + builder.add(element); + } + } + + enrichers().add(EnricherSpec.create(OnPublicNetworkEnricher.class).configure(OnPublicNetworkEnricher.SENSORS, builder.build())); + } + + @Override + protected void disconnectSensors() { + if(isSshMonitoringEnabled()) { + disconnectServiceUpIsRunning(); + } + super.disconnectSensors(); + } + + @Override + protected void connectSensors() { + super.connectSensors(); + if (isSshMonitoringEnabled()) { + connectServiceUpIsRunning(); + } else { + sensors().set(Attributes.SERVICE_UP, true); + } + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java new file mode 100644 index 0000000..ecf3214 --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java @@ -0,0 +1,185 @@ +package io.cloudsoft.amp.containerservice.dockerlocation; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.api.location.NoMachinesAvailableException; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.text.Identifiers; +import org.apache.brooklyn.util.text.Strings; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.domain.Template; +import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Functions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; + +import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer; + +/** + * For provisioning docker containers, using the jclouds-docker integration. + * + * This adds special support for default Cloudsoft images. If the image description matches our + * cloudsoft regexes, then auto-generate a password and pass that in as + * {@code CLOUDSOFT_ROOT_PASSWORD} when launching the container. That will then be used as the + * {@link DockerTemplateOptions#getLoginPassword()}. + * + * Also, if no image is specified then this will set the default to "cloudsoft/centos:7" + * (see https://hub.docker.com/r/cloudsoft/centos/). + */ +public class DockerJcloudsLocation extends JcloudsLocation { + + private static final Logger LOG = LoggerFactory.getLogger(DockerJcloudsLocation.class); + + public static final ConfigKey<Boolean> INJECT_LOGIN_CREDENTIAL = ConfigKeys.newBooleanConfigKey( + "injectLoginCredential", + "Whether to inject login credentials (if null, will infer from image choice)", + null); + + public static final ConfigKey<String> DEFAULT_IMAGE_DESCRIPTION_REGEX = ConfigKeys.newStringConfigKey( + "defaultImageDescriptionRegex", + "The default image description to use, if no other image preferences are supplied", + "cloudsoft/centos:7"); + + /** + * The regex for the image descriptions that support us injecting login credentials. + */ + private static final List<String> IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS = ImmutableList.of( + "cloudsoft/centos.*", + "cloudsoft/ubuntu.*"); + + private static final List<ImageMetadata> DEFAULT_IMAGES = ImmutableList.of( + new ImageMetadata(OsFamily.CENTOS, "7", "cloudsoft/centos:7"), + new ImageMetadata(OsFamily.UBUNTU, "14.04", "cloudsoft/ubuntu:14.04"), + new ImageMetadata(OsFamily.UBUNTU, "16.04", "cloudsoft/ubuntu:16.04")); + + private static class ImageMetadata { + private final OsFamily osFamily; + private final String osVersion; + private final String imageDescription; + + public ImageMetadata(OsFamily osFamily, String osVersion, String imageDescription) { + this.osFamily = checkNotNull(osFamily, "osFamily"); + this.osVersion = checkNotNull(osVersion, "osVersion"); + this.imageDescription = checkNotNull(imageDescription, "imageDescription"); + } + + public boolean matches(@Nullable OsFamily osFamily, @Nullable String osVersionRegex) { + if (osFamily != null && osFamily != this.osFamily) return false; + if (osVersionRegex != null && !osVersion.matches(osVersionRegex)) return false; + return true; + } + + public String getImageDescription() { + return imageDescription; + } + } + + @Override + protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailableException { + // Use the provider name that jclouds expects; rely on resolver to have validated this. + setup.configure(JcloudsLocation.CLOUD_PROVIDER, "docker"); + + // Inject default image, if absent + String imageId = setup.get(JcloudsLocation.IMAGE_ID); + String imageNameRegex = setup.get(JcloudsLocation.IMAGE_NAME_REGEX); + String imageDescriptionRegex = setup.get(JcloudsLocation.IMAGE_DESCRIPTION_REGEX); + String defaultImageDescriptionRegex = setup.get(DEFAULT_IMAGE_DESCRIPTION_REGEX); + OsFamily osFamily = setup.get(OS_FAMILY); + String osVersionRegex = setup.get(OS_VERSION_REGEX); + + if (Strings.isBlank(imageId) && Strings.isBlank(imageNameRegex) && Strings.isBlank(imageDescriptionRegex)) { + if (osFamily != null || osVersionRegex != null) { + for (ImageMetadata imageMetadata : DEFAULT_IMAGES) { + if (imageMetadata.matches(osFamily, osVersionRegex)) { + String imageDescription = imageMetadata.getImageDescription(); + LOG.debug("Setting default image regex to {}, for obtain call in {}; removing osFamily={} and osVersionRegex={}", + new Object[] {imageDescription, this, osFamily, osVersionRegex}); + setup.configure(JcloudsLocation.IMAGE_DESCRIPTION_REGEX, imageDescription); + setup.configure(OS_FAMILY, null); + setup.configure(OS_VERSION_REGEX, null); + break; + } + } + } else if (Strings.isNonBlank(defaultImageDescriptionRegex)) { + LOG.debug("Setting default image regex to {}, for obtain call in {}", defaultImageDescriptionRegex, this); + setup.configure(JcloudsLocation.IMAGE_DESCRIPTION_REGEX, defaultImageDescriptionRegex); + } + } + + return super.obtainOnce(setup); + } + + @Override + public Template buildTemplate(ComputeService computeService, ConfigBag config, Collection<JcloudsLocationCustomizer> customizers) { + String loginUser = config.get(JcloudsLocation.LOGIN_USER); + String loginPassword = config.get(JcloudsLocation.LOGIN_USER_PASSWORD); + String loginKeyFile = config.get(JcloudsLocation.LOGIN_USER_PRIVATE_KEY_FILE); + String loginKeyData = config.get(JcloudsLocation.LOGIN_USER_PRIVATE_KEY_DATA); + + Template template = super.buildTemplate(computeService, config, customizers); + DockerTemplateOptions templateOptions = (DockerTemplateOptions) template.getOptions(); + Image image = template.getImage(); + List<String> env = MutableList.copyOf(templateOptions.getEnv()); + + // Inject login credentials, if required + Boolean injectLoginCredentials = config.get(INJECT_LOGIN_CREDENTIAL); + if (injectLoginCredentials == null) { + String imageDescription = image.getDescription(); + for (String regex : IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS) { + if (imageDescription != null && imageDescription.matches(regex)) { + injectLoginCredentials = true; + break; + } + } + } + if (Strings.isBlank(loginUser) && Strings.isBlank(loginPassword) && Strings.isBlank(loginKeyFile) && Strings.isBlank(loginKeyData)) { + if (Boolean.TRUE.equals(injectLoginCredentials)) { + loginUser = "root"; + loginPassword = Identifiers.makeRandomPassword(12); + templateOptions.overrideLoginUser(loginUser); + templateOptions.overrideLoginPassword(loginPassword); + + env.add("CLOUDSOFT_ROOT_PASSWORD="+loginPassword); + } + } + + Entity context = validateCallerContext(config); + Map<String,Object> containerEnv = MutableMap.copyOf(context.config().get(DockerContainer.CONTAINER_ENVIRONMENT)); + for (Map.Entry<String,String> entry : Maps.transformValues(containerEnv, Functions.toStringFunction()).entrySet()) { + env.add(String.format("%s=%s", entry.getKey(), entry.getValue())); + } + templateOptions.env(env); + + return template; + } + + + private Entity validateCallerContext(ConfigBag setup) { + // Lookup entity flags + Object callerContext = setup.get(LocationConfigKeys.CALLER_CONTEXT); + if (callerContext == null || !(callerContext instanceof Entity)) { + throw new IllegalStateException("Invalid caller context: " + callerContext); + } + return (Entity) callerContext; + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java new file mode 100644 index 0000000..d6c8c99 --- /dev/null +++ b/brooklyn-server/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java @@ -0,0 +1,71 @@ +package io.cloudsoft.amp.containerservice.dockerlocation; + +import java.util.Map; + +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.LocationResolver; +import org.apache.brooklyn.core.location.AbstractLocationResolver; +import org.apache.brooklyn.core.location.LocationConfigUtils; +import org.apache.brooklyn.core.location.LocationPropertiesFromBrooklynProperties; +import org.apache.brooklyn.location.jclouds.JcloudsPropertiesFromBrooklynProperties; +import org.apache.brooklyn.util.collections.MutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Locations starting with the given prefix (@code "docker") will use this resolver, to instantiate + * a {@link DockerJcloudsLocation}. + * + * We ensure that config will be picked up from brooklyn.properties using the appropriate precedence: + * <ol> + * <li>named location config + * <li>Prefix {@code brooklyn.location.docker.} + * <li>Prefix {@code brooklyn.jclouds.docker.} + * <li>Prefix {@code brooklyn.jclouds.} + * </ol> + */ +public class DockerLocationResolver extends AbstractLocationResolver implements LocationResolver { + + public static final Logger log = LoggerFactory.getLogger(DockerLocationResolver.class); + + public static final String PREFIX = "docker"; + + @Override + public boolean isEnabled() { + return LocationConfigUtils.isResolverPrefixEnabled(managementContext, getPrefix()); + } + + @Override + public String getPrefix() { + return PREFIX; + } + + @Override + protected Class<? extends Location> getLocationType() { + return DockerJcloudsLocation.class; + } + + @Override + protected SpecParser getSpecParser() { + return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"docker\""); + } + + @Override + protected Map<String, Object> getFilteredLocationProperties(String provider, String namedLocation, Map<String, ?> prioritisedProperties, Map<String, ?> globalProperties) { + Map<String, Object> dockerConf = new LocationPropertiesFromBrooklynProperties().getLocationProperties(getPrefix(), namedLocation, globalProperties); + + Object providerInConf = dockerConf.get("provider"); + if (providerInConf != null && !provider.equals(providerInConf)) { + throw new IllegalArgumentException(provider+" location configured with provider '"+providerInConf+"', but must be blank or '"+provider+"'"); + } + + String providerOrApi = "docker"; + String regionName = (String) prioritisedProperties.get("region"); + Map<String, Object> jcloudsConf = new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties(providerOrApi, regionName, namedLocation, globalProperties); + return MutableMap.<String, Object>builder() + .putAll(jcloudsConf) + .putAll(dockerConf) + .put("provider", providerOrApi) + .build(); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocationLiveTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocationLiveTest.java new file mode 100644 index 0000000..eb64173 --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocationLiveTest.java @@ -0,0 +1,252 @@ +package io.cloudsoft.amp.containerservice.dockerlocation; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.location.MachineLocation; +import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; +import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig; +import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; +import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.os.Os; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.docker.compute.options.DockerTemplateOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +/** + * TODO For these tests to pass, they need on the classpath the patch file(s) from AMP. + * + * Assumes that a pre-existing swarm endpoint is available. See system properties and the defaults + * below. + */ +public class DockerJcloudsLocationLiveTest extends BrooklynAppLiveTestSupport { + + private static final Logger LOG = LoggerFactory.getLogger(DockerJcloudsLocationLiveTest.class); + + private static final String SWARM_ENDPOINT = System.getProperty("test.amp.docker.swarmEndpoint", "https://10.104.0.162:3376/"); + private static final String IDENTITY_FILE_PATH = System.getProperty("test.amp.docker.identity", Os.tidyPath("~/.docker/.certs/cert.pem")); + private static final String CREDENTIAL_FILE_PATH = System.getProperty("test.amp.docker.credential", Os.tidyPath("~/.docker/.certs/key.pem")); + private static final String SWARM_NETWORK_NAME = System.getProperty("test.amp.docker.networkName", Os.tidyPath("brooklyn")); + + protected DockerJcloudsLocation loc; + protected List<MachineLocation> machines; + protected DockerTemplateOptions templateOptions; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + machines = Lists.newCopyOnWriteArrayList(); + } + + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + for (MachineLocation machine : machines) { + try { + loc.release(machine); + } catch (Exception e) { + LOG.error("Error releasing machine "+machine+" in location "+loc, e); + } + } + super.tearDown(); + } + + protected DockerJcloudsLocation newDockerLocation(Map<String, ?> flags) throws Exception { + JcloudsLocationCustomizer locationCustomizer = new BasicJcloudsLocationCustomizer() { + @Override + public void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions) { + DockerJcloudsLocationLiveTest.this.templateOptions = (DockerTemplateOptions) templateOptions; + } + }; + Map<String, ?> templateOptionsOverrides = (Map<String, ?>) flags.get(JcloudsLocation.TEMPLATE_OPTIONS.getName()); + Map<String,?> templateOptions = MutableMap.<String, Object>builder() + .put("networkMode", SWARM_NETWORK_NAME) + .putAll(templateOptionsOverrides != null ? templateOptionsOverrides : ImmutableMap.<String, Object>of()) + .build(); + Map<String,?> allFlags = MutableMap.<String,Object>builder() + .put("identity", IDENTITY_FILE_PATH) + .put("credential", CREDENTIAL_FILE_PATH) + .put("endpoint", SWARM_ENDPOINT) + .put("tags", ImmutableList.of(getClass().getName())) + .put(JcloudsLocation.WAIT_FOR_SSHABLE.getName(), false) + .put(JcloudsLocation.JCLOUDS_LOCATION_CUSTOMIZERS.getName(), ImmutableList.of(locationCustomizer)) + .putAll(flags) + .put(JcloudsLocation.TEMPLATE_OPTIONS.getName(), templateOptions) + .build(); + return (DockerJcloudsLocation) mgmt.getLocationRegistry().getLocationManaged("docker", allFlags); + } + + private JcloudsSshMachineLocation newDockerMachine(DockerJcloudsLocation loc, Map<?, ?> flags) throws Exception { + MachineLocation result = loc.obtain(flags); + machines.add(result); + return (JcloudsSshMachineLocation) result; + } + + @Test(groups={"Live", "Live-sanity"}) + public void testDefaultImageHasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + + assertMachineSshableSecureAndFromImage(machine, "cloudsoft/centos:7"); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testExplicitCredentialsNotOverwritten() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, MutableMap.of( + JcloudsLocationConfig.LOGIN_USER, "myuser", + JcloudsLocationConfig.LOGIN_USER_PASSWORD, "mypassword")); + Image image = getOptionalImage(machine).get(); + assertEquals(image.getDescription(), "cloudsoft/centos:7"); + assertEquals(templateOptions.getLoginUser(), "myuser"); + assertEquals(templateOptions.getLoginPassword(), "mypassword"); + assertEquals(templateOptions.getLoginPassword(), "mypassword"); + assertEnvNotContainsKey(templateOptions, "CLOUDSOFT_ROOT_PASSWORD"); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testExplicitImageIdNotOverwritten() throws Exception { + // TODO This id will likely change sometimes; once CCS-29 is done, then use an image name. + // Assumes we have executed: + // docker run ubuntu /bin/echo 'Hello world' + // which will have downloaded the ubuntu image with the given id. + String imageId = "sha256:2fa927b5cdd31cdec0027ff4f45ef4343795c7a2d19a9af4f32425132a222330"; + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, MutableMap.of( + JcloudsLocation.IMAGE_ID, imageId, + JcloudsLocation.TEMPLATE_OPTIONS, ImmutableMap.of( + "entrypoint", ImmutableList.of("/bin/sleep", "1000")))); + Image image = getOptionalImage(machine).get(); + assertEquals(image.getId(), imageId); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testMatchingImageDescriptionHasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( + JcloudsLocation.IMAGE_DESCRIPTION_REGEX.getName(), "cloudsoft/centos:7", + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + + assertTrue(machine.isSshable(), "machine="+machine); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testMatchingOsFamilyCentosHasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( + JcloudsLocation.OS_FAMILY.getName(), OsFamily.CENTOS, + JcloudsLocation.OS_VERSION_REGEX.getName(), "7.*", + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + + assertMachineSshableSecureAndFromImage(machine, "cloudsoft/centos:7"); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testMatchingOsFamilyUbuntu14HasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( + JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, + JcloudsLocation.OS_VERSION_REGEX.getName(), "14.04.*", + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + + assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:14.04"); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testMatchingOsFamilyUbuntu16HasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of()); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( + JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, + JcloudsLocation.OS_VERSION_REGEX.getName(), "16.04.*", + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + + assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:16.04"); + } + + @Test(groups={"Live", "Live-sanity"}) + public void testMatchingOsFamilyConfiguredOnLocationHasAutoGeneratedCredentials() throws Exception { + loc = newDockerLocation(ImmutableMap.<String, Object>of( + JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, + JcloudsLocation.OS_VERSION_REGEX.getName(), "16.04.*", + JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); + JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of()); + + assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:16.04"); + } + + protected void assertMachineSshableSecureAndFromImage(JcloudsSshMachineLocation machine, String expectedImageDescription) throws Exception { + Image image = getOptionalImage(machine).get(); + assertEquals(image.getDescription(), expectedImageDescription); + assertEquals(templateOptions.getLoginUser(), "root"); + assertEnvContainsKeyValue(templateOptions, "CLOUDSOFT_ROOT_PASSWORD", templateOptions.getLoginPassword()); + assertPasswordIsSecure(templateOptions.getLoginPassword()); + + assertTrue(machine.isSshable(), "machine="+machine); + } + + protected void assertEnvNotContainsKey(DockerTemplateOptions templateOptions, String key) { + List<String> env = templateOptions.getEnv(); + if (env == null) return; + for (String keyval : env) { + if (keyval.startsWith(key+"=")) { + fail("has key "+key+"; env="+env); + } + } + } + + protected void assertEnvContainsKeyValue(DockerTemplateOptions templateOptions, String key, String value) { + String keyval = key+"="+value; + List<String> env = templateOptions.getEnv(); + if (env == null) { + fail("env is null; does not contain "+keyval); + } + if (!env.contains(keyval)) { + fail("env does not contain "+keyval+"; env="+env); + } + } + + protected void assertPasswordIsSecure(String val) { + if (!val.matches(".*[0-9].*")) { + fail("Password '"+val+"' does not contain a digit"); + } + if (!val.matches(".*[A-Z].*")) { + fail("Password '"+val+"' does not contain an upper-case letter"); + } + if (val.trim().length() < 7) { + fail("Password '"+val+"' is too short"); + } + + LOG.debug("Password '"+val+"' passes basic security check"); + } + + @SuppressWarnings("unchecked") + protected Optional<Image> getOptionalImage(JcloudsSshMachineLocation machine) throws Exception { + Method method = machine.getClass().getDeclaredMethod("getOptionalImage"); + method.setAccessible(true); + Optional<Image> result = (Optional<Image>) method.invoke(machine); + return checkNotNull(result, "null must not be returned by getOptionalImage, for %s", machine); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerLocationResolverTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerLocationResolverTest.java b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerLocationResolverTest.java new file mode 100644 index 0000000..d04ccf3 --- /dev/null +++ b/brooklyn-server/locations/container/src/test/java/org/apache/brooklyn/container/location/docker/DockerLocationResolverTest.java @@ -0,0 +1,100 @@ +package io.cloudsoft.amp.containerservice.dockerlocation; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +public class DockerLocationResolverTest extends BrooklynMgmtUnitTestSupport { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(DockerLocationResolverTest.class); + + private BrooklynProperties brooklynProperties; + + @BeforeMethod(alwaysRun = true) + @Override + public void setUp() throws Exception { + super.setUp(); + brooklynProperties = mgmt.getBrooklynProperties(); + + brooklynProperties.put("brooklyn.location.docker.identity", "docker-id"); + brooklynProperties.put("brooklyn.location.docker.credential", "docker-cred"); + } + + @Test + public void testGivesCorrectLocationType() { + LocationSpec<?> spec = getLocationSpec("docker"); + assertEquals(spec.getType(), DockerJcloudsLocation.class); + + DockerJcloudsLocation loc = resolve("docker"); + assertTrue(loc instanceof DockerJcloudsLocation, "loc="+loc); + } + + @Test + public void testParametersInSpecString() { + DockerJcloudsLocation loc = resolve("docker(loginUser=myLoginUser,imageId=myImageId)"); + assertEquals(loc.getConfig(DockerJcloudsLocation.LOGIN_USER), "myLoginUser"); + assertEquals(loc.getConfig(DockerJcloudsLocation.IMAGE_ID), "myImageId"); + } + + @Test + public void testTakesDotSeparateProperty() { + brooklynProperties.put("brooklyn.location.docker.loginUser", "myLoginUser"); + DockerJcloudsLocation loc = resolve("docker"); + assertEquals(loc.getConfig(DockerJcloudsLocation.LOGIN_USER), "myLoginUser"); + } + + @Test + public void testPropertiesPrecedence() { + // prefer those in "spec" over everything else + brooklynProperties.put("brooklyn.location.named.mydocker", "docker:(loginUser=\"loginUser-inSpec\")"); + + brooklynProperties.put("brooklyn.location.named.mydocker.loginUser", "loginUser-inNamed"); + brooklynProperties.put("brooklyn.location.docker.loginUser", "loginUser-inDocker"); + brooklynProperties.put("brooklyn.location.jclouds.docker.loginUser", "loginUser-inJcloudsProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.loginUser", "loginUser-inJcloudsGeneric"); + + // prefer those in "named" over everything else + brooklynProperties.put("brooklyn.location.named.mydocker.privateKeyFile", "privateKeyFile-inNamed"); + brooklynProperties.put("brooklyn.location.docker.privateKeyFile", "privateKeyFile-inDocker"); + brooklynProperties.put("brooklyn.location.jclouds.docker.privateKeyFile", "privateKeyFile-inJcloudsProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyFile", "privateKeyFile-inJcloudsGeneric"); + + // prefer those in docker-specific + brooklynProperties.put("brooklyn.location.docker.publicKeyFile", "publicKeyFile-inDocker"); + brooklynProperties.put("brooklyn.location.jclouds.docker.publicKeyFile", "publicKeyFile-inJcloudsProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.publicKeyFile", "publicKeyFile-inJcloudsGeneric"); + + // prefer those in jclouds provider-specific + brooklynProperties.put("brooklyn.location.jclouds.docker.privateKeyPassphrase", "privateKeyPassphrase-inJcloudsProviderSpecific"); + brooklynProperties.put("brooklyn.location.jclouds.privateKeyPassphrase", "privateKeyPassphrase-inJcloudsGeneric"); + + // accept those in jclouds generic + brooklynProperties.put("brooklyn.location.jclouds.privateKeyData", "privateKeyData-inJcloudsGeneric"); + + Map<String, Object> conf = resolve("named:mydocker").config().getBag().getAllConfig(); + + assertEquals(conf.get("loginUser"), "loginUser-inSpec"); + assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed"); + assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inDocker"); + assertEquals(conf.get("privateKeyPassphrase"), "privateKeyPassphrase-inJcloudsProviderSpecific"); + assertEquals(conf.get("privateKeyData"), "privateKeyData-inJcloudsGeneric"); + } + + private LocationSpec<?> getLocationSpec(String spec) { + return mgmt.getLocationRegistry().getLocationSpec(spec).get(); + } + + private DockerJcloudsLocation resolve(String spec) { + return (DockerJcloudsLocation) mgmt.getLocationRegistry().getLocationManaged(spec); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainer.java ---------------------------------------------------------------------- diff --git a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainer.java b/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainer.java deleted file mode 100644 index ef012c3..0000000 --- a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainer.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockercontainer; - -import org.apache.brooklyn.api.entity.ImplementedBy; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.BasicConfigInheritance; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.config.MapConfigKey; -import org.apache.brooklyn.entity.software.base.SoftwareProcess; - -import com.google.common.collect.ImmutableMap; -import com.google.common.reflect.TypeToken; - -/** - * The DockerContainer type is for easily deploying any docker image from the - * image repository set on the target swarm or docker-engine based location - * <p> - * Example YAML is shown below. Note the different types of the {@code env} - * key in the location config and the {@code docker.container.environment} - * key on the entity. The entity environment variables will override any - * environment configured on the location. To specify environment variables - * that will be set when executing SSH commands against the container you - * should use the {@link SoftwareProcess#SHELL_ENVIRONMENT shell.env} key. - * <p> - * <pre>{@code location: - * docker: - * endpoint: "https://52.29.59.193:3376" - * identity: "~/.certs/cert.pem" - * credential: "~/.certs/key.pem" - * templateOptions: - * networkMode: "brooklyn" - * env: - * - "HTTP_CONFIG_ROOT=/var/httpd" - * - "USE_DEFAULTS=true" - * services: - * - type: io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer - * brooklyn.config: - * docker.container.imageName: "apache/httpd:latest" - * docker.container.disableSsh: true - * docker.container.inboundPorts: - * - "8080-8081" - * docker.container.environment: - * ENABLE_JMX: false - * ENABLE_SHUTDOWN: false - * }</pre> - */ -@ImplementedBy(DockerContainerImpl.class) -public interface DockerContainer extends SoftwareProcess { - - ConfigKey<Boolean> DISABLE_SSH = - ConfigKeys.newBooleanConfigKey( - "docker.container.disableSsh", - "Skip checks such as ssh for when docker image doesn't allow ssh", - Boolean.TRUE); - - ConfigKey<String> IMAGE_NAME = - ConfigKeys.newStringConfigKey( - "docker.container.imageName", - "Image name to pull from docker hub"); - - @SuppressWarnings("serial") - ConfigKey<Iterable<String>> INBOUND_TCP_PORTS = - ConfigKeys.newConfigKey( - new TypeToken<Iterable<String>>() {}, - "docker.container.inboundPorts", - "List of ports, that the docker image opens, to be made public"); - - MapConfigKey<Object> CONTAINER_ENVIRONMENT = new MapConfigKey.Builder<Object>(Object.class, "docker.container.environment") - .description("Environment variables to set on container startup") - .defaultValue(ImmutableMap.<String, Object>of()) - .typeInheritance(BasicConfigInheritance.DEEP_MERGE) - .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED_ELSE_DEEP_MERGE) - .build(); -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainerImpl.java ---------------------------------------------------------------------- diff --git a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainerImpl.java b/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainerImpl.java deleted file mode 100644 index 790985f..0000000 --- a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockercontainer/DockerContainerImpl.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockercontainer; - -import java.util.Iterator; -import java.util.List; - -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.api.sensor.EnricherSpec; -import org.apache.brooklyn.core.entity.Attributes; -import org.apache.brooklyn.core.entity.BrooklynConfigKeys; -import org.apache.brooklyn.core.location.PortRanges; -import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl; -import org.apache.brooklyn.util.collections.MutableList; - -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableSet; - -public class DockerContainerImpl extends EmptySoftwareProcessImpl implements DockerContainer { - - @Override - public void init() { - super.init(); - - String imageName = config().get(DockerContainer.IMAGE_NAME); - if (!Strings.isNullOrEmpty(imageName)) { - config().set(PROVISIONING_PROPERTIES.subKey("imageId"), imageName); - } - - if (Boolean.TRUE.equals(config().get(DockerContainer.DISABLE_SSH))) { - config().set(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true); - config().set(PROVISIONING_PROPERTIES.subKey("useJcloudsSshInit"), false); - config().set(PROVISIONING_PROPERTIES.subKey("waitForSshable"), false); - config().set(PROVISIONING_PROPERTIES.subKey("pollForFirstReachableAddress"), false); - config().set(EmptySoftwareProcessImpl.USE_SSH_MONITORING, false); - } - - ImmutableSet.Builder<AttributeSensor<Integer>> builder = ImmutableSet.builder(); - List<String> portRanges = MutableList.copyOf(config().get(DockerContainer.INBOUND_TCP_PORTS)); - for (String portRange : portRanges) { - Iterator<Integer> iterator = PortRanges.fromString(portRange).iterator(); - while (iterator.hasNext()) { - Integer port = iterator.next(); - AttributeSensor<Integer> element = Sensors.newIntegerSensor("docker.port." + port); - sensors().set(element, port); - builder.add(element); - } - } - - enrichers().add(EnricherSpec.create(OnPublicNetworkEnricher.class).configure(OnPublicNetworkEnricher.SENSORS, builder.build())); - } - - @Override - protected void disconnectSensors() { - if(isSshMonitoringEnabled()) { - disconnectServiceUpIsRunning(); - } - super.disconnectSensors(); - } - - @Override - protected void connectSensors() { - super.connectSensors(); - if (isSshMonitoringEnabled()) { - connectServiceUpIsRunning(); - } else { - sensors().set(Attributes.SERVICE_UP, true); - } - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocation.java ---------------------------------------------------------------------- diff --git a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocation.java b/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocation.java deleted file mode 100644 index ecf3214..0000000 --- a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocation.java +++ /dev/null @@ -1,185 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockerlocation; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.location.NoMachinesAvailableException; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.location.LocationConfigKeys; -import org.apache.brooklyn.location.jclouds.JcloudsLocation; -import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.domain.Image; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.Template; -import org.jclouds.docker.compute.options.DockerTemplateOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Functions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; - -import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer; - -/** - * For provisioning docker containers, using the jclouds-docker integration. - * - * This adds special support for default Cloudsoft images. If the image description matches our - * cloudsoft regexes, then auto-generate a password and pass that in as - * {@code CLOUDSOFT_ROOT_PASSWORD} when launching the container. That will then be used as the - * {@link DockerTemplateOptions#getLoginPassword()}. - * - * Also, if no image is specified then this will set the default to "cloudsoft/centos:7" - * (see https://hub.docker.com/r/cloudsoft/centos/). - */ -public class DockerJcloudsLocation extends JcloudsLocation { - - private static final Logger LOG = LoggerFactory.getLogger(DockerJcloudsLocation.class); - - public static final ConfigKey<Boolean> INJECT_LOGIN_CREDENTIAL = ConfigKeys.newBooleanConfigKey( - "injectLoginCredential", - "Whether to inject login credentials (if null, will infer from image choice)", - null); - - public static final ConfigKey<String> DEFAULT_IMAGE_DESCRIPTION_REGEX = ConfigKeys.newStringConfigKey( - "defaultImageDescriptionRegex", - "The default image description to use, if no other image preferences are supplied", - "cloudsoft/centos:7"); - - /** - * The regex for the image descriptions that support us injecting login credentials. - */ - private static final List<String> IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS = ImmutableList.of( - "cloudsoft/centos.*", - "cloudsoft/ubuntu.*"); - - private static final List<ImageMetadata> DEFAULT_IMAGES = ImmutableList.of( - new ImageMetadata(OsFamily.CENTOS, "7", "cloudsoft/centos:7"), - new ImageMetadata(OsFamily.UBUNTU, "14.04", "cloudsoft/ubuntu:14.04"), - new ImageMetadata(OsFamily.UBUNTU, "16.04", "cloudsoft/ubuntu:16.04")); - - private static class ImageMetadata { - private final OsFamily osFamily; - private final String osVersion; - private final String imageDescription; - - public ImageMetadata(OsFamily osFamily, String osVersion, String imageDescription) { - this.osFamily = checkNotNull(osFamily, "osFamily"); - this.osVersion = checkNotNull(osVersion, "osVersion"); - this.imageDescription = checkNotNull(imageDescription, "imageDescription"); - } - - public boolean matches(@Nullable OsFamily osFamily, @Nullable String osVersionRegex) { - if (osFamily != null && osFamily != this.osFamily) return false; - if (osVersionRegex != null && !osVersion.matches(osVersionRegex)) return false; - return true; - } - - public String getImageDescription() { - return imageDescription; - } - } - - @Override - protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailableException { - // Use the provider name that jclouds expects; rely on resolver to have validated this. - setup.configure(JcloudsLocation.CLOUD_PROVIDER, "docker"); - - // Inject default image, if absent - String imageId = setup.get(JcloudsLocation.IMAGE_ID); - String imageNameRegex = setup.get(JcloudsLocation.IMAGE_NAME_REGEX); - String imageDescriptionRegex = setup.get(JcloudsLocation.IMAGE_DESCRIPTION_REGEX); - String defaultImageDescriptionRegex = setup.get(DEFAULT_IMAGE_DESCRIPTION_REGEX); - OsFamily osFamily = setup.get(OS_FAMILY); - String osVersionRegex = setup.get(OS_VERSION_REGEX); - - if (Strings.isBlank(imageId) && Strings.isBlank(imageNameRegex) && Strings.isBlank(imageDescriptionRegex)) { - if (osFamily != null || osVersionRegex != null) { - for (ImageMetadata imageMetadata : DEFAULT_IMAGES) { - if (imageMetadata.matches(osFamily, osVersionRegex)) { - String imageDescription = imageMetadata.getImageDescription(); - LOG.debug("Setting default image regex to {}, for obtain call in {}; removing osFamily={} and osVersionRegex={}", - new Object[] {imageDescription, this, osFamily, osVersionRegex}); - setup.configure(JcloudsLocation.IMAGE_DESCRIPTION_REGEX, imageDescription); - setup.configure(OS_FAMILY, null); - setup.configure(OS_VERSION_REGEX, null); - break; - } - } - } else if (Strings.isNonBlank(defaultImageDescriptionRegex)) { - LOG.debug("Setting default image regex to {}, for obtain call in {}", defaultImageDescriptionRegex, this); - setup.configure(JcloudsLocation.IMAGE_DESCRIPTION_REGEX, defaultImageDescriptionRegex); - } - } - - return super.obtainOnce(setup); - } - - @Override - public Template buildTemplate(ComputeService computeService, ConfigBag config, Collection<JcloudsLocationCustomizer> customizers) { - String loginUser = config.get(JcloudsLocation.LOGIN_USER); - String loginPassword = config.get(JcloudsLocation.LOGIN_USER_PASSWORD); - String loginKeyFile = config.get(JcloudsLocation.LOGIN_USER_PRIVATE_KEY_FILE); - String loginKeyData = config.get(JcloudsLocation.LOGIN_USER_PRIVATE_KEY_DATA); - - Template template = super.buildTemplate(computeService, config, customizers); - DockerTemplateOptions templateOptions = (DockerTemplateOptions) template.getOptions(); - Image image = template.getImage(); - List<String> env = MutableList.copyOf(templateOptions.getEnv()); - - // Inject login credentials, if required - Boolean injectLoginCredentials = config.get(INJECT_LOGIN_CREDENTIAL); - if (injectLoginCredentials == null) { - String imageDescription = image.getDescription(); - for (String regex : IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS) { - if (imageDescription != null && imageDescription.matches(regex)) { - injectLoginCredentials = true; - break; - } - } - } - if (Strings.isBlank(loginUser) && Strings.isBlank(loginPassword) && Strings.isBlank(loginKeyFile) && Strings.isBlank(loginKeyData)) { - if (Boolean.TRUE.equals(injectLoginCredentials)) { - loginUser = "root"; - loginPassword = Identifiers.makeRandomPassword(12); - templateOptions.overrideLoginUser(loginUser); - templateOptions.overrideLoginPassword(loginPassword); - - env.add("CLOUDSOFT_ROOT_PASSWORD="+loginPassword); - } - } - - Entity context = validateCallerContext(config); - Map<String,Object> containerEnv = MutableMap.copyOf(context.config().get(DockerContainer.CONTAINER_ENVIRONMENT)); - for (Map.Entry<String,String> entry : Maps.transformValues(containerEnv, Functions.toStringFunction()).entrySet()) { - env.add(String.format("%s=%s", entry.getKey(), entry.getValue())); - } - templateOptions.env(env); - - return template; - } - - - private Entity validateCallerContext(ConfigBag setup) { - // Lookup entity flags - Object callerContext = setup.get(LocationConfigKeys.CALLER_CONTEXT); - if (callerContext == null || !(callerContext instanceof Entity)) { - throw new IllegalStateException("Invalid caller context: " + callerContext); - } - return (Entity) callerContext; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolver.java ---------------------------------------------------------------------- diff --git a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolver.java b/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolver.java deleted file mode 100644 index d6c8c99..0000000 --- a/docker-location/src/main/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolver.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockerlocation; - -import java.util.Map; - -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.location.LocationResolver; -import org.apache.brooklyn.core.location.AbstractLocationResolver; -import org.apache.brooklyn.core.location.LocationConfigUtils; -import org.apache.brooklyn.core.location.LocationPropertiesFromBrooklynProperties; -import org.apache.brooklyn.location.jclouds.JcloudsPropertiesFromBrooklynProperties; -import org.apache.brooklyn.util.collections.MutableMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Locations starting with the given prefix (@code "docker") will use this resolver, to instantiate - * a {@link DockerJcloudsLocation}. - * - * We ensure that config will be picked up from brooklyn.properties using the appropriate precedence: - * <ol> - * <li>named location config - * <li>Prefix {@code brooklyn.location.docker.} - * <li>Prefix {@code brooklyn.jclouds.docker.} - * <li>Prefix {@code brooklyn.jclouds.} - * </ol> - */ -public class DockerLocationResolver extends AbstractLocationResolver implements LocationResolver { - - public static final Logger log = LoggerFactory.getLogger(DockerLocationResolver.class); - - public static final String PREFIX = "docker"; - - @Override - public boolean isEnabled() { - return LocationConfigUtils.isResolverPrefixEnabled(managementContext, getPrefix()); - } - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - protected Class<? extends Location> getLocationType() { - return DockerJcloudsLocation.class; - } - - @Override - protected SpecParser getSpecParser() { - return new AbstractLocationResolver.SpecParser(getPrefix()).setExampleUsage("\"docker\""); - } - - @Override - protected Map<String, Object> getFilteredLocationProperties(String provider, String namedLocation, Map<String, ?> prioritisedProperties, Map<String, ?> globalProperties) { - Map<String, Object> dockerConf = new LocationPropertiesFromBrooklynProperties().getLocationProperties(getPrefix(), namedLocation, globalProperties); - - Object providerInConf = dockerConf.get("provider"); - if (providerInConf != null && !provider.equals(providerInConf)) { - throw new IllegalArgumentException(provider+" location configured with provider '"+providerInConf+"', but must be blank or '"+provider+"'"); - } - - String providerOrApi = "docker"; - String regionName = (String) prioritisedProperties.get("region"); - Map<String, Object> jcloudsConf = new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties(providerOrApi, regionName, namedLocation, globalProperties); - return MutableMap.<String, Object>builder() - .putAll(jcloudsConf) - .putAll(dockerConf) - .put("provider", providerOrApi) - .build(); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocationLiveTest.java ---------------------------------------------------------------------- diff --git a/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocationLiveTest.java b/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocationLiveTest.java deleted file mode 100644 index eb64173..0000000 --- a/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerJcloudsLocationLiveTest.java +++ /dev/null @@ -1,252 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockerlocation; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; - -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport; -import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer; -import org.apache.brooklyn.location.jclouds.JcloudsLocation; -import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig; -import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; -import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.os.Os; -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.domain.Image; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.options.TemplateOptions; -import org.jclouds.docker.compute.options.DockerTemplateOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; - -/** - * TODO For these tests to pass, they need on the classpath the patch file(s) from AMP. - * - * Assumes that a pre-existing swarm endpoint is available. See system properties and the defaults - * below. - */ -public class DockerJcloudsLocationLiveTest extends BrooklynAppLiveTestSupport { - - private static final Logger LOG = LoggerFactory.getLogger(DockerJcloudsLocationLiveTest.class); - - private static final String SWARM_ENDPOINT = System.getProperty("test.amp.docker.swarmEndpoint", "https://10.104.0.162:3376/"); - private static final String IDENTITY_FILE_PATH = System.getProperty("test.amp.docker.identity", Os.tidyPath("~/.docker/.certs/cert.pem")); - private static final String CREDENTIAL_FILE_PATH = System.getProperty("test.amp.docker.credential", Os.tidyPath("~/.docker/.certs/key.pem")); - private static final String SWARM_NETWORK_NAME = System.getProperty("test.amp.docker.networkName", Os.tidyPath("brooklyn")); - - protected DockerJcloudsLocation loc; - protected List<MachineLocation> machines; - protected DockerTemplateOptions templateOptions; - - @BeforeMethod(alwaysRun=true) - @Override - public void setUp() throws Exception { - super.setUp(); - machines = Lists.newCopyOnWriteArrayList(); - } - - @AfterMethod(alwaysRun=true) - @Override - public void tearDown() throws Exception { - for (MachineLocation machine : machines) { - try { - loc.release(machine); - } catch (Exception e) { - LOG.error("Error releasing machine "+machine+" in location "+loc, e); - } - } - super.tearDown(); - } - - protected DockerJcloudsLocation newDockerLocation(Map<String, ?> flags) throws Exception { - JcloudsLocationCustomizer locationCustomizer = new BasicJcloudsLocationCustomizer() { - @Override - public void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions) { - DockerJcloudsLocationLiveTest.this.templateOptions = (DockerTemplateOptions) templateOptions; - } - }; - Map<String, ?> templateOptionsOverrides = (Map<String, ?>) flags.get(JcloudsLocation.TEMPLATE_OPTIONS.getName()); - Map<String,?> templateOptions = MutableMap.<String, Object>builder() - .put("networkMode", SWARM_NETWORK_NAME) - .putAll(templateOptionsOverrides != null ? templateOptionsOverrides : ImmutableMap.<String, Object>of()) - .build(); - Map<String,?> allFlags = MutableMap.<String,Object>builder() - .put("identity", IDENTITY_FILE_PATH) - .put("credential", CREDENTIAL_FILE_PATH) - .put("endpoint", SWARM_ENDPOINT) - .put("tags", ImmutableList.of(getClass().getName())) - .put(JcloudsLocation.WAIT_FOR_SSHABLE.getName(), false) - .put(JcloudsLocation.JCLOUDS_LOCATION_CUSTOMIZERS.getName(), ImmutableList.of(locationCustomizer)) - .putAll(flags) - .put(JcloudsLocation.TEMPLATE_OPTIONS.getName(), templateOptions) - .build(); - return (DockerJcloudsLocation) mgmt.getLocationRegistry().getLocationManaged("docker", allFlags); - } - - private JcloudsSshMachineLocation newDockerMachine(DockerJcloudsLocation loc, Map<?, ?> flags) throws Exception { - MachineLocation result = loc.obtain(flags); - machines.add(result); - return (JcloudsSshMachineLocation) result; - } - - @Test(groups={"Live", "Live-sanity"}) - public void testDefaultImageHasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - - assertMachineSshableSecureAndFromImage(machine, "cloudsoft/centos:7"); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testExplicitCredentialsNotOverwritten() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, MutableMap.of( - JcloudsLocationConfig.LOGIN_USER, "myuser", - JcloudsLocationConfig.LOGIN_USER_PASSWORD, "mypassword")); - Image image = getOptionalImage(machine).get(); - assertEquals(image.getDescription(), "cloudsoft/centos:7"); - assertEquals(templateOptions.getLoginUser(), "myuser"); - assertEquals(templateOptions.getLoginPassword(), "mypassword"); - assertEquals(templateOptions.getLoginPassword(), "mypassword"); - assertEnvNotContainsKey(templateOptions, "CLOUDSOFT_ROOT_PASSWORD"); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testExplicitImageIdNotOverwritten() throws Exception { - // TODO This id will likely change sometimes; once CCS-29 is done, then use an image name. - // Assumes we have executed: - // docker run ubuntu /bin/echo 'Hello world' - // which will have downloaded the ubuntu image with the given id. - String imageId = "sha256:2fa927b5cdd31cdec0027ff4f45ef4343795c7a2d19a9af4f32425132a222330"; - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, MutableMap.of( - JcloudsLocation.IMAGE_ID, imageId, - JcloudsLocation.TEMPLATE_OPTIONS, ImmutableMap.of( - "entrypoint", ImmutableList.of("/bin/sleep", "1000")))); - Image image = getOptionalImage(machine).get(); - assertEquals(image.getId(), imageId); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testMatchingImageDescriptionHasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( - JcloudsLocation.IMAGE_DESCRIPTION_REGEX.getName(), "cloudsoft/centos:7", - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - - assertTrue(machine.isSshable(), "machine="+machine); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testMatchingOsFamilyCentosHasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( - JcloudsLocation.OS_FAMILY.getName(), OsFamily.CENTOS, - JcloudsLocation.OS_VERSION_REGEX.getName(), "7.*", - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - - assertMachineSshableSecureAndFromImage(machine, "cloudsoft/centos:7"); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testMatchingOsFamilyUbuntu14HasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( - JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, - JcloudsLocation.OS_VERSION_REGEX.getName(), "14.04.*", - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - - assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:14.04"); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testMatchingOsFamilyUbuntu16HasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of()); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of( - JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, - JcloudsLocation.OS_VERSION_REGEX.getName(), "16.04.*", - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - - assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:16.04"); - } - - @Test(groups={"Live", "Live-sanity"}) - public void testMatchingOsFamilyConfiguredOnLocationHasAutoGeneratedCredentials() throws Exception { - loc = newDockerLocation(ImmutableMap.<String, Object>of( - JcloudsLocation.OS_FAMILY.getName(), OsFamily.UBUNTU, - JcloudsLocation.OS_VERSION_REGEX.getName(), "16.04.*", - JcloudsLocation.WAIT_FOR_SSHABLE.getName(), "1m")); - JcloudsSshMachineLocation machine = newDockerMachine(loc, ImmutableMap.<String, Object>of()); - - assertMachineSshableSecureAndFromImage(machine, "cloudsoft/ubuntu:16.04"); - } - - protected void assertMachineSshableSecureAndFromImage(JcloudsSshMachineLocation machine, String expectedImageDescription) throws Exception { - Image image = getOptionalImage(machine).get(); - assertEquals(image.getDescription(), expectedImageDescription); - assertEquals(templateOptions.getLoginUser(), "root"); - assertEnvContainsKeyValue(templateOptions, "CLOUDSOFT_ROOT_PASSWORD", templateOptions.getLoginPassword()); - assertPasswordIsSecure(templateOptions.getLoginPassword()); - - assertTrue(machine.isSshable(), "machine="+machine); - } - - protected void assertEnvNotContainsKey(DockerTemplateOptions templateOptions, String key) { - List<String> env = templateOptions.getEnv(); - if (env == null) return; - for (String keyval : env) { - if (keyval.startsWith(key+"=")) { - fail("has key "+key+"; env="+env); - } - } - } - - protected void assertEnvContainsKeyValue(DockerTemplateOptions templateOptions, String key, String value) { - String keyval = key+"="+value; - List<String> env = templateOptions.getEnv(); - if (env == null) { - fail("env is null; does not contain "+keyval); - } - if (!env.contains(keyval)) { - fail("env does not contain "+keyval+"; env="+env); - } - } - - protected void assertPasswordIsSecure(String val) { - if (!val.matches(".*[0-9].*")) { - fail("Password '"+val+"' does not contain a digit"); - } - if (!val.matches(".*[A-Z].*")) { - fail("Password '"+val+"' does not contain an upper-case letter"); - } - if (val.trim().length() < 7) { - fail("Password '"+val+"' is too short"); - } - - LOG.debug("Password '"+val+"' passes basic security check"); - } - - @SuppressWarnings("unchecked") - protected Optional<Image> getOptionalImage(JcloudsSshMachineLocation machine) throws Exception { - Method method = machine.getClass().getDeclaredMethod("getOptionalImage"); - method.setAccessible(true); - Optional<Image> result = (Optional<Image>) method.invoke(machine); - return checkNotNull(result, "null must not be returned by getOptionalImage, for %s", machine); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/e890c003/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolverTest.java ---------------------------------------------------------------------- diff --git a/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolverTest.java b/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolverTest.java deleted file mode 100644 index d04ccf3..0000000 --- a/docker-location/src/test/java/io/cloudsoft/amp/containerservice/dockerlocation/DockerLocationResolverTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.cloudsoft.amp.containerservice.dockerlocation; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.util.Map; - -import org.apache.brooklyn.api.location.LocationSpec; -import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class DockerLocationResolverTest extends BrooklynMgmtUnitTestSupport { - - @SuppressWarnings("unused") - private static final Logger log = LoggerFactory.getLogger(DockerLocationResolverTest.class); - - private BrooklynProperties brooklynProperties; - - @BeforeMethod(alwaysRun = true) - @Override - public void setUp() throws Exception { - super.setUp(); - brooklynProperties = mgmt.getBrooklynProperties(); - - brooklynProperties.put("brooklyn.location.docker.identity", "docker-id"); - brooklynProperties.put("brooklyn.location.docker.credential", "docker-cred"); - } - - @Test - public void testGivesCorrectLocationType() { - LocationSpec<?> spec = getLocationSpec("docker"); - assertEquals(spec.getType(), DockerJcloudsLocation.class); - - DockerJcloudsLocation loc = resolve("docker"); - assertTrue(loc instanceof DockerJcloudsLocation, "loc="+loc); - } - - @Test - public void testParametersInSpecString() { - DockerJcloudsLocation loc = resolve("docker(loginUser=myLoginUser,imageId=myImageId)"); - assertEquals(loc.getConfig(DockerJcloudsLocation.LOGIN_USER), "myLoginUser"); - assertEquals(loc.getConfig(DockerJcloudsLocation.IMAGE_ID), "myImageId"); - } - - @Test - public void testTakesDotSeparateProperty() { - brooklynProperties.put("brooklyn.location.docker.loginUser", "myLoginUser"); - DockerJcloudsLocation loc = resolve("docker"); - assertEquals(loc.getConfig(DockerJcloudsLocation.LOGIN_USER), "myLoginUser"); - } - - @Test - public void testPropertiesPrecedence() { - // prefer those in "spec" over everything else - brooklynProperties.put("brooklyn.location.named.mydocker", "docker:(loginUser=\"loginUser-inSpec\")"); - - brooklynProperties.put("brooklyn.location.named.mydocker.loginUser", "loginUser-inNamed"); - brooklynProperties.put("brooklyn.location.docker.loginUser", "loginUser-inDocker"); - brooklynProperties.put("brooklyn.location.jclouds.docker.loginUser", "loginUser-inJcloudsProviderSpecific"); - brooklynProperties.put("brooklyn.location.jclouds.loginUser", "loginUser-inJcloudsGeneric"); - - // prefer those in "named" over everything else - brooklynProperties.put("brooklyn.location.named.mydocker.privateKeyFile", "privateKeyFile-inNamed"); - brooklynProperties.put("brooklyn.location.docker.privateKeyFile", "privateKeyFile-inDocker"); - brooklynProperties.put("brooklyn.location.jclouds.docker.privateKeyFile", "privateKeyFile-inJcloudsProviderSpecific"); - brooklynProperties.put("brooklyn.location.jclouds.privateKeyFile", "privateKeyFile-inJcloudsGeneric"); - - // prefer those in docker-specific - brooklynProperties.put("brooklyn.location.docker.publicKeyFile", "publicKeyFile-inDocker"); - brooklynProperties.put("brooklyn.location.jclouds.docker.publicKeyFile", "publicKeyFile-inJcloudsProviderSpecific"); - brooklynProperties.put("brooklyn.location.jclouds.publicKeyFile", "publicKeyFile-inJcloudsGeneric"); - - // prefer those in jclouds provider-specific - brooklynProperties.put("brooklyn.location.jclouds.docker.privateKeyPassphrase", "privateKeyPassphrase-inJcloudsProviderSpecific"); - brooklynProperties.put("brooklyn.location.jclouds.privateKeyPassphrase", "privateKeyPassphrase-inJcloudsGeneric"); - - // accept those in jclouds generic - brooklynProperties.put("brooklyn.location.jclouds.privateKeyData", "privateKeyData-inJcloudsGeneric"); - - Map<String, Object> conf = resolve("named:mydocker").config().getBag().getAllConfig(); - - assertEquals(conf.get("loginUser"), "loginUser-inSpec"); - assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed"); - assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inDocker"); - assertEquals(conf.get("privateKeyPassphrase"), "privateKeyPassphrase-inJcloudsProviderSpecific"); - assertEquals(conf.get("privateKeyData"), "privateKeyData-inJcloudsGeneric"); - } - - private LocationSpec<?> getLocationSpec(String spec) { - return mgmt.getLocationRegistry().getLocationSpec(spec).get(); - } - - private DockerJcloudsLocation resolve(String spec) { - return (DockerJcloudsLocation) mgmt.getLocationRegistry().getLocationManaged(spec); - } -}
