Java tutorial
/* * 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 org.apache.brooklyn.container.location.docker; 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.container.entity.docker.DockerContainer; 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; /** * For provisioning docker containers, using the jclouds-docker integration. * <p> * This adds special support for default Docker images. If the image description matches our * image regexes, then auto-generate a password and pass that in as * {@code BROOKLYN_ROOT_PASSWORD} when launching the container. That will then be used as the * {@link DockerTemplateOptions#getLoginPassword()}. * <p> * Also, if no image is specified then this will set the default to "brooklyncentral/centos:7" * (see https://hub.docker.com/r/brooklyncentral/centos/). */ public class DockerJcloudsLocation extends JcloudsLocation { 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", "brooklyncentral/centos:7"); private static final Logger LOG = LoggerFactory.getLogger(DockerJcloudsLocation.class); /** * 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("brooklyncentral/centos.*", "brooklyncentral/ubuntu.*"); private static final List<ImageMetadata> DEFAULT_IMAGES = ImmutableList.of( new ImageMetadata(OsFamily.CENTOS, "7", "brooklyncentral/centos:7"), new ImageMetadata(OsFamily.UBUNTU, "14.04", "brooklyncentral/ubuntu:14.04"), new ImageMetadata(OsFamily.UBUNTU, "16.04", "brooklyncentral/ubuntu:16.04")); @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("BROOKLYN_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; } 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; } } }