Merge tag 'apache-brooklyn' into feature/container-service

* tag 'apache-brooklyn': (339 commits)
  Removed references to AMP and QA framework
  Change tabs to spaces in BOM
  Fix README documentation and typos in BOM file
  Removes Docs as they need to be re-writtern to fit into Brooklyn
  Updates LICENCE to Apache-2.0
  Fix openshift config so location config is used
  white_check_mark: Fix build and cleanup
  no_entry: Updates package signatures
  no_entry: Merge resources into new location
  no_entry: Move openshift classes to new location
  no_entry: Move kubernetes classes to new location
  no_entry: Move docker classes to new location
  Tidying up for open-sourcing of project
  Filter out empty KubernetesEmptyMachineLocation for KubernetesPod 
compatibility
  Update wordpress image tag to 4-apache
  Fix handling of location and Kubernetes live tests
  Organize imports
  Use KubernetesMachineLocation correctly in location specs
  Add test for OpenShiftResource deployment and update other OpenShift tests
  Handle multiple addresses for services and set resource type correctly
  ...


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/92a65d45
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/92a65d45
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/92a65d45

Branch: refs/heads/master
Commit: 92a65d4507ae6fa9a576ba03c89c694f6c8e21b7
Parents: 6bdad92 975807b
Author: Andrew Donald Kennedy <[email protected]>
Authored: Mon Jun 12 14:58:16 2017 -0400
Committer: Andrew Donald Kennedy <[email protected]>
Committed: Mon Jun 12 14:58:16 2017 -0400

----------------------------------------------------------------------
 locations/container/pom.xml                     |  153 +++
 .../entity/docker/DockerContainer.java          |   91 ++
 .../entity/docker/DockerContainerImpl.java      |   88 ++
 .../entity/kubernetes/KubernetesPod.java        |  101 ++
 .../entity/kubernetes/KubernetesPodImpl.java    |   25 +
 .../entity/kubernetes/KubernetesResource.java   |   58 +
 .../kubernetes/KubernetesResourceImpl.java      |   36 +
 .../entity/openshift/OpenShiftPod.java          |   27 +
 .../entity/openshift/OpenShiftPodImpl.java      |   25 +
 .../entity/openshift/OpenShiftResource.java     |   32 +
 .../entity/openshift/OpenShiftResourceImpl.java |   24 +
 .../location/docker/DockerJcloudsLocation.java  |  196 ++++
 .../location/docker/DockerLocationResolver.java |   89 ++
 .../location/kubernetes/ImageChooser.java       |   85 ++
 .../location/kubernetes/KubernetesCerts.java    |   74 ++
 .../kubernetes/KubernetesClientRegistry.java    |   28 +
 .../KubernetesClientRegistryImpl.java           |   97 ++
 .../location/kubernetes/KubernetesLocation.java | 1052 ++++++++++++++++++
 .../kubernetes/KubernetesLocationConfig.java    |  182 +++
 .../kubernetes/KubernetesLocationResolver.java  |   65 ++
 .../machine/KubernetesEmptyMachineLocation.java |   85 ++
 .../machine/KubernetesMachineLocation.java      |   45 +
 .../machine/KubernetesSshMachineLocation.java   |   46 +
 .../openshift/OpenShiftClientRegistryImpl.java  |   44 +
 .../location/openshift/OpenShiftLocation.java   |  261 +++++
 .../openshift/OpenShiftLocationConfig.java      |   32 +
 .../openshift/OpenShiftLocationResolver.java    |   65 ++
 ...pache.brooklyn.api.location.LocationResolver |   21 +
 .../resources/OSGI-INF/blueprint/blueprint.xml  |   37 +
 .../docker/DockerJcloudsLocationLiveTest.java   |  265 +++++
 .../docker/DockerLocationResolverTest.java      |  118 ++
 .../location/kubernetes/ImageChooserTest.java   |   85 ++
 .../kubernetes/KubernetesCertsTest.java         |  162 +++
 .../kubernetes/KubernetesLocationLiveTest.java  |  235 ++++
 .../KubernetesLocationResolverTest.java         |  102 ++
 .../KubernetesLocationYamlLiveTest.java         |  521 +++++++++
 .../openshift/OpenShiftLocationLiveTest.java    |   65 ++
 .../OpenShiftLocationResolverTest.java          |  103 ++
 .../OpenShiftLocationYamlLiveTest.java          |  141 +++
 .../resources/nginx-replication-controller.yaml |   37 +
 .../src/test/resources/nginx-service.yaml       |   29 +
 41 files changed, 5027 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/pom.xml
