http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java deleted file mode 100644 index e87866c..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java +++ /dev/null @@ -1,1014 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import java.io.InputStream; -import java.net.InetAddress; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.LocationSpec; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.location.MachineProvisioningLocation; -import org.apache.brooklyn.api.location.NoMachinesAvailableException; -import org.apache.brooklyn.api.location.PortRange; -import org.apache.brooklyn.api.sensor.AttributeSensor; -import org.apache.brooklyn.api.sensor.EnricherSpec; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.entity.BrooklynConfigKeys; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.location.AbstractLocation; -import org.apache.brooklyn.core.location.LocationConfigKeys; -import org.apache.brooklyn.core.location.PortRanges; -import org.apache.brooklyn.core.location.access.PortForwardManager; -import org.apache.brooklyn.core.location.access.PortForwardManagerLocationResolver; -import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; -import org.apache.brooklyn.core.network.OnPublicNetworkEnricher; -import org.apache.brooklyn.core.sensor.Sensors; -import org.apache.brooklyn.location.ssh.SshMachineLocation; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.ResourceUtils; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.core.config.ResolvingConfigBag; -import org.apache.brooklyn.util.core.internal.ssh.SshTool; -import org.apache.brooklyn.util.core.text.TemplateProcessor; -import org.apache.brooklyn.util.exceptions.ReferenceWithError; -import org.apache.brooklyn.util.net.Networking; -import org.apache.brooklyn.util.repeat.Repeater; -import org.apache.brooklyn.util.stream.Streams; -import org.apache.brooklyn.util.text.Identifiers; -import org.apache.brooklyn.util.text.Strings; -import org.apache.brooklyn.util.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Functions; -import com.google.common.base.Joiner; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.base.Stopwatch; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.io.BaseEncoding; -import com.google.common.net.HostAndPort; - -import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer; -import io.cloudsoft.amp.containerservice.dockerlocation.DockerJcloudsLocation; -import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesPod; -import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesResource; -import io.cloudsoft.amp.containerservice.kubernetes.location.machine.KubernetesEmptyMachineLocation; -import io.cloudsoft.amp.containerservice.kubernetes.location.machine.KubernetesMachineLocation; -import io.cloudsoft.amp.containerservice.kubernetes.location.machine.KubernetesSshMachineLocation; -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.ContainerBuilder; -import io.fabric8.kubernetes.api.model.ContainerPort; -import io.fabric8.kubernetes.api.model.ContainerPortBuilder; -import io.fabric8.kubernetes.api.model.EndpointAddress; -import io.fabric8.kubernetes.api.model.EndpointSubset; -import io.fabric8.kubernetes.api.model.Endpoints; -import io.fabric8.kubernetes.api.model.EnvVar; -import io.fabric8.kubernetes.api.model.EnvVarBuilder; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.Namespace; -import io.fabric8.kubernetes.api.model.NamespaceBuilder; -import io.fabric8.kubernetes.api.model.PersistentVolume; -import io.fabric8.kubernetes.api.model.PersistentVolumeBuilder; -import io.fabric8.kubernetes.api.model.Pod; -import io.fabric8.kubernetes.api.model.PodList; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; -import io.fabric8.kubernetes.api.model.QuantityBuilder; -import io.fabric8.kubernetes.api.model.ReplicationController; -import io.fabric8.kubernetes.api.model.ResourceRequirements; -import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder; -import io.fabric8.kubernetes.api.model.Secret; -import io.fabric8.kubernetes.api.model.SecretBuilder; -import io.fabric8.kubernetes.api.model.Service; -import io.fabric8.kubernetes.api.model.ServiceBuilder; -import io.fabric8.kubernetes.api.model.ServicePort; -import io.fabric8.kubernetes.api.model.ServicePortBuilder; -import io.fabric8.kubernetes.api.model.extensions.Deployment; -import io.fabric8.kubernetes.api.model.extensions.DeploymentBuilder; -import io.fabric8.kubernetes.api.model.extensions.DeploymentStatus; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientException; - -public class KubernetesLocation extends AbstractLocation implements MachineProvisioningLocation<KubernetesMachineLocation>, KubernetesLocationConfig { - - /* - * TODO - * - * - Ignores config such as 'user' and 'password', just uses 'loginUser' - * and 'loginUser.password' for connecting to the container. - * - Does not create a user, so behaves differently from things that use - * JcloudsLocation. - * - Does not use ssh keys only passwords. - * - The 'cloudsoft/*' images use root which is discouraged. - * - Error handling needs revisited. For example, if provisioning fails then - * it waits for five minutes and then fails without a reason why. - * e.g. try launching a container with an incorrect image name. - */ - - private static final Logger LOG = LoggerFactory.getLogger(KubernetesLocation.class); - - public static final String NODE_PORT = "NodePort"; - - public static final String IMMUTABLE_CONTAINER_KEY = "immutable-container"; - public static final String SSHABLE_CONTAINER = "sshable-container"; - public static final String CLOUDSOFT_ENTITY_ID = "cloudsoft.io/entity-id"; - public static final String CLOUDSOFT_APPLICATION_ID = "cloudsoft.io/application-id"; - public static final String KUBERNETES_DOCKERCFG = "kubernetes.io/dockercfg"; - - public static final String PHASE_AVAILABLE = "Available"; - public static final String PHASE_TERMINATING = "Terminating"; - public static final String PHASE_ACTIVE = "Active"; - - /** - * The regex for the image descriptions that support us injecting login credentials. - */ - public static final List<String> IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS = ImmutableList.of( - "cloudsoft/centos.*", - "cloudsoft/ubuntu.*"); - - /** The environment variable for injecting login credentials. */ - public static final String CLOUDSOFT_ROOT_PASSWORD = "CLOUDSOFT_ROOT_PASSWORD"; - - private KubernetesClient client; - - public KubernetesLocation() { - super(); - } - - public KubernetesLocation(Map<?, ?> properties) { - super(properties); - } - - @Override - public void init() { - super.init(); - } - - protected KubernetesClient getClient() { - return getClient(MutableMap.of()); - } - - protected KubernetesClient getClient(Map<?, ?> flags) { - ConfigBag conf = (flags == null || flags.isEmpty()) - ? config().getBag() - : ConfigBag.newInstanceExtending(config().getBag(), flags); - return getClient(conf); - } - - protected KubernetesClient getClient(ConfigBag config) { - if (client == null) { - KubernetesClientRegistry registry = getConfig(KUBERNETES_CLIENT_REGISTRY); - client = registry.getKubernetesClient(ResolvingConfigBag.newInstanceExtending(getManagementContext(), config)); - } - return client; - } - - @Override - public KubernetesMachineLocation obtain(Map<?, ?> flags) throws NoMachinesAvailableException { - ConfigBag setupRaw = ConfigBag.newInstanceExtending(config().getBag(), flags); - ConfigBag setup = ResolvingConfigBag.newInstanceExtending(getManagementContext(), setupRaw); - - client = getClient(setup); - Entity entity = validateCallerContext(setup); - if (isKubernetesResource(entity)) { - return createKubernetesResourceLocation(entity, setup); - } else { - return createKubernetesContainerLocation(entity, setup); - } - } - - @Override - public void release(KubernetesMachineLocation machine) { - Entity entity = validateCallerContext(machine); - if (isKubernetesResource(entity)) { - deleteKubernetesResourceLocation(entity); - } else { - deleteKubernetesContainerLocation(entity, machine); - } - } - - protected void deleteKubernetesContainerLocation(Entity entity, MachineLocation machine) { - final String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE); - final String deployment = entity.sensors().get(KubernetesPod.KUBERNETES_DEPLOYMENT); - final String pod = entity.sensors().get(KubernetesPod.KUBERNETES_POD); - final String service = entity.sensors().get(KubernetesPod.KUBERNETES_SERVICE); - - undeploy(namespace, deployment, pod); - - client.services().inNamespace(namespace).withName(service).delete(); - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - return client.services().inNamespace(namespace).withName(service).get() == null; - } - @Override - public String getFailureMessage() { - return "No service with namespace=" + namespace + ", serviceName=" + service; - } - }; - waitForExitCondition(exitCondition); - - Boolean delete = machine.config().get(DELETE_EMPTY_NAMESPACE); - if (delete) { - deleteEmptyNamespace(namespace); - } - } - - protected void deleteKubernetesResourceLocation(Entity entity) { - final String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE); - final String resourceType = entity.sensors().get(KubernetesResource.RESOURCE_TYPE); - final String resourceName = entity.sensors().get(KubernetesResource.RESOURCE_NAME); - - if (!handleResourceDelete(resourceType, resourceName, namespace)) { - LOG.warn("Resource {}: {} not deleted", resourceName, resourceType); - } - } - - protected boolean handleResourceDelete(String resourceType, String resourceName, String namespace) { - try { - switch (resourceType) { - case KubernetesResource.DEPLOYMENT: - return client.extensions().deployments().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.REPLICA_SET: - return client.extensions().replicaSets().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.CONFIG_MAP: - return client.configMaps().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.PERSISTENT_VOLUME: - return client.persistentVolumes().withName(resourceName).delete(); - case KubernetesResource.SECRET: - return client.secrets().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.SERVICE: - return client.services().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.REPLICATION_CONTROLLER: - return client.replicationControllers().inNamespace(namespace).withName(resourceName).delete(); - case KubernetesResource.NAMESPACE: - return client.namespaces().withName(resourceName).delete(); - } - } catch (KubernetesClientException kce) { - LOG.warn("Error deleting resource {}: {}", resourceName, kce); - } - return false; - } - - protected void undeploy(final String namespace, final String deployment, final String pod) { - client.extensions().deployments().inNamespace(namespace).withName(deployment).delete(); - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - return client.extensions().deployments().inNamespace(namespace).withName(deployment).get() == null; - } - @Override - public String getFailureMessage() { - return "No deployment with namespace=" + namespace + ", deployment=" + deployment; - } - }; - waitForExitCondition(exitCondition); - } - - protected synchronized void deleteEmptyNamespace(final String name) { - if (!name.equals("default") && isNamespaceEmpty(name)) { - if (client.namespaces().withName(name).get() != null && - !client.namespaces().withName(name).get().getStatus().getPhase().equals(PHASE_TERMINATING)) { - client.namespaces().withName(name).delete(); - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - return client.namespaces().withName(name).get() == null; - } - @Override - public String getFailureMessage() { - return "Namespace " + name + " still present"; - } - }; - waitForExitCondition(exitCondition); - } - } - } - - protected boolean isNamespaceEmpty(String name) { - return client.extensions().deployments().inNamespace(name).list().getItems().isEmpty() && - client.services().inNamespace(name).list().getItems().isEmpty() && - client.secrets().inNamespace(name).list().getItems().isEmpty(); - } - - @Override - public Map<String, Object> getProvisioningFlags(Collection<String> tags) { - return null; - } - - protected KubernetesMachineLocation createKubernetesResourceLocation(Entity entity, ConfigBag setup) { - String resourceUri = entity.config().get(KubernetesResource.RESOURCE_FILE); - InputStream resource = ResourceUtils.create(entity).getResourceFromUrl(resourceUri); - String templateContents = Streams.readFullyString(resource); - String processedContents = TemplateProcessor.processTemplateContents(templateContents, (EntityInternal) entity, setup.getAllConfig()); - InputStream processedResource = Streams.newInputStreamWithContents(processedContents); - - final List<HasMetadata> result = getClient().load(processedResource).createOrReplace(); - - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - if (result.isEmpty()) { - return false; - } - List<HasMetadata> check = client.resource(result.get(0)).inNamespace(result.get(0).getMetadata().getNamespace()).get(); - if (result.size() > 1 || check.size() != 1 || check.get(0).getMetadata() == null) { - return false; - } - return true; - } - @Override - public String getFailureMessage() { - return "Cannot find created resources"; - } - }; - waitForExitCondition(exitCondition); - - HasMetadata metadata = result.get(0); - String resourceType = metadata.getKind(); - String resourceName = metadata.getMetadata().getName(); - String namespace = metadata.getMetadata().getNamespace(); - LOG.debug("Resource {} (type {}) deployed to {}", resourceName, resourceType, namespace); - - entity.sensors().set(KubernetesPod.KUBERNETES_NAMESPACE, namespace); - entity.sensors().set(KubernetesResource.RESOURCE_NAME, resourceName); - entity.sensors().set(KubernetesResource.RESOURCE_TYPE, resourceType); - - LocationSpec<? extends KubernetesMachineLocation> locationSpec = LocationSpec.create(KubernetesSshMachineLocation.class); - if (!findResourceAddress(locationSpec, entity, metadata, resourceType, resourceName, namespace)) { - LOG.info("Resource {} with type {} has no associated address", resourceName, resourceType); - locationSpec = LocationSpec.create(KubernetesEmptyMachineLocation.class); - } - locationSpec.configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT)) - .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, namespace) - .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, resourceName) - .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, resourceType); - - KubernetesMachineLocation machine = getManagementContext().getLocationManager().createLocation(locationSpec); - - if (resourceType.equals(KubernetesResource.SERVICE) && machine instanceof KubernetesSshMachineLocation) { - Service service = getService(namespace, resourceName); - registerPortMappings((KubernetesSshMachineLocation) machine, entity, service); - } - - return machine; - } - - protected boolean findResourceAddress(LocationSpec<? extends KubernetesMachineLocation> locationSpec, Entity entity, HasMetadata metadata, String resourceType, String resourceName, String namespace) { - if (resourceType.equals(KubernetesResource.DEPLOYMENT) || resourceType.equals(KubernetesResource.REPLICATION_CONTROLLER) || resourceType.equals(KubernetesResource.POD)) { - Map<String, String> labels = MutableMap.of(); - if (resourceType.equals(KubernetesResource.DEPLOYMENT)) { - Deployment deployment = (Deployment) metadata; - labels = deployment.getSpec().getTemplate().getMetadata().getLabels(); - } else if (resourceType.equals(KubernetesResource.REPLICATION_CONTROLLER)) { - ReplicationController replicationController = (ReplicationController) metadata; - labels = replicationController.getSpec().getTemplate().getMetadata().getLabels(); - } - Pod pod = resourceType.equals(KubernetesResource.POD) ? getPod(namespace, resourceName) : getPod(namespace, labels); - entity.sensors().set(KubernetesPod.KUBERNETES_POD, pod.getMetadata().getName()); - - InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName()); - String podAddress = pod.getStatus().getPodIP(); - - locationSpec.configure("address", node); - locationSpec.configure(SshMachineLocation.PRIVATE_ADDRESSES, ImmutableSet.of(podAddress)); - - return true; - } else if (resourceType.equals(KubernetesResource.SERVICE)) { - getService(namespace, resourceName); - Endpoints endpoints = client.endpoints().inNamespace(namespace).withName(resourceName).get(); - Set<String> privateIps = Sets.newLinkedHashSet(); - Set<String> podNames = Sets.newLinkedHashSet(); - for (EndpointSubset subset : endpoints.getSubsets()) { - for (EndpointAddress address : subset.getAddresses()) { - String podName = address.getTargetRef().getName(); - podNames.add(podName); - String privateIp = address.getIp(); - privateIps.add(privateIp); - } - } - locationSpec.configure(SshMachineLocation.PRIVATE_ADDRESSES, ImmutableSet.copyOf(privateIps)); - - if (podNames.size() > 0) { - // Use the first pod name from the list; warn when multiple pods are referenced - String podName = Iterables.get(podNames, 0); - if (podNames.size() > 1) { - LOG.warn("Multiple pods referenced by service {} in namespace {}, using {}: {}", - new Object[] { resourceName, namespace, podName, Iterables.toString(podNames) }); - } - try { - Pod pod = getPod(namespace, podName); - entity.sensors().set(KubernetesPod.KUBERNETES_POD, podName); - - InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName()); - locationSpec.configure("address", node); - } catch (KubernetesClientException kce) { - LOG.warn("Cannot find pod {} in namespace {} for service {}", new Object[] { podName, namespace, resourceName }); - } - } - - return true; - } else { - return false; - } - } - - protected KubernetesMachineLocation createKubernetesContainerLocation(Entity entity, ConfigBag setup) { - String deploymentName = lookup(KubernetesPod.DEPLOYMENT, entity, setup, entity.getId()); - Integer replicas = lookup(KubernetesPod.REPLICAS, entity, setup); - List<String> volumes = lookup(KubernetesPod.PERSISTENT_VOLUMES, entity, setup); - Map<String, String> secrets = lookup(KubernetesPod.SECRETS, entity, setup); - Map<String, String> limits = lookup(KubernetesPod.LIMITS, entity, setup); - Boolean privileged = lookup(KubernetesPod.PRIVILEGED, entity, setup); - String imageName = findImageName(entity, setup); - Iterable<Integer> inboundPorts = findInboundPorts(entity, setup); - Map<String, String> env = findEnvironmentVariables(entity, setup, imageName); - Map<String, String> metadata = findMetadata(entity, setup, deploymentName); - - if (volumes != null) { - createPersistentVolumes(volumes); - } - - Namespace namespace = createOrGetNamespace(lookup(NAMESPACE, entity, setup), setup.get(CREATE_NAMESPACE)); - - if (secrets != null) { - createSecrets(namespace.getMetadata().getName(), secrets); - } - - Container container = buildContainer(namespace.getMetadata().getName(), metadata, deploymentName, imageName, inboundPorts, env, limits, privileged); - deploy(namespace.getMetadata().getName(), entity, metadata, deploymentName, container, replicas, secrets); - Service service = exposeService(namespace.getMetadata().getName(), metadata, deploymentName, inboundPorts); - Pod pod = getPod(namespace.getMetadata().getName(), metadata); - - entity.sensors().set(KubernetesPod.KUBERNETES_NAMESPACE, namespace.getMetadata().getName()); - entity.sensors().set(KubernetesPod.KUBERNETES_DEPLOYMENT, deploymentName); - entity.sensors().set(KubernetesPod.KUBERNETES_POD, pod.getMetadata().getName()); - entity.sensors().set(KubernetesPod.KUBERNETES_SERVICE, service.getMetadata().getName()); - - LocationSpec<KubernetesSshMachineLocation> locationSpec = prepareSshableLocationSpec(entity, setup, namespace, deploymentName, service, pod) - .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, namespace.getMetadata().getName()) - .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, deploymentName) - .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, getContainerResourceType()); - - KubernetesSshMachineLocation machine = getManagementContext().getLocationManager().createLocation(locationSpec); - registerPortMappings(machine, entity, service); - if (!isDockerContainer(entity)) { - waitForSshable(machine, Duration.FIVE_MINUTES); - } - - return machine; - } - - protected String getContainerResourceType() { - return KubernetesResource.DEPLOYMENT; - } - - protected void waitForSshable(final SshMachineLocation machine, Duration timeout) { - Callable<Boolean> checker = new Callable<Boolean>() { - public Boolean call() { - int exitstatus = machine.execScript( - ImmutableMap.of( // TODO investigate why SSH connection does not time out with this config - SshTool.PROP_CONNECT_TIMEOUT.getName(), Duration.TEN_SECONDS.toMilliseconds(), - SshTool.PROP_SESSION_TIMEOUT.getName(), Duration.TEN_SECONDS.toMilliseconds(), - SshTool.PROP_SSH_TRIES_TIMEOUT.getName(), Duration.TEN_SECONDS.toMilliseconds(), - SshTool.PROP_SSH_TRIES.getName(), 1), - "check-sshable", - ImmutableList.of("true")); - boolean success = (exitstatus == 0); - return success; - }}; - - Stopwatch stopwatch = Stopwatch.createStarted(); - ReferenceWithError<Boolean> reachable = Repeater.create("reachable") - .threaded() - .backoff(Duration.FIVE_SECONDS, 2, Duration.TEN_SECONDS) // Exponential backoff, to 10 seconds - .until(checker) - .limitTimeTo(timeout) - .runKeepingError(); - if (!reachable.getWithoutError()) { - throw new IllegalStateException("Connection failed for "+machine.getSshHostAndPort()+" after waiting "+stopwatch.elapsed(TimeUnit.SECONDS), reachable.getError()); - } else { - LOG.debug("Connection succeeded for {} after {}", machine.getSshHostAndPort(), stopwatch.elapsed(TimeUnit.SECONDS)); - } - } - - protected void registerPortMappings(KubernetesSshMachineLocation machine, Entity entity, Service service) { - PortForwardManager portForwardManager = (PortForwardManager) getManagementContext().getLocationRegistry() - .getLocationManaged(PortForwardManagerLocationResolver.PFM_GLOBAL_SPEC); - List<ServicePort> ports = service.getSpec().getPorts(); - String publicHostText = ((SshMachineLocation) machine).getSshHostAndPort().getHostText(); - LOG.debug("Recording port-mappings for container {} of {}: {}", new Object[] { machine, this, ports }); - - for (ServicePort port : ports) { - String protocol = port.getProtocol(); - Integer targetPort = port.getTargetPort().getIntVal(); - - if (!"TCP".equalsIgnoreCase(protocol)) { - LOG.debug("Ignoring port mapping {} for {} because only TCP is currently supported", port, machine); - } else if (targetPort == null) { - LOG.debug("Ignoring port mapping {} for {} because targetPort.intValue is null", port, machine); - } else if (port.getNodePort() == null) { - LOG.debug("Ignoring port mapping {} to {} because port.getNodePort() is null", targetPort, machine); - } else { - portForwardManager.associate(publicHostText, HostAndPort.fromParts(publicHostText, port.getNodePort()), machine, targetPort); - AttributeSensor<Integer> sensor = Sensors.newIntegerSensor("kubernetes." + Strings.maybeNonBlank(port.getName()).or(targetPort.toString()) + ".port"); - entity.sensors().set(sensor, targetPort); - } - } - - entity.enrichers().add(EnricherSpec.create(OnPublicNetworkEnricher.class).configure(OnPublicNetworkEnricher.MAP_MATCHING, "kubernetes.[a-zA-Z0-9][a-zA-Z0-9-_]*.port")); - } - - protected synchronized Namespace createOrGetNamespace(final String name, Boolean create) { - Namespace namespace = client.namespaces().withName(name).get(); - ExitCondition namespaceReady = new ExitCondition() { - @Override - public Boolean call() { - Namespace actualNamespace = client.namespaces().withName(name).get(); - return actualNamespace != null && actualNamespace.getStatus().getPhase().equals(PHASE_ACTIVE); - } - @Override - public String getFailureMessage() { - Namespace actualNamespace = client.namespaces().withName(name).get(); - return "Namespace for " + name + " " + (actualNamespace == null ? "absent" : " status " + actualNamespace.getStatus()); - } - }; - if (namespace != null) { - LOG.debug("Found namespace {}, returning it.", namespace); - } else if (create) { - namespace = client.namespaces().create(new NamespaceBuilder().withNewMetadata().withName(name).endMetadata().build()); - LOG.debug("Created namespace {}.", namespace); - } else { - throw new IllegalStateException("Namespace " + name + " does not exist and namespace.create is not set"); - } - waitForExitCondition(namespaceReady); - return client.namespaces().withName(name).get(); - } - - protected Pod getPod(final String namespace, final String name) { - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - Pod result = client.pods().inNamespace(namespace).withName(name).get(); - return result != null && result.getStatus().getPodIP() != null; - } - @Override - public String getFailureMessage() { - return "Cannot find pod with name: " + name; - } - }; - waitForExitCondition(exitCondition); - Pod result = client.pods().inNamespace(namespace).withName(name).get(); - return result; - } - - protected Pod getPod(final String namespace, final Map<String, String> metadata) { - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - PodList result = client.pods().inNamespace(namespace).withLabels(metadata).list(); - return result.getItems().size() >= 1 && result.getItems().get(0).getStatus().getPodIP() != null; - } - @Override - public String getFailureMessage() { - return "Cannot find pod with metadata: " + Joiner.on(" ").withKeyValueSeparator("=").join(metadata); - } - }; - waitForExitCondition(exitCondition); - PodList result = client.pods().inNamespace(namespace).withLabels(metadata).list(); - return result.getItems().get(0); - } - - protected void createSecrets(String namespace, Map<String, String> secrets) { - for (Map.Entry<String, String> nameAuthEntry : secrets.entrySet()) { - createSecret(namespace, nameAuthEntry.getKey(), nameAuthEntry.getValue()); - } - } - - protected Secret createSecret(final String namespace, final String secretName, String auth) { - Secret secret = client.secrets().inNamespace(namespace).withName(secretName).get(); - if (secret != null) return secret; - - String json = String.format("{\"https://index.docker.io/v1/\":{\"auth\":\"%s\"}}", auth); - String base64encoded = BaseEncoding.base64().encode(json.getBytes(Charset.defaultCharset())); - secret = new SecretBuilder() - .withNewMetadata() - .withName(secretName) - .endMetadata() - .withType(KUBERNETES_DOCKERCFG) - .withData(ImmutableMap.of(".dockercfg", base64encoded)) - .build(); - try { - client.secrets().inNamespace(namespace).create(secret); - } catch (KubernetesClientException e) { - if (e.getCode() == 500 && e.getMessage().contains("Message: resourceVersion may not be set on objects to be created")) { - // ignore exception as per https://github.com/fabric8io/kubernetes-client/issues/451 - } else { - throw Throwables.propagate(e); - } - } - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - return client.secrets().inNamespace(namespace).withName(secretName).get() != null; - } - @Override - public String getFailureMessage() { - return "Absent namespace=" + namespace + ", secretName=" + secretName; - } - }; - waitForExitCondition(exitCondition); - return client.secrets().inNamespace(namespace).withName(secretName).get(); - } - - protected Container buildContainer(String namespace, Map<String, String> metadata, String deploymentName, String imageName, Iterable<Integer> inboundPorts, Map<String, ?> env, Map<String, String> limits, boolean privileged) { - List<ContainerPort> containerPorts = Lists.newArrayList(); - for (Integer inboundPort : inboundPorts) { - containerPorts.add(new ContainerPortBuilder().withContainerPort(inboundPort).build()); - } - - List<EnvVar> envVars = Lists.newArrayList(); - for (Map.Entry<String, ?> envVarEntry : env.entrySet()) { - envVars.add(new EnvVarBuilder().withName(envVarEntry.getKey()).withValue(envVarEntry.getValue().toString()).build()); - } - - ContainerBuilder containerBuilder = new ContainerBuilder() - .withName(deploymentName) - .withImage(imageName) - .addToPorts(Iterables.toArray(containerPorts, ContainerPort.class)) - .addToEnv(Iterables.toArray(envVars, EnvVar.class)) - .withNewSecurityContext() - .withPrivileged(privileged) - .endSecurityContext(); - - if (limits != null) { - for (Map.Entry<String, String> nameValueEntry : limits.entrySet()) { - ResourceRequirements resourceRequirements = new ResourceRequirementsBuilder().addToLimits(nameValueEntry.getKey(), new QuantityBuilder().withAmount(nameValueEntry.getValue()).build()).build(); - containerBuilder.withResources(resourceRequirements); - } - } - LOG.debug("Built container {} to be deployed in namespace {} with metadata {}.", containerBuilder.build(), namespace, metadata); - return containerBuilder.build(); - } - - protected void deploy(final String namespace, Entity entity, Map<String, String> metadata, final String deploymentName, Container container, final Integer replicas, Map<String, String> secrets) { - PodTemplateSpecBuilder podTemplateSpecBuilder = new PodTemplateSpecBuilder() - .withNewMetadata() - .addToLabels("name", deploymentName) - .addToLabels(metadata) - .endMetadata() - .withNewSpec() - .addToContainers(container) - .endSpec(); - if (secrets != null) { - for (String secretName : secrets.keySet()) { - podTemplateSpecBuilder.withNewSpec() - .addToContainers(container) - .addNewImagePullSecret(secretName) - .endSpec(); - } - } - PodTemplateSpec template = podTemplateSpecBuilder.build(); - Deployment deployment = new DeploymentBuilder() - .withNewMetadata() - .withName(deploymentName) - .addToAnnotations(CLOUDSOFT_ENTITY_ID, entity.getId()) - .addToAnnotations(CLOUDSOFT_APPLICATION_ID, entity.getApplicationId()) - .endMetadata() - .withNewSpec() - .withReplicas(replicas) - .withTemplate(template) - .endSpec() - .build(); - client.extensions().deployments().inNamespace(namespace).create(deployment); - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - Deployment dep = client.extensions().deployments().inNamespace(namespace).withName(deploymentName).get(); - DeploymentStatus status = (dep == null) ? null : dep.getStatus(); - Integer replicas = (status == null) ? null : status.getAvailableReplicas(); - return replicas != null && replicas.intValue() == replicas; - } - @Override - public String getFailureMessage() { - Deployment dep = client.extensions().deployments().inNamespace(namespace).withName(deploymentName).get(); - DeploymentStatus status = (dep == null) ? null : dep.getStatus(); - return "Namespace=" + namespace + "; deploymentName= " + deploymentName + "; Deployment=" + dep - + "; status=" + status - + "; availableReplicas=" + (status == null ? "null" : status.getAvailableReplicas()); - } - }; - waitForExitCondition(exitCondition); - LOG.debug("Deployed deployment {} in namespace {}.", deployment, namespace); - } - - protected Service exposeService(String namespace, Map<String, String> metadata, String serviceName, Iterable<Integer> inboundPorts) { - List<ServicePort> servicePorts = Lists.newArrayList(); - for (Integer inboundPort : inboundPorts) { - servicePorts.add(new ServicePortBuilder().withName(Integer.toString(inboundPort)).withPort(inboundPort).build()); - } - Service service = new ServiceBuilder().withNewMetadata().withName(serviceName).endMetadata() - .withNewSpec() - .addToSelector(metadata) - .addToPorts(Iterables.toArray(servicePorts, ServicePort.class)) - .withType(NODE_PORT) - .endSpec() - .build(); - client.services().inNamespace(namespace).create(service); - - service = getService(namespace, serviceName); - LOG.debug("Exposed service {} in namespace {}.", service, namespace); - return service; - } - - protected Service getService(final String namespace, final String serviceName) { - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - Service svc = client.services().inNamespace(namespace).withName(serviceName).get(); - if (svc == null || svc.getStatus() == null) { - return false; - } - Endpoints endpoints = client.endpoints().inNamespace(namespace).withName(serviceName).get(); - if (endpoints == null || endpoints.getSubsets().isEmpty()) { - return false; - } - for (EndpointSubset subset : endpoints.getSubsets()) { - if (subset.getNotReadyAddresses().size() > 0) { - return false; - } - } - return true; - } - @Override - public String getFailureMessage() { - Endpoints endpoints = client.endpoints().inNamespace(namespace).withName(serviceName).get(); - return "Service endpoints in " + namespace + " for serviceName= " + serviceName + " not ready: " + endpoints; - } - }; - waitForExitCondition(exitCondition); - - return client.services().inNamespace(namespace).withName(serviceName).get(); - } - - protected LocationSpec<KubernetesSshMachineLocation> prepareSshableLocationSpec(Entity entity, ConfigBag setup, Namespace namespace, String deploymentName, Service service, Pod pod) { - InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName()); - String podAddress = pod.getStatus().getPodIP(); - LocationSpec<KubernetesSshMachineLocation> locationSpec = LocationSpec.create(KubernetesSshMachineLocation.class) - .configure("address", node) - .configure(SshMachineLocation.PRIVATE_ADDRESSES, ImmutableSet.of(podAddress)) - .configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT)); - if (!isDockerContainer(entity)) { - Optional<ServicePort> sshPort = Iterables.tryFind(service.getSpec().getPorts(), new Predicate<ServicePort>() { - @Override - public boolean apply(ServicePort input) { - return input.getProtocol().equalsIgnoreCase("TCP") && input.getPort().intValue() == 22; - } - }); - Optional<Integer> sshPortNumber; - if (sshPort.isPresent()) { - sshPortNumber = Optional.of(sshPort.get().getNodePort()); - } else { - LOG.warn("No port-mapping found to ssh port 22, for container {}", service); - sshPortNumber = Optional.absent(); - } - locationSpec.configure(CloudLocationConfig.USER, setup.get(KubernetesLocationConfig.LOGIN_USER)) - .configure(SshMachineLocation.PASSWORD, setup.get(KubernetesLocationConfig.LOGIN_USER_PASSWORD)) - .configureIfNotNull(SshMachineLocation.SSH_PORT, sshPortNumber.orNull()) - .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true) - .configure(BrooklynConfigKeys.ONBOX_BASE_DIR, "/tmp"); - } - return locationSpec; - } - - protected void createPersistentVolumes(List<String> volumes) { - for (final String persistentVolume : volumes) { - PersistentVolume volume = new PersistentVolumeBuilder() - .withNewMetadata() - .withName(persistentVolume) - .withLabels(ImmutableMap.of("type", "local")) // TODO make it configurable - .endMetadata() - .withNewSpec() - .addToCapacity("storage", new QuantityBuilder().withAmount("20").build()) // TODO make it configurable - .addToAccessModes("ReadWriteOnce") // TODO make it configurable - .withNewHostPath().withPath("/tmp/pv-1").endHostPath() // TODO make it configurable - .endSpec() - .build(); - client.persistentVolumes().create(volume); - ExitCondition exitCondition = new ExitCondition() { - @Override - public Boolean call() { - PersistentVolume pv = client.persistentVolumes().withName(persistentVolume).get(); - return pv != null && pv.getStatus() != null - && pv.getStatus().getPhase().equals(PHASE_AVAILABLE); - } - @Override - public String getFailureMessage() { - PersistentVolume pv = client.persistentVolumes().withName(persistentVolume).get(); - return "PersistentVolume for " + persistentVolume + " " + (pv == null ? "absent" : "pv=" + pv); - } - }; - waitForExitCondition(exitCondition); - } - } - - protected Entity validateCallerContext(ConfigBag setup) { - // Lookup entity flags - Object callerContext = setup.get(LocationConfigKeys.CALLER_CONTEXT); - if (callerContext instanceof Entity) { - return (Entity) callerContext; - } else { - throw new IllegalStateException("Invalid caller context: " + callerContext); - } - } - - protected Entity validateCallerContext(MachineLocation machine) { - // Lookup entity flags - Object callerContext = machine.config().get(LocationConfigKeys.CALLER_CONTEXT); - if (callerContext instanceof Entity) { - return (Entity) callerContext; - } else { - throw new IllegalStateException("Invalid caller context: " + callerContext); - } - } - - protected Map<String, String> findMetadata(Entity entity, ConfigBag setup, String value) { - Map<String, String> podMetadata = Maps.newLinkedHashMap(); - if (isDockerContainer(entity)) { - podMetadata.put(IMMUTABLE_CONTAINER_KEY, value); - } else { - podMetadata.put(SSHABLE_CONTAINER, value); - } - - Map<String, Object> metadata = MutableMap.<String, Object>builder() - .putAll(MutableMap.copyOf(setup.get(KubernetesPod.METADATA))) - .putAll(MutableMap.copyOf(entity.config().get(KubernetesPod.METADATA))) - .putAll(podMetadata) - .build(); - return Maps.transformValues(metadata, Functions.toStringFunction()); - } - - /** - * Sets the {@code CLOUDSOFT_ROOT_PASSWORD} variable in the container environment if appropriate. - * This is (approximately) the same behaviour as the {@link DockerJcloudsLocation} used for - * Swarm. - * - * Side-effects the location {@code config} to set the {@link KubernetesLocationConfig#LOGIN_USER_PASSWORD loginUser.password} - * if one is auto-generated. Note that this injected value overrides any other settings configured for the - * container environment. - */ - protected Map<String, String> findEnvironmentVariables(Entity entity, ConfigBag setup, String imageName) { - String loginUser = setup.get(LOGIN_USER); - String loginPassword = setup.get(LOGIN_USER_PASSWORD); - Map<String, String> injections = Maps.newLinkedHashMap(); - - // Check if login credentials should be injected - Boolean injectLoginCredentials = setup.get(INJECT_LOGIN_CREDENTIAL); - if (injectLoginCredentials == null) { - for (String regex : IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS) { - if (imageName != null && imageName.matches(regex)) { - injectLoginCredentials = true; - break; - } - } - } - - if (Boolean.TRUE.equals(injectLoginCredentials)) { - if ((Strings.isBlank(loginUser) || "root".equals(loginUser))) { - loginUser = "root"; - setup.configure(LOGIN_USER, loginUser); - - if (Strings.isBlank(loginPassword)) { - loginPassword = Identifiers.makeRandomPassword(12); - setup.configure(LOGIN_USER_PASSWORD, loginPassword); - } - - injections.put(CLOUDSOFT_ROOT_PASSWORD, loginPassword); - } - } - - Map<String,Object> rawEnv = MutableMap.<String, Object>builder() - .putAll(MutableMap.copyOf(setup.get(ENV))) - .putAll(MutableMap.copyOf(entity.config().get(DockerContainer.CONTAINER_ENVIRONMENT))) - .putAll(injections) - .build(); - return Maps.transformValues(rawEnv, Functions.toStringFunction()); - } - - protected Iterable<Integer> findInboundPorts(Entity entity, ConfigBag setup) { - Iterable<String> inboundTcpPorts = entity.config().get(DockerContainer.INBOUND_TCP_PORTS); - if (inboundTcpPorts != null) { - List<Integer> inboundPorts = Lists.newArrayList(); - List<String> portRanges = MutableList.copyOf(entity.config().get(DockerContainer.INBOUND_TCP_PORTS)); - for (String portRange : portRanges) { - for (Integer port : PortRanges.fromString(portRange)) { - inboundPorts.add(port); - } - } - return inboundPorts; - } else { - if (setup.containsKey(INBOUND_PORTS)) { - return toIntPortList(setup.get(INBOUND_PORTS)); - } else { - return ImmutableList.of(22); - } - } - } - - protected List<Integer> toIntPortList(Object v) { - if (v == null) return ImmutableList.of(); - PortRange portRange = PortRanges.fromIterable(ImmutableList.of(v)); - return ImmutableList.copyOf(portRange); - } - - protected String findImageName(Entity entity, ConfigBag setup) { - String result = entity.config().get(DockerContainer.IMAGE_NAME); - if (Strings.isNonBlank(result)) return result; - - result = setup.get(IMAGE); - if (Strings.isNonBlank(result)) return result; - - String osFamily = setup.get(OS_FAMILY); - String osVersion = setup.get(OS_VERSION_REGEX); - Optional<String> imageName = new ImageChooser().chooseImage(osFamily, osVersion); - if (imageName.isPresent()) return imageName.get(); - - throw new IllegalStateException("No matching image found for " + entity - + " (no explicit image name, osFamily=" + osFamily + "; osVersion=" + osVersion + ")"); - } - - protected boolean isDockerContainer(Entity entity) { - return implementsInterface(entity, DockerContainer.class); - } - - protected boolean isKubernetesPod(Entity entity) { - return implementsInterface(entity, KubernetesPod.class); - } - - protected boolean isKubernetesResource(Entity entity) { - return implementsInterface(entity, KubernetesResource.class); - } - - public boolean implementsInterface(Entity entity, Class<?> type) { - return Iterables.tryFind(Arrays.asList(entity.getClass().getInterfaces()), Predicates.assignableFrom(type)).isPresent(); - } - - @Override - public MachineProvisioningLocation<KubernetesMachineLocation> newSubLocation(Map<?, ?> newFlags) { - throw new UnsupportedOperationException(); - } - - /** @see {@link #lookup(ConfigKey, Entity, ConfigBag, Object)} */ - public <T> T lookup(ConfigKey<T> config, Entity entity, ConfigBag setup) { - return lookup(config, entity, setup, null); - } - - /** - * Looks up {@link ConfigKey configuration} with the entity value taking precedence over the - * location, and returning a default value (normally {@literal null}) if neither is present. - */ - public <T> T lookup(ConfigKey<T> config, Entity entity, ConfigBag setup, T defaultValue) { - Optional<T> entityValue = Optional.fromNullable(entity.config().get(config)); - Optional<T> locationValue = Optional.fromNullable(setup.get(config)); - - return Iterables.getFirst(Optional.presentInstances(Arrays.asList(entityValue, locationValue)), defaultValue); - } - - public void waitForExitCondition(ExitCondition exitCondition) { - waitForExitCondition(exitCondition, Duration.ONE_SECOND, Duration.FIVE_MINUTES); - } - - public void waitForExitCondition(ExitCondition exitCondition, Duration initial, Duration duration) { - ReferenceWithError<Boolean> result = Repeater.create() - .backoff(initial, 1.2, duration) - .limitTimeTo(duration) - .until(exitCondition) - .runKeepingError(); - if (!result.get()) { - String err = "Exit condition unsatisfied after " + duration + ": " + exitCondition.getFailureMessage(); - LOG.info(err + " (rethrowing)"); - throw new IllegalStateException(err); - } - } - - public static interface ExitCondition extends Callable<Boolean> { - public String getFailureMessage(); - } -}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java deleted file mode 100644 index 0571fc2..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java +++ /dev/null @@ -1,164 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import java.util.Map; - -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.location.LocationConfigKeys; -import org.apache.brooklyn.core.location.cloud.CloudLocationConfig; -import org.apache.brooklyn.util.time.Duration; - -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableMap; -import com.google.common.reflect.TypeToken; - -public interface KubernetesLocationConfig extends CloudLocationConfig { - - ConfigKey<String> MASTER_URL = LocationConfigKeys.CLOUD_ENDPOINT; - - ConfigKey<String> CA_CERT_DATA = ConfigKeys.builder(String.class) - .name("caCertData") - .description("Data for CA certificate") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CA_CERT_FILE = ConfigKeys.builder(String.class) - .name("caCertFile") - .description("URL of resource containing CA certificate data") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_CERT_DATA = ConfigKeys.builder(String.class) - .name("clientCertData") - .description("Data for client certificate") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_CERT_FILE = ConfigKeys.builder(String.class) - .name("clientCertFile") - .description("URL of resource containing client certificate data") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_KEY_DATA = ConfigKeys.builder(String.class) - .name("clientKeyData") - .description("Data for client key") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_KEY_FILE = ConfigKeys.builder(String.class) - .name("clientKeyFile") - .description("URL of resource containing client key data") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_KEY_ALGO = ConfigKeys.builder(String.class) - .name("clientKeyAlgo") - .description("Algorithm used for the client key") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> CLIENT_KEY_PASSPHRASE = ConfigKeys.builder(String.class) - .name("clientKeyPassphrase") - .description("Passphrase used for the client key") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> OAUTH_TOKEN = ConfigKeys.builder(String.class) - .name("oauthToken") - .description("The OAuth token data for the current user") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<Duration> CLIENT_TIMEOUT = ConfigKeys.builder(Duration.class) - .name("timeout") - .description("The timeout for the client") - .defaultValue(Duration.seconds(10)) - .constraint(Predicates.<Duration>notNull()) - .build(); - - ConfigKey<Duration> ACTION_TIMEOUT = ConfigKeys.builder(Duration.class) - .name("actionTimeout") - .description("The timeout for Kubernetes actions") - .defaultValue(Duration.ONE_MINUTE) - .constraint(Predicates.<Duration>notNull()) - .build(); - - ConfigKey<Boolean> CREATE_NAMESPACE = ConfigKeys.builder(Boolean.class) - .name("namespace.create") - .description("Whether to create the namespace if it does not exist") - .defaultValue(true) - .constraint(Predicates.<Boolean>notNull()) - .build(); - - ConfigKey<Boolean> DELETE_EMPTY_NAMESPACE = ConfigKeys.builder(Boolean.class) - .name("namespace.deleteEmpty") - .description("Whether to delete an empty namespace when releasing resources") - .defaultValue(false) - .constraint(Predicates.<Boolean>notNull()) - .build(); - - ConfigKey<String> NAMESPACE = ConfigKeys.builder(String.class) - .name("namespace") - .description("Namespace where resources will live; the default is 'amp'") - .defaultValue("amp") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<Boolean> PRIVILEGED = ConfigKeys.builder(Boolean.class) - .name("privileged") - .description("Whether the pods use privileged containers") - .defaultValue(false) - .build(); - - @SuppressWarnings("serial") - ConfigKey<Map<String, ?>> ENV = ConfigKeys.builder(new TypeToken<Map<String, ?>>() {}) - .name("env") - .description("Environment variables to inject when starting the container") - .defaultValue(ImmutableMap.<String, Object>of()) - .constraint(Predicates.<Map<String, ?>>notNull()) - .build(); - - ConfigKey<String> IMAGE = ConfigKeys.builder(String.class) - .name("image") - .description("Docker image to be deployed into the pod") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> OS_FAMILY = ConfigKeys.builder(String.class) - .name("osFamily") - .description("OS family, e.g. CentOS, Ubuntu") - .build(); - - ConfigKey<String> OS_VERSION_REGEX = ConfigKeys.builder(String.class) - .name("osVersionRegex") - .description("Regular expression for the OS version to load") - .build(); - - ConfigKey<KubernetesClientRegistry> KUBERNETES_CLIENT_REGISTRY = ConfigKeys.builder(KubernetesClientRegistry.class) - .name("kubernetesClientRegistry") - .description("Registry/Factory for creating Kubernetes client; default is almost always fine, " - + "except where tests want to customize behaviour") - .defaultValue(KubernetesClientRegistryImpl.INSTANCE) - .build(); - - ConfigKey<String> LOGIN_USER = ConfigKeys.builder(String.class) - .name("loginUser") - .description("Override the user who logs in initially to perform setup") - .defaultValue("root") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<String> LOGIN_USER_PASSWORD = ConfigKeys.builder(String.class) - .name("loginUser.password") - .description("Custom password for the user who logs in initially") - .constraint(Predicates.<String>notNull()) - .build(); - - ConfigKey<Boolean> INJECT_LOGIN_CREDENTIAL = ConfigKeys.builder(Boolean.class) - .name("injectLoginCredential") - .description("Whether to inject login credentials (if null, will infer from image choice); ignored if explicit 'loginUser.password' supplied") - .build(); - -} - http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationResolver.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationResolver.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationResolver.java deleted file mode 100644 index 79e7c64..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationResolver.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Locations starting with the given prefix (@code "kubernetes") will use this resolver, to instantiate - * a {@link KubernetesLocation}. - * - * 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.kubernetes.} - * <li>Prefix {@code brooklyn.kubernetes.} - * </ol> - */ -public class KubernetesLocationResolver extends AbstractLocationResolver implements LocationResolver { - - public static final Logger log = LoggerFactory.getLogger(KubernetesLocationResolver.class); - - public static final String PREFIX = "kubernetes"; - - @Override - public boolean isEnabled() { - return LocationConfigUtils.isResolverPrefixEnabled(managementContext, getPrefix()); - } - - @Override - public String getPrefix() { - return PREFIX; - } - - @Override - protected Class<? extends Location> getLocationType() { - return KubernetesLocation.class; - } - - @Override - protected SpecParser getSpecParser() { - return new SpecParser(getPrefix()).setExampleUsage("\"kubernetes\""); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesEmptyMachineLocation.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesEmptyMachineLocation.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesEmptyMachineLocation.java deleted file mode 100644 index 8875ef3..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesEmptyMachineLocation.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location.machine; - -import java.net.InetAddress; -import java.util.Set; - -import org.apache.brooklyn.api.location.MachineDetails; -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.api.location.OsDetails; -import org.apache.brooklyn.location.ssh.SshMachineLocation; -import org.apache.brooklyn.util.net.Networking; - -import com.google.common.collect.ImmutableSet; - -/** - * A {@link MachineLocation} represemnting a Kubernetes resource that does not support SSH access. - * - * @see {@link KubernetesSshMachineLocation} - */ -public class KubernetesEmptyMachineLocation extends SshMachineLocation implements KubernetesMachineLocation { - - @Override - public String getHostname() { - return getResourceName(); - } - - @Override - public Set<String> getPublicAddresses() { - return ImmutableSet.of("0.0.0.0"); - } - - @Override - public Set<String> getPrivateAddresses() { - return ImmutableSet.of("0.0.0.0"); - } - - @Override - public InetAddress getAddress() { - return Networking.getInetAddressWithFixedName("0.0.0.0"); - } - - @Override - public OsDetails getOsDetails() { - return null; - // throw new UnsupportedOperationException("No OS details for empty KubernetesMachineLocation"); - } - - @Override - public MachineDetails getMachineDetails() { - return null; - // throw new UnsupportedOperationException("No machine details for empty KubernetesMachineLocation"); - } - - @Override - public String getResourceName() { - return config().get(KUBERNETES_RESOURCE_NAME); - } - - @Override - public String getResourceType() { - return config().get(KUBERNETES_RESOURCE_TYPE); - } - - @Override - public String getNamespace() { - return config().get(KUBERNETES_NAMESPACE); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesMachineLocation.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesMachineLocation.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesMachineLocation.java deleted file mode 100644 index 6d8838b..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesMachineLocation.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location.machine; - -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; - -public interface KubernetesMachineLocation extends MachineLocation { - - ConfigKey<String> KUBERNETES_NAMESPACE = ConfigKeys.builder(String.class, "kubernetes.namespace") - .description("Namespace for the KubernetesMachineLocation") - .build(); - - ConfigKey<String> KUBERNETES_RESOURCE_NAME = ConfigKeys.builder(String.class, "kubernetes.name") - .description("Name of the resource represented by the KubernetesMachineLocation") - .build(); - - ConfigKey<String> KUBERNETES_RESOURCE_TYPE = ConfigKeys.builder(String.class, "kubernetes.type") - .description("Type of the resource represented by the KubernetesMachineLocation") - .build(); - - public String getResourceName(); - - public String getResourceType(); - - public String getNamespace(); - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesSshMachineLocation.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesSshMachineLocation.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesSshMachineLocation.java deleted file mode 100644 index ad83f8f..0000000 --- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/machine/KubernetesSshMachineLocation.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location.machine; - -import org.apache.brooklyn.api.location.MachineLocation; -import org.apache.brooklyn.location.ssh.SshMachineLocation; - -/** - * A {@link MachineLocation} represemnting a Kubernetes resource that allows SSH access. - * - * @see {@link KubernetesSshMachineLocation} - */ -public class KubernetesSshMachineLocation extends SshMachineLocation implements KubernetesMachineLocation { - - @Override - public String getResourceName() { - return config().get(KUBERNETES_RESOURCE_NAME); - } - - @Override - public String getResourceType() { - return config().get(KUBERNETES_RESOURCE_TYPE); - } - - @Override - public String getNamespace() { - return config().get(KUBERNETES_NAMESPACE); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooserTest.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooserTest.java b/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooserTest.java deleted file mode 100644 index 8f28929..0000000 --- a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/ImageChooserTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; - -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -public class ImageChooserTest { - - private ImageChooser chooser; - - @BeforeMethod(alwaysRun=true) - public void setUp() { - chooser = new ImageChooser(); - } - - @Test - public void testDefault() throws Exception { - assertEquals(chooser.chooseImage((String)null, null).get(), "cloudsoft/centos:7"); - } - - @Test - public void testCentos() throws Exception { - assertEquals(chooser.chooseImage("cEnToS", null).get(), "cloudsoft/centos:7"); - } - - @Test - public void testCentos7() throws Exception { - assertEquals(chooser.chooseImage("cEnToS", "7").get(), "cloudsoft/centos:7"); - } - - @Test - public void testUbnutu() throws Exception { - assertEquals(chooser.chooseImage("uBuNtU", null).get(), "cloudsoft/ubuntu:14.04"); - } - - @Test - public void testUbnutu14() throws Exception { - assertEquals(chooser.chooseImage("uBuNtU", "14.*").get(), "cloudsoft/ubuntu:14.04"); - } - - @Test - public void testUbnutu16() throws Exception { - assertEquals(chooser.chooseImage("uBuNtU", "16.*").get(), "cloudsoft/ubuntu:16.04"); - } - - @Test - public void testAbsentForCentos6() throws Exception { - assertFalse(chooser.chooseImage("cEnToS", "6").isPresent()); - } - - @Test - public void testAbsentForUbuntu15() throws Exception { - assertFalse(chooser.chooseImage("uBuNtU", "15").isPresent()); - } - - @Test - public void testAbsentForDebian() throws Exception { - assertFalse(chooser.chooseImage("debian", null).isPresent()); - } - - @Test - public void testAbsentForWrongOsFamily() throws Exception { - assertFalse(chooser.chooseImage("weirdOsFamily", null).isPresent()); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/445884b1/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCertsTest.java ---------------------------------------------------------------------- diff --git a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCertsTest.java b/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCertsTest.java deleted file mode 100644 index 27c1b79..0000000 --- a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesCertsTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.cloudsoft.amp.containerservice.kubernetes.location; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; - -import java.io.File; -import java.util.List; - -import org.apache.brooklyn.test.Asserts; -import org.apache.brooklyn.test.LogWatcher; -import org.apache.brooklyn.test.LogWatcher.EventPredicates; -import org.apache.brooklyn.util.core.config.ConfigBag; -import org.apache.brooklyn.util.text.Identifiers; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import com.google.common.base.Charsets; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.io.Files; - -import ch.qos.logback.classic.spi.ILoggingEvent; - -public class KubernetesCertsTest { - - private List<File> tempFiles; - - @BeforeMethod(alwaysRun=true) - public void setUp() throws Exception { - tempFiles = Lists.newArrayList(); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() throws Exception { - if (tempFiles != null) { - for (File tempFile : tempFiles) { - tempFile.delete(); - } - } - } - - @Test - public void testCertsAbsent() throws Exception { - ConfigBag config = ConfigBag.newInstance(); - KubernetesCerts certs = new KubernetesCerts(config); - - assertFalse(certs.caCertData.isPresent()); - assertFalse(certs.clientCertData.isPresent()); - assertFalse(certs.clientKeyData.isPresent()); - assertFalse(certs.clientKeyAlgo.isPresent()); - assertFalse(certs.clientKeyPassphrase.isPresent()); - } - - @Test - public void testCertsFromData() throws Exception { - ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() - .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") - .put(KubernetesLocationConfig.CLIENT_CERT_DATA, "myClientCertData") - .put(KubernetesLocationConfig.CLIENT_KEY_DATA, "myClientKeyData") - .put(KubernetesLocationConfig.CLIENT_KEY_ALGO, "myClientKeyAlgo") - .put(KubernetesLocationConfig.CLIENT_KEY_PASSPHRASE, "myClientKeyPassphrase") - .build()); - KubernetesCerts certs = new KubernetesCerts(config); - - assertEquals(certs.caCertData.get(), "myCaCertData"); - assertEquals(certs.clientCertData.get(), "myClientCertData"); - assertEquals(certs.clientKeyData.get(), "myClientKeyData"); - assertEquals(certs.clientKeyAlgo.get(), "myClientKeyAlgo"); - assertEquals(certs.clientKeyPassphrase.get(), "myClientKeyPassphrase"); - } - - @Test - public void testCertsFromFile() throws Exception { - ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() - .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("myCaCertData").getAbsolutePath()) - .put(KubernetesLocationConfig.CLIENT_CERT_FILE, newTempFile("myClientCertData").getAbsolutePath()) - .put(KubernetesLocationConfig.CLIENT_KEY_FILE, newTempFile("myClientKeyData").getAbsolutePath()) - .build()); - KubernetesCerts certs = new KubernetesCerts(config); - - assertEquals(certs.caCertData.get(), "myCaCertData"); - assertEquals(certs.clientCertData.get(), "myClientCertData"); - assertEquals(certs.clientKeyData.get(), "myClientKeyData"); - } - - @Test - public void testCertsFailsIfConflictingConfig() throws Exception { - ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() - .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") - .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("differentCaCertData").getAbsolutePath()) - .build()); - try { - new KubernetesCerts(config); - Asserts.shouldHaveFailedPreviously(); - } catch (Exception e) { - Asserts.expectedFailureContains(e, "Duplicate conflicting configuration for caCertData and caCertFile"); - } - } - - @Test - public void testCertsWarnsIfConflictingConfig() throws Exception { - ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() - .put(KubernetesLocationConfig.CA_CERT_DATA, "myCaCertData") - .put(KubernetesLocationConfig.CA_CERT_FILE, newTempFile("myCaCertData").getAbsolutePath()) - .build()); - - String loggerName = KubernetesCerts.class.getName(); - ch.qos.logback.classic.Level logLevel = ch.qos.logback.classic.Level.WARN; - Predicate<ILoggingEvent> filter = EventPredicates.containsMessage("Duplicate (matching) configuration for " - + "caCertData and caCertFile (continuing)"); - LogWatcher watcher = new LogWatcher(loggerName, logLevel, filter); - - watcher.start(); - KubernetesCerts certs; - try { - certs = new KubernetesCerts(config); - watcher.assertHasEvent(); - } finally { - watcher.close(); - } - - assertEquals(certs.caCertData.get(), "myCaCertData"); - } - - @Test - public void testCertsFailsIfFileNotFound() throws Exception { - ConfigBag config = ConfigBag.newInstance(ImmutableMap.builder() - .put(KubernetesLocationConfig.CA_CERT_FILE, "/path/to/fileDoesNotExist-"+Identifiers.makeRandomId(8)) - .build()); - try { - new KubernetesCerts(config); - Asserts.shouldHaveFailedPreviously(); - } catch (Exception e) { - Asserts.expectedFailureContains(e, "not found on classpath or filesystem"); - } - } - - private File newTempFile(String contents) throws Exception { - File file = File.createTempFile("KubernetesCertsTest", ".txt"); - tempFiles.add(file); - Files.write(contents, file, Charsets.UTF_8); - return file; - } -}