----------------------------------------------------------------------
diff --cc locations/container/pom.xml
index 0000000,0000000..b5dd506
new file mode 100644
--- /dev/null
+++ b/locations/container/pom.xml
@@@ -1,0 -1,0 +1,153 @@@
++<?xml version="1.0" encoding="UTF-8"?>
++<!--
++    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.
++-->
++<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns="http://maven.apache.org/POM/4.0.0";
++         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
++    <modelVersion>4.0.0</modelVersion>
++    <artifactId>brooklyn-locations-container</artifactId>
++    <packaging>jar</packaging>
++    <name>Brooklyn Container Location Targets</name>
++
++    <parent>
++        <groupId>org.apache.brooklyn</groupId>
++        <artifactId>brooklyn-parent</artifactId>
++        <version>0.12.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
++    </parent>
++
++    <properties>
++        <kubernetes-client.version>1.4.27</kubernetes-client.version>
++    </properties>
++    <dependencies>
++        <dependency>
++            <groupId>io.fabric8</groupId>
++            <artifactId>kubernetes-client</artifactId>
++            <version>${kubernetes-client.version}</version>
++            <classifier>bundle</classifier>
++            <exclusions>
++                <exclusion>
++                    <groupId>com.squareup.okio</groupId>
++                    <artifactId>okio</artifactId>
++                </exclusion>
++            </exclusions>
++        </dependency>
++        <dependency>
++            <groupId>io.fabric8</groupId>
++            <artifactId>openshift-client</artifactId>
++            <version>${kubernetes-client.version}</version>
++            <classifier>bundle</classifier>
++        </dependency>
++
++        <!--
++            Expect Brooklyn to exclude this, and to choose its own 
jclouds-docker version.
++            So if this version is not kept up-to-date then it won't impact 
Brooklyn.
++        -->
++        <dependency>
++            <groupId>${jclouds.groupId}.api</groupId>
++            <artifactId>docker</artifactId>
++            <version>${jclouds.version}</version>
++            <exclusions>
++                <exclusion>
++                    <!-- Conflicts with javax.ws.rs-api -->
++                    <groupId>javax.ws.rs</groupId>
++                    <artifactId>jsr311-api</artifactId>
++                </exclusion>
++            </exclusions>
++        </dependency>
++
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-core</artifactId>
++            <version>${brooklyn.version}</version>
++        </dependency>
++
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-software-base</artifactId>
++            <version>${brooklyn.version}</version>
++        </dependency>
++
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-locations-jclouds</artifactId>
++            <version>${brooklyn.version}</version>
++        </dependency>
++        <dependency>
++            <groupId>org.slf4j</groupId>
++            <artifactId>slf4j-api</artifactId>
++        </dependency>
++
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-test-support</artifactId>
++            <version>${brooklyn.version}</version>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-utils-test-support</artifactId>
++            <version>${brooklyn.version}</version>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-core</artifactId>
++            <version>${brooklyn.version}</version>
++            <classifier>tests</classifier>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-locations-jclouds</artifactId>
++            <version>${brooklyn.version}</version>
++            <classifier>tests</classifier>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-launcher</artifactId>
++            <version>${brooklyn.version}</version>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-launcher</artifactId>
++            <version>${brooklyn.version}</version>
++            <type>test-jar</type>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.apache.brooklyn</groupId>
++            <artifactId>brooklyn-camp</artifactId>
++            <version>${brooklyn.version}</version>
++            <classifier>tests</classifier>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>org.testng</groupId>
++            <artifactId>testng</artifactId>
++            <scope>test</scope>
++        </dependency>
++        <dependency>
++            <groupId>javax.ws.rs</groupId>
++            <artifactId>javax.ws.rs-api</artifactId>
++            <version>${jax-rs-api.version}</version>
++            <scope>test</scope>
++        </dependency>
++    </dependencies>
++</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java
index 0000000,0000000..f8831a4
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainer.java
@@@ -1,0 -1,0 +1,91 @@@
++/*
++ * 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.entity.docker;
++
++import com.google.common.collect.ImmutableMap;
++import com.google.common.reflect.TypeToken;
++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;
++
++/**
++ * 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: org.apache.brooklyn.container.entity.docker.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/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java
index 0000000,0000000..f0a9708
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/docker/DockerContainerImpl.java
@@@ -1,0 -1,0 +1,88 @@@
++/*
++ * 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.entity.docker;
++
++import com.google.common.base.Strings;
++import com.google.common.collect.ImmutableSet;
++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 java.util.Iterator;
++import java.util.List;
++
++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/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPod.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPod.java
index 0000000,0000000..b5e9a9d
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPod.java
@@@ -1,0 -1,0 +1,101 @@@
++/*
++ * 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.entity.kubernetes;
++
++import com.google.common.collect.ImmutableMap;
++import com.google.common.reflect.TypeToken;
++import org.apache.brooklyn.api.entity.ImplementedBy;
++import org.apache.brooklyn.api.sensor.AttributeSensor;
++import org.apache.brooklyn.config.ConfigKey;
++import org.apache.brooklyn.container.entity.docker.DockerContainer;
++import 
org.apache.brooklyn.container.location.kubernetes.KubernetesLocationConfig;
++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.core.sensor.Sensors;
++import org.apache.brooklyn.util.math.MathPredicates;
++
++import java.util.List;
++import java.util.Map;
++
++@ImplementedBy(KubernetesPodImpl.class)
++public interface KubernetesPod extends DockerContainer {
++
++    ConfigKey<String> NAMESPACE = KubernetesLocationConfig.NAMESPACE;
++
++    ConfigKey<Boolean> PRIVILEGED = KubernetesLocationConfig.PRIVILEGED;
++
++    @SuppressWarnings("serial")
++    ConfigKey<List<String>> PERSISTENT_VOLUMES = ConfigKeys.builder(new 
TypeToken<List<String>>() {
++    })
++            .name("persistentVolumes")
++            .description("Persistent volumes used by the pod")
++            .build();
++
++    ConfigKey<String> DEPLOYMENT = ConfigKeys.builder(String.class)
++            .name("deployment")
++            .description("The name of the service the deployed pod will use")
++            .build();
++
++    ConfigKey<Integer> REPLICAS = ConfigKeys.builder(Integer.class)
++            .name("replicas")
++            .description("Number of replicas in the pod")
++            .constraint(MathPredicates.greaterThanOrEqual(1d))
++            .defaultValue(1)
++            .build();
++
++    @SuppressWarnings("serial")
++    ConfigKey<Map<String, String>> SECRETS = ConfigKeys.builder(new 
TypeToken<Map<String, String>>() {
++    })
++            .name("secrets")
++            .description("Secrets to be added to the pod")
++            .build();
++
++    @SuppressWarnings("serial")
++    ConfigKey<Map<String, String>> LIMITS = ConfigKeys.builder(new 
TypeToken<Map<String, String>>() {
++    })
++            .name("limits")
++            .description("Container resource limits for the pod")
++            .build();
++
++    MapConfigKey<Object> METADATA = new 
MapConfigKey.Builder<Object>(Object.class, "metadata")
++            .description("Metadata to set on the pod")
++            .defaultValue(ImmutableMap.<String, Object>of())
++            .typeInheritance(BasicConfigInheritance.DEEP_MERGE)
++            
.runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED_ELSE_DEEP_MERGE)
++            .build();
++
++    AttributeSensor<String> KUBERNETES_DEPLOYMENT = 
Sensors.builder(String.class, "kubernetes.deployment")
++            .description("Deployment resources run in")
++            .build();
++
++    AttributeSensor<String> KUBERNETES_NAMESPACE = 
Sensors.builder(String.class, "kubernetes.namespace")
++            .description("Namespace that resources run in")
++            .build();
++
++    AttributeSensor<String> KUBERNETES_SERVICE = 
Sensors.builder(String.class, "kubernetes.service")
++            .description("Service that exposes the deployment")
++            .build();
++
++    AttributeSensor<String> KUBERNETES_POD = Sensors.builder(String.class, 
"kubernetes.pod")
++            .description("Pod running the deployment")
++            .build();
++
++    String EMPTY = "Empty";
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPodImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPodImpl.java
index 0000000,0000000..0765896
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesPodImpl.java
@@@ -1,0 -1,0 +1,25 @@@
++/*
++ * 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.entity.kubernetes;
++
++import org.apache.brooklyn.container.entity.docker.DockerContainerImpl;
++
++public class KubernetesPodImpl extends DockerContainerImpl implements 
KubernetesPod {
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResource.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResource.java
index 0000000,0000000..5754897
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResource.java
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ * 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.entity.kubernetes;
++
++import org.apache.brooklyn.api.entity.ImplementedBy;
++import org.apache.brooklyn.api.sensor.AttributeSensor;
++import org.apache.brooklyn.config.ConfigKey;
++import org.apache.brooklyn.core.config.ConfigKeys;
++import org.apache.brooklyn.core.sensor.Sensors;
++import org.apache.brooklyn.entity.software.base.SoftwareProcess;
++import org.apache.brooklyn.util.core.ResourcePredicates;
++
++@ImplementedBy(KubernetesResourceImpl.class)
++public interface KubernetesResource extends SoftwareProcess {
++
++    ConfigKey<String> RESOURCE_FILE = ConfigKeys.builder(String.class)
++            .name("resource")
++            .description("Kubernetes resource YAML file URI")
++            .constraint(ResourcePredicates.urlExists())
++            .build();
++
++    AttributeSensor<String> RESOURCE_TYPE = Sensors.builder(String.class, 
"kubernetes.resource.type")
++            .description("Kubernetes resource type")
++            .build();
++
++    AttributeSensor<String> RESOURCE_NAME = Sensors.builder(String.class, 
"kubernetes.resource.name")
++            .description("Kubernetes resource name")
++            .build();
++
++    AttributeSensor<String> KUBERNETES_NAMESPACE = 
KubernetesPod.KUBERNETES_NAMESPACE;
++
++    String POD = "Pod";
++    String DEPLOYMENT = "Deployment";
++    String REPLICA_SET = "ReplicaSet";
++    String CONFIG_MAP = "ConfigMap";
++    String PERSISTENT_VOLUME = "PersistentVolume";
++    String SECRET = "Secret";
++    String SERVICE = "Service";
++    String REPLICATION_CONTROLLER = "ReplicationController";
++    String NAMESPACE = "Namespace";
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResourceImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResourceImpl.java
index 0000000,0000000..d87e22d
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/kubernetes/KubernetesResourceImpl.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * 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.entity.kubernetes;
++
++import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
++import org.apache.brooklyn.entity.software.base.EmptySoftwareProcessImpl;
++
++public class KubernetesResourceImpl extends EmptySoftwareProcessImpl 
implements KubernetesResource {
++
++    @Override
++    public void init() {
++        super.init();
++
++        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);
++    }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPod.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPod.java
index 0000000,0000000..739580c
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPod.java
@@@ -1,0 -1,0 +1,27 @@@
++/*
++ * 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.entity.openshift;
++
++import org.apache.brooklyn.api.entity.ImplementedBy;
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesPod;
++
++@ImplementedBy(OpenShiftPodImpl.class)
++public interface OpenShiftPod extends KubernetesPod {
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPodImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPodImpl.java
index 0000000,0000000..2d83421
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftPodImpl.java
@@@ -1,0 -1,0 +1,25 @@@
++/*
++ * 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.entity.openshift;
++
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesPodImpl;
++
++public class OpenShiftPodImpl extends KubernetesPodImpl implements 
OpenShiftPod {
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResource.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResource.java
index 0000000,0000000..6c39538
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResource.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * 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.entity.openshift;
++
++import org.apache.brooklyn.api.entity.ImplementedBy;
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesResource;
++
++@ImplementedBy(OpenShiftResourceImpl.class)
++public interface OpenShiftResource extends KubernetesResource {
++
++    String DEPLOYMENT_CONFIG = "DeploymentConfig";
++    String PROJECT = "Project";
++    String TEMPLATE = "Template";
++    String BUILD_CONFIG = "BuildConfig";
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResourceImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResourceImpl.java
index 0000000,0000000..fc1be96
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/entity/openshift/OpenShiftResourceImpl.java
@@@ -1,0 -1,0 +1,24 @@@
++/*
++ * 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.entity.openshift;
++
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesResourceImpl;
++
++public class OpenShiftResourceImpl extends KubernetesResourceImpl implements 
OpenShiftResource {
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java
index 0000000,0000000..124a191
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerJcloudsLocation.java
@@@ -1,0 -1,0 +1,196 @@@
++/*
++ * 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 com.google.common.base.Functions;
++import com.google.common.collect.ImmutableList;
++import com.google.common.collect.Maps;
++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 javax.annotation.Nullable;
++import java.util.Collection;
++import java.util.List;
++import java.util.Map;
++
++import static com.google.common.base.Preconditions.checkNotNull;
++
++/**
++ * For provisioning docker containers, using the jclouds-docker integration.
++ * <p>
++ * 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()}.
++ * <p>
++ * 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 {
++
++    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");
++    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(
++            "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"));
++
++    @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;
++    }
++
++    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;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java
index 0000000,0000000..e76b12f
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/docker/DockerLocationResolver.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ * 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 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;
++
++import java.util.Map;
++
++/**
++ * Locations starting with the given prefix (@code "docker") will use this 
resolver, to instantiate
++ * a {@link DockerJcloudsLocation}.
++ * <p>
++ * 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/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/ImageChooser.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/ImageChooser.java
index 0000000,0000000..5536b82
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/ImageChooser.java
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.brooklyn.container.location.kubernetes;
++
++import com.google.common.base.Optional;
++import com.google.common.collect.ImmutableList;
++import org.jclouds.compute.domain.OsFamily;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++import javax.annotation.Nullable;
++import java.util.List;
++
++import static com.google.common.base.Preconditions.checkNotNull;
++
++public class ImageChooser {
++
++    private static final Logger LOG = 
LoggerFactory.getLogger(ImageChooser.class);
++    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 final List<ImageMetadata> images;
++
++    public ImageChooser() {
++        this.images = DEFAULT_IMAGES;
++    }
++
++    public ImageChooser(List<? extends ImageMetadata> images) {
++        this.images = ImmutableList.copyOf(images);
++    }
++
++    public Optional<String> chooseImage(String osFamily, String 
osVersionRegex) {
++        return chooseImage((osFamily == null ? (OsFamily) null : 
OsFamily.fromValue(osFamily)), osVersionRegex);
++    }
++
++    public Optional<String> chooseImage(OsFamily osFamily, String 
osVersionRegex) {
++        for (ImageMetadata imageMetadata : images) {
++            if (imageMetadata.matches(osFamily, osVersionRegex)) {
++                String imageName = imageMetadata.getImageName();
++                LOG.debug("Choosing container image {}, for osFamily={} and 
osVersionRegex={}", new Object[]{imageName, osFamily, osVersionRegex});
++                return Optional.of(imageName);
++            }
++        }
++        return Optional.absent();
++    }
++
++    public static class ImageMetadata {
++        private final OsFamily osFamily;
++        private final String osVersion;
++        private final String imageName;
++
++        public ImageMetadata(OsFamily osFamily, String osVersion, String 
imageName) {
++            this.osFamily = checkNotNull(osFamily, "osFamily");
++            this.osVersion = checkNotNull(osVersion, "osVersion");
++            this.imageName = checkNotNull(imageName, "imageName");
++        }
++
++        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 getImageName() {
++            return imageName;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCerts.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCerts.java
index 0000000,0000000..5bb7e2d
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesCerts.java
@@@ -1,0 -1,0 +1,74 @@@
++/*
++ * 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.kubernetes;
++
++import com.google.common.base.Optional;
++import org.apache.brooklyn.config.ConfigKey;
++import org.apache.brooklyn.util.core.ResourceUtils;
++import org.apache.brooklyn.util.core.config.ConfigBag;
++import org.apache.brooklyn.util.text.Strings;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++import static 
org.apache.brooklyn.container.location.kubernetes.KubernetesLocationConfig.*;
++
++class KubernetesCerts {
++
++    private static final Logger LOG = 
LoggerFactory.getLogger(KubernetesCerts.class);
++
++    public final Optional<String> caCertData;
++    public final Optional<String> clientCertData;
++    public final Optional<String> clientKeyData;
++    public final Optional<String> clientKeyAlgo;
++    public final Optional<String> clientKeyPassphrase;
++
++    public KubernetesCerts(ConfigBag config) {
++        caCertData = getData(CA_CERT_DATA, CA_CERT_FILE, config);
++        clientCertData = getData(CLIENT_CERT_DATA, CLIENT_CERT_FILE, config);
++        clientKeyData = getData(CLIENT_KEY_DATA, CLIENT_KEY_FILE, config);
++        clientKeyAlgo = getNonBlankOptional(CLIENT_KEY_ALGO, config);
++        clientKeyPassphrase = getNonBlankOptional(CLIENT_KEY_PASSPHRASE, 
config);
++    }
++
++    protected Optional<String> getData(ConfigKey<String> dataKey, 
ConfigKey<String> fileKey, ConfigBag config) {
++        String data = Strings.isNonBlank(config.get(dataKey)) ? 
config.get(dataKey).trim() : null;
++        String file = config.get(fileKey);
++        String fileData = Strings.isNonBlank(file) ? 
getFileContents(file).trim() : null;
++
++        if (Strings.isNonBlank(data) && Strings.isNonBlank(fileData)) {
++            if (data.equals(fileData)) {
++                LOG.warn("Duplicate (matching) configuration for " + 
dataKey.getName() + " and " + fileKey.getName() + " (continuing)");
++            } else {
++                throw new IllegalStateException("Duplicate conflicting 
configuration for " + dataKey.getName() + " and " + fileKey.getName());
++            }
++        }
++
++        String result = Strings.isNonBlank(data) ? data : 
(Strings.isNonBlank(fileData) ? fileData : null);
++        return Optional.fromNullable(result);
++    }
++
++    protected Optional<String> getNonBlankOptional(ConfigKey<? extends 
String> key, ConfigBag config) {
++        String result = config.get(key);
++        return Optional.fromNullable(Strings.isNonBlank(result) ? result : 
null);
++    }
++
++    protected String getFileContents(String file) {
++        return ResourceUtils.create(this).getResourceAsString(file);
++    }
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistry.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistry.java
index 0000000,0000000..9cbf0b1
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistry.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ * 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.kubernetes;
++
++import io.fabric8.kubernetes.client.KubernetesClient;
++import org.apache.brooklyn.util.core.config.ConfigBag;
++
++public interface KubernetesClientRegistry {
++
++    KubernetesClient getKubernetesClient(ConfigBag conf);
++
++}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistryImpl.java
----------------------------------------------------------------------
diff --cc 
locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistryImpl.java
index 0000000,0000000..27ab589
new file mode 100644
--- /dev/null
+++ 
b/locations/container/src/main/java/org/apache/brooklyn/container/location/kubernetes/KubernetesClientRegistryImpl.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * 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.kubernetes;
++
++import com.google.common.base.Throwables;
++import com.google.common.io.BaseEncoding;
++import io.fabric8.kubernetes.client.ConfigBuilder;
++import io.fabric8.kubernetes.client.DefaultKubernetesClient;
++import io.fabric8.kubernetes.client.KubernetesClient;
++import org.apache.brooklyn.util.core.config.ConfigBag;
++import org.apache.brooklyn.util.text.Strings;
++import org.apache.brooklyn.util.time.Duration;
++
++import java.net.MalformedURLException;
++import java.net.URL;
++
++import static com.google.common.base.Preconditions.checkNotNull;
++
++public class KubernetesClientRegistryImpl implements KubernetesClientRegistry 
{
++
++    public static final KubernetesClientRegistryImpl INSTANCE = new 
KubernetesClientRegistryImpl();
++
++    @Override
++    public KubernetesClient getKubernetesClient(ConfigBag conf) {
++        String masterUrl = 
checkNotNull(conf.get(KubernetesLocationConfig.MASTER_URL), "master url must 
not be null");
++
++        URL url;
++        try {
++            url = new URL(masterUrl);
++        } catch (MalformedURLException e) {
++            throw Throwables.propagate(e);
++        }
++
++        ConfigBuilder configBuilder = new ConfigBuilder()
++                .withMasterUrl(masterUrl)
++                .withTrustCerts(false);
++
++        if (url.getProtocol().equals("https")) {
++            KubernetesCerts certs = new KubernetesCerts(conf);
++            if (certs.caCertData.isPresent()) 
configBuilder.withCaCertData(toBase64Encoding(certs.caCertData.get()));
++            if (certs.clientCertData.isPresent())
++                
configBuilder.withClientCertData(toBase64Encoding(certs.clientCertData.get()));
++            if (certs.clientKeyData.isPresent())
++                
configBuilder.withClientKeyData(toBase64Encoding(certs.clientKeyData.get()));
++            if (certs.clientKeyAlgo.isPresent()) 
configBuilder.withClientKeyAlgo(certs.clientKeyAlgo.get());
++            if (certs.clientKeyPassphrase.isPresent())
++                
configBuilder.withClientKeyPassphrase(certs.clientKeyPassphrase.get());
++            // TODO Should we also set configBuilder.withTrustCerts(true) 
here?
++        }
++
++        String username = conf.get(KubernetesLocationConfig.ACCESS_IDENTITY);
++        if (Strings.isNonBlank(username)) 
configBuilder.withUsername(username);
++
++        String password = 
conf.get(KubernetesLocationConfig.ACCESS_CREDENTIAL);
++        if (Strings.isNonBlank(password)) 
configBuilder.withPassword(password);
++
++        String token = conf.get(KubernetesLocationConfig.OAUTH_TOKEN);
++        if (Strings.isNonBlank(token)) configBuilder.withOauthToken(token);
++
++        Duration clientTimeout = 
conf.get(KubernetesLocationConfig.CLIENT_TIMEOUT);
++        if (clientTimeout.isPositive()) {
++            configBuilder.withConnectionTimeout((int) 
clientTimeout.toMilliseconds());
++            configBuilder.withRequestTimeout((int) 
clientTimeout.toMilliseconds());
++        } else {
++            throw new IllegalArgumentException("Kubernetes client timeout 
should be a positive duration: " + clientTimeout.toString());
++        }
++        Duration actionTimeout = 
conf.get(KubernetesLocationConfig.ACTION_TIMEOUT);
++        if (actionTimeout.isPositive()) {
++            configBuilder.withRollingTimeout(actionTimeout.toMilliseconds());
++            configBuilder.withScaleTimeout(actionTimeout.toMilliseconds());
++        } else {
++            throw new IllegalArgumentException("Kubernetes action timeout 
should be a positive duration: " + actionTimeout.toString());
++        }
++
++        return new DefaultKubernetesClient(configBuilder.build());
++    }
++
++    private String toBase64Encoding(String val) {
++        return BaseEncoding.base64().encode(val.getBytes());
++    }
++}

Reply via email to