You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by dr...@apache.org on 2017/06/29 15:36:24 UTC
[42/50] [abbrv] brooklyn-server git commit: Merge tag
'apache-brooklyn' into feature/container-service
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java
----------------------------------------------------------------------
diff --cc locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java
index 0000000,0000000..f8b2645
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/kubernetes/KubernetesLocationYamlLiveTest.java
@@@ -1,0 -1,0 +1,521 @@@
++/*
++ * 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.Joiner;
++import com.google.common.collect.ImmutableList;
++import com.google.common.collect.Iterables;
++import com.google.common.net.HostAndPort;
++import io.fabric8.kubernetes.api.model.Pod;
++import io.fabric8.kubernetes.client.KubernetesClient;
++import org.apache.brooklyn.api.entity.Entity;
++import org.apache.brooklyn.api.location.MachineLocation;
++import org.apache.brooklyn.api.location.MachineProvisioningLocation;
++import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
++import org.apache.brooklyn.container.entity.docker.DockerContainer;
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesPod;
++import org.apache.brooklyn.container.entity.kubernetes.KubernetesResource;
++import org.apache.brooklyn.core.entity.Attributes;
++import org.apache.brooklyn.core.entity.Entities;
++import org.apache.brooklyn.core.entity.EntityPredicates;
++import org.apache.brooklyn.core.location.Machines;
++import org.apache.brooklyn.core.network.OnPublicNetworkEnricher;
++import org.apache.brooklyn.core.sensor.Sensors;
++import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
++import org.apache.brooklyn.entity.software.base.SoftwareProcess;
++import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
++import org.apache.brooklyn.entity.stock.BasicStartable;
++import org.apache.brooklyn.location.ssh.SshMachineLocation;
++import org.apache.brooklyn.util.core.config.ConfigBag;
++import org.apache.brooklyn.util.net.Networking;
++import org.apache.brooklyn.util.text.Identifiers;
++import org.apache.commons.lang3.StringUtils;
++import org.testng.annotations.BeforeMethod;
++import org.testng.annotations.Test;
++
++import java.util.List;
++import java.util.Map;
++
++import static com.google.common.base.Predicates.*;
++import static org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest.*;
++import static org.apache.brooklyn.core.entity.EntityAsserts.*;
++import static org.apache.brooklyn.test.Asserts.succeedsEventually;
++import static org.apache.brooklyn.util.http.HttpAsserts.assertHttpStatusCodeEventuallyEquals;
++import static org.testng.Assert.assertEquals;
++import static org.testng.Assert.assertTrue;
++
++/**
++ * Live tests for deploying simple blueprints. Particularly useful during dev, but not so useful
++ * after that (because assumes the existence of a kubernetes endpoint). It needs configured with
++ * something like:
++ * <p>
++ * {@code -Dtest.brooklyn-container-service.kubernetes.endpoint=http://10.104.2.206:8080}
++ */
++public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
++
++ protected KubernetesLocation loc;
++ protected List<MachineLocation> machines;
++ protected String locationYaml;
++
++ @BeforeMethod(alwaysRun = true)
++ @Override
++ public void setUp() throws Exception {
++ super.setUp();
++
++ locationYaml = Joiner.on("\n").join(
++ "location:",
++ " kubernetes:",
++ " " + KubernetesLocationConfig.MASTER_URL.getName() + ": \"" + KUBERNETES_ENDPOINT + "\"",
++ " " + (StringUtils.isBlank(IDENTITY) ? "" : "identity: " + IDENTITY),
++ " " + (StringUtils.isBlank(CREDENTIAL) ? "" : "credential: " + CREDENTIAL));
++ }
++
++ @Test(groups = {"Live"})
++ public void testLoginPasswordOverride() throws Exception {
++ String customPassword = "myDifferentPassword";
++
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + EmptySoftwareProcess.class.getName(),
++ " brooklyn.config:",
++ " provisioning.properties:",
++ " " + KubernetesLocationConfig.LOGIN_USER_PASSWORD.getName() + ": " + customPassword);
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ EmptySoftwareProcess entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, EmptySoftwareProcess.class));
++
++ SshMachineLocation machine = Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get();
++ assertEquals(machine.config().get(SshMachineLocation.PASSWORD), customPassword);
++ assertTrue(machine.isSshable());
++ }
++
++ @Test(groups = {"Live"})
++ public void testNetcatServer() throws Exception {
++ // Runs as root user (hence not `sudo yum install ...`)
++ // breaks if shell.env uses attributeWhenReady, so not doing that - see testNetcatServerWithDslInShellEnv()
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + VanillaSoftwareProcess.class.getName(),
++ " brooklyn.parameters:",
++ " - name: netcat.port",
++ " type: port",
++ " default: 8081",
++ " brooklyn.config:",
++ " install.command: |",
++ " yum install -y nc",
++ " launch.command: |",
++ " echo $MESSAGE | nc -l $NETCAT_PORT &",
++ " echo $! > $PID_FILE",
++ " shell.env:",
++ " MESSAGE: mymessage",
++ " NETCAT_PORT: $brooklyn:attributeWhenReady(\"netcat.port\")",
++ " brooklyn.enrichers:",
++ " - type: " + OnPublicNetworkEnricher.class.getName(),
++ " brooklyn.config:",
++ " " + OnPublicNetworkEnricher.SENSORS.getName() + ":",
++ " - netcat.port");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ VanillaSoftwareProcess entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, VanillaSoftwareProcess.class));
++
++ String publicMapped = assertAttributeEventuallyNonNull(entity, Sensors.newStringSensor("netcat.endpoint.mapped.public"));
++ HostAndPort publicPort = HostAndPort.fromString(publicMapped);
++
++ assertTrue(Networking.isReachable(publicPort), "publicPort=" + publicPort);
++ }
++
++ @Test(groups = {"Live"})
++ public void testInterContainerNetworking() throws Exception {
++ String message = "mymessage";
++ int netcatPort = 8081;
++
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + VanillaSoftwareProcess.class.getName(),
++ " name: server1",
++ " brooklyn.parameters:",
++ " - name: netcat.port",
++ " type: port",
++ " default: " + netcatPort,
++ " brooklyn.config:",
++ " install.command: |",
++ " yum install -y nc",
++ " launch.command: |",
++ " echo " + message + " | nc -l " + netcatPort + " > netcat.out &",
++ " echo $! > $PID_FILE",
++ " - type: " + VanillaSoftwareProcess.class.getName(),
++ " name: server2",
++ " brooklyn.config:",
++ " install.command: |",
++ " yum install -y nc",
++ " launch.command: true",
++ " checkRunning.command: true");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ Entities.dumpInfo(app);
++
++ Entity server1 = Iterables.find(Entities.descendantsAndSelf(app), EntityPredicates.displayNameEqualTo("server1"));
++ Entity server2 = Iterables.find(Entities.descendantsAndSelf(app), EntityPredicates.displayNameEqualTo("server2"));
++
++ SshMachineLocation machine1 = Machines.findUniqueMachineLocation(server1.getLocations(), SshMachineLocation.class).get();
++ SshMachineLocation machine2 = Machines.findUniqueMachineLocation(server2.getLocations(), SshMachineLocation.class).get();
++
++ String addr1 = server1.sensors().get(Attributes.SUBNET_ADDRESS);
++ String addr2 = server2.sensors().get(Attributes.SUBNET_ADDRESS);
++
++ // Ping between containers
++ int result1 = machine1.execCommands("ping-server2", ImmutableList.of("ping -c 4 " + addr2));
++ int result2 = machine2.execCommands("ping-server1", ImmutableList.of("ping -c 4 " + addr1));
++
++ // Reach netcat port from other container
++ int result3 = machine2.execCommands("nc-to-server1", ImmutableList.of(
++ "echo \"fromServer2\" | nc " + addr1 + " " + netcatPort + " > netcat.out",
++ "cat netcat.out",
++ "grep " + message + " netcat.out"));
++
++ String errMsg = "result1=" + result1 + "; result2=" + result2 + "; result3=" + result3;
++ assertEquals(result1, 0, errMsg);
++ assertEquals(result2, 0, errMsg);
++ assertEquals(result3, 0, errMsg);
++ }
++
++ @Test(groups = {"Live"})
++ public void testTomcatPod() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesPod.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts: [ \"8080\" ]");
++
++ runTomcat(yaml, KubernetesPod.class);
++ }
++
++ @Test(groups = {"Live"})
++ public void testTomcatPodExtras() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesPod.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts: [ \"8080\" ]",
++ " metadata:",
++ " extra: test");
++
++ KubernetesPod entity = runTomcat(yaml, KubernetesPod.class);
++
++ String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE);
++ String podName = entity.sensors().get(KubernetesPod.KUBERNETES_POD);
++ KubernetesClient client = getClient(entity);
++ Pod pod = client.pods().inNamespace(namespace).withName(podName).get();
++ Map<String, String> labels = pod.getMetadata().getLabels();
++ assertTrue(labels.containsKey("extra"));
++ assertEquals(labels.get("extra"), "test");
++ }
++
++ @Test(groups = {"Live"})
++ public void testTomcatContainer() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + DockerContainer.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts: [ \"8080\" ]");
++
++ runTomcat(yaml, DockerContainer.class);
++ }
++
++ /**
++ * Assumes that the container entity uses port 8080.
++ */
++ protected <T extends Entity> T runTomcat(String yaml, Class<T> type) throws Exception {
++ Entity app = createStartWaitAndLogApplication(yaml);
++ T entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type));
++
++ Entities.dumpInfo(app);
++ String publicMapped = assertAttributeEventuallyNonNull(entity, Sensors.newStringSensor("docker.port.8080.mapped.public"));
++ HostAndPort publicPort = HostAndPort.fromString(publicMapped);
++
++ assertReachableEventually(publicPort);
++ assertHttpStatusCodeEventuallyEquals("http://" + publicPort.getHostText() + ":" + publicPort.getPort(), 200);
++
++ return entity;
++ }
++
++ @Test(groups = {"Live"})
++ public void testWordpressInContainersWithStartableParent() throws Exception {
++ // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes
++ String randomId = Identifiers.makeRandomLowercaseId(4);
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + BasicStartable.class.getName(),
++ " brooklyn.children:",
++ " - type: " + DockerContainer.class.getName(),
++ " id: wordpress-mysql",
++ " name: mysql",
++ " brooklyn.config:",
++ " docker.container.imageName: mysql:5.6",
++ " docker.container.inboundPorts:",
++ " - \"3306\"",
++ " docker.container.environment:",
++ " MYSQL_ROOT_PASSWORD: \"password\"",
++ " provisioning.properties:",
++ " deployment: wordpress-mysql-" + randomId,
++ " - type: " + DockerContainer.class.getName(),
++ " id: wordpress",
++ " name: wordpress",
++ " brooklyn.config:",
++ " docker.container.imageName: wordpress:4-apache",
++ " docker.container.inboundPorts:",
++ " - \"80\"",
++ " docker.container.environment:",
++ " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"",
++ " WORDPRESS_DB_PASSWORD: \"password\"",
++ " provisioning.properties:",
++ " deployment: wordpress-" + randomId);
++
++ runWordpress(yaml, randomId);
++ }
++
++ @Test(groups = {"Live"})
++ public void testWordpressInPodsWithStartableParent() throws Exception {
++ // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes
++ String randomId = Identifiers.makeRandomLowercaseId(4);
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + BasicStartable.class.getName(),
++ " brooklyn.children:",
++ " - type: " + KubernetesPod.class.getName(),
++ " id: wordpress-mysql",
++ " name: mysql",
++ " brooklyn.config:",
++ " docker.container.imageName: mysql:5.6",
++ " docker.container.inboundPorts:",
++ " - \"3306\"",
++ " docker.container.environment:",
++ " MYSQL_ROOT_PASSWORD: \"password\"",
++ " deployment: wordpress-mysql-" + randomId,
++ " - type: " + KubernetesPod.class.getName(),
++ " id: wordpress",
++ " name: wordpress",
++ " brooklyn.config:",
++ " docker.container.imageName: wordpress:4-apache",
++ " docker.container.inboundPorts:",
++ " - \"80\"",
++ " docker.container.environment:",
++ " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"",
++ " WORDPRESS_DB_PASSWORD: \"password\"",
++ " deployment: wordpress-" + randomId);
++
++ runWordpress(yaml, randomId);
++ }
++
++ @Test(groups = {"Live"})
++ public void testWordpressInPods() throws Exception {
++ // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes
++ String randomId = Identifiers.makeRandomLowercaseId(4);
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesPod.class.getName(),
++ " id: wordpress-mysql",
++ " name: mysql",
++ " brooklyn.config:",
++ " docker.container.imageName: mysql:5.6",
++ " docker.container.inboundPorts:",
++ " - \"3306\"",
++ " docker.container.environment:",
++ " MYSQL_ROOT_PASSWORD: \"password\"",
++ " deployment: wordpress-mysql-" + randomId,
++ " - type: " + KubernetesPod.class.getName(),
++ " id: wordpress",
++ " name: wordpress",
++ " brooklyn.config:",
++ " docker.container.imageName: wordpress:4-apache",
++ " docker.container.inboundPorts:",
++ " - \"80\"",
++ " docker.container.environment:",
++ " WORDPRESS_DB_HOST: \"wordpress-mysql-" + randomId + "\"",
++ " WORDPRESS_DB_PASSWORD: \"password\"",
++ " deployment: wordpress-" + randomId);
++
++ runWordpress(yaml, randomId);
++ }
++
++ /**
++ * Assumes that the {@link DockerContainer} entities have display names of "mysql" and "wordpress",
++ * and that they use ports 3306 and 80 respectively.
++ */
++ protected void runWordpress(String yaml, String randomId) throws Exception {
++ Entity app = createStartWaitAndLogApplication(yaml);
++ Entities.dumpInfo(app);
++
++ Iterable<DockerContainer> containers = Entities.descendantsAndSelf(app, DockerContainer.class);
++ DockerContainer mysql = Iterables.find(containers, EntityPredicates.displayNameEqualTo("mysql"));
++ DockerContainer wordpress = Iterables.find(containers, EntityPredicates.displayNameEqualTo("wordpress"));
++
++ String mysqlPublicPort = assertAttributeEventuallyNonNull(mysql, Sensors.newStringSensor("docker.port.3306.mapped.public"));
++ assertReachableEventually(HostAndPort.fromString(mysqlPublicPort));
++ assertAttributeEquals(mysql, KubernetesPod.KUBERNETES_NAMESPACE, "brooklyn");
++ assertAttributeEquals(mysql, KubernetesPod.KUBERNETES_SERVICE, "wordpress-mysql-" + randomId);
++
++ String wordpressPublicPort = assertAttributeEventuallyNonNull(wordpress, Sensors.newStringSensor("docker.port.80.mapped.public"));
++ assertReachableEventually(HostAndPort.fromString(wordpressPublicPort));
++ assertAttributeEquals(wordpress, KubernetesPod.KUBERNETES_NAMESPACE, "brooklyn");
++ assertAttributeEquals(wordpress, KubernetesPod.KUBERNETES_SERVICE, "wordpress-" + randomId);
++
++ // TODO more assertions (e.g. wordpress can successfully reach the database)
++ }
++
++ @Test(groups = {"Live"})
++ public void testPod() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesPod.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts:",
++ " - \"8080\"",
++ " shell.env:",
++ " CLUSTER_ID: \"id\"",
++ " CLUSTER_TOKEN: \"token\"");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkPod(app, KubernetesPod.class);
++ }
++
++ @Test(groups = {"Live"}, enabled = false)
++ public void testPodCatalogEntry() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: kubernetes-pod-entity",
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts:",
++ " - \"8080\"",
++ " shell.env:",
++ " CLUSTER_ID: \"id\"",
++ " CLUSTER_TOKEN: \"token\"");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkPod(app, KubernetesPod.class);
++ }
++
++ protected <T extends Entity> void checkPod(Entity app, Class<T> type) {
++ T container = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type));
++
++ Entities.dumpInfo(app);
++
++ String publicMapped = assertAttributeEventuallyNonNull(container, Sensors.newStringSensor("docker.port.8080.mapped.public"));
++ HostAndPort publicPort = HostAndPort.fromString(publicMapped);
++
++ assertReachableEventually(publicPort);
++ assertHttpStatusCodeEventuallyEquals("http://" + publicPort.getHostText() + ":" + publicPort.getPort(), 200);
++ }
++
++ @Test(groups = {"Live"})
++ public void testNginxReplicationController() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesResource.class.getName(),
++ " id: nginx-replication-controller",
++ " name: \"nginx-replication-controller\"",
++ " brooklyn.config:",
++ " resource: classpath://nginx-replication-controller.yaml");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkNginxResource(app, KubernetesResource.class);
++ }
++
++ protected <T extends Entity> void checkNginxResource(Entity app, Class<T> type) {
++ T entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, type));
++
++ Entities.dumpInfo(app);
++
++ assertEntityHealthy(entity);
++ assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_NAME, "nginx-replication-controller");
++ assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_TYPE, "ReplicationController");
++ assertAttributeEqualsEventually(entity, KubernetesResource.KUBERNETES_NAMESPACE, "default");
++ assertAttributeEventually(entity, SoftwareProcess.ADDRESS, and(notNull(), not(equalTo("0.0.0.0"))));
++ assertAttributeEventually(entity, SoftwareProcess.SUBNET_ADDRESS, and(notNull(), not(equalTo("0.0.0.0"))));
++ }
++
++ @Test(groups = {"Live"})
++ public void testNginxService() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + KubernetesResource.class.getName(),
++ " id: nginx-replication-controller",
++ " name: \"nginx-replication-controller\"",
++ " brooklyn.config:",
++ " resource: classpath://nginx-replication-controller.yaml",
++ " - type: " + KubernetesResource.class.getName(),
++ " id: nginx-service",
++ " name: \"nginx-service\"",
++ " brooklyn.config:",
++ " resource: classpath://nginx-service.yaml");
++ Entity app = createStartWaitAndLogApplication(yaml);
++
++ Iterable<KubernetesResource> resources = Entities.descendantsAndSelf(app, KubernetesResource.class);
++ KubernetesResource nginxReplicationController = Iterables.find(resources, EntityPredicates.displayNameEqualTo("nginx-replication-controller"));
++ KubernetesResource nginxService = Iterables.find(resources, EntityPredicates.displayNameEqualTo("nginx-service"));
++
++ assertEntityHealthy(nginxReplicationController);
++ assertEntityHealthy(nginxService);
++
++ Entities.dumpInfo(app);
++
++ Integer httpPort = assertAttributeEventuallyNonNull(nginxService, Sensors.newIntegerSensor("kubernetes.http.port"));
++ assertEquals(httpPort, Integer.valueOf(80));
++ String httpPublicPort = assertAttributeEventuallyNonNull(nginxService, Sensors.newStringSensor("kubernetes.http.endpoint.mapped.public"));
++ assertReachableEventually(HostAndPort.fromString(httpPublicPort));
++ }
++
++ protected void assertReachableEventually(final HostAndPort hostAndPort) {
++ succeedsEventually(new Runnable() {
++ public void run() {
++ assertTrue(Networking.isReachable(hostAndPort), "publicPort=" + hostAndPort);
++ }
++ });
++ }
++
++ public KubernetesClient getClient(Entity entity) {
++ MachineProvisioningLocation location = entity.sensors().get(SoftwareProcess.PROVISIONING_LOCATION);
++ if (location instanceof KubernetesLocation) {
++ KubernetesLocation kubernetes = (KubernetesLocation) location;
++ ConfigBag config = kubernetes.config().getBag();
++ KubernetesClientRegistry registry = kubernetes.config().get(KubernetesLocationConfig.KUBERNETES_CLIENT_REGISTRY);
++ KubernetesClient client = registry.getKubernetesClient(config);
++ return client;
++ }
++ throw new IllegalStateException("Cannot find KubernetesLocation on entity: " + Iterables.toString(entity.getLocations()));
++ }
++}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationLiveTest.java
----------------------------------------------------------------------
diff --cc locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationLiveTest.java
index 0000000,0000000..0606f68
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationLiveTest.java
@@@ -1,0 -1,0 +1,65 @@@
++/*
++ * 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.openshift;
++
++import org.apache.brooklyn.container.location.kubernetes.KubernetesLocationLiveTest;
++import org.apache.brooklyn.util.collections.MutableMap;
++import org.apache.brooklyn.util.os.Os;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++import java.util.Map;
++
++/**
++ * Tests deploying containers via the {@code openshift} location, to an OpenShift endpoint.
++ * By extending {@link KubernetesLocationLiveTest}, we get all the k8s tests.
++ * <p>
++ * It needs configured with something like:
++ * <p>
++ * <pre>{@code
++ * -Dtest.brooklyn-container-service.openshift.endpoint=https://192.168.99.100:8443/
++ * -Dtest.brooklyn-container-service.openshift.certsBaseDir=~/repos/grkvlt/40bdf09b09d5896e19a9d287f41d39bb
++ * }</pre>
++ */
++public class OpenShiftLocationLiveTest extends KubernetesLocationLiveTest {
++
++ public static final String OPENSHIFT_ENDPOINT = System.getProperty("test.brooklyn-container-service.openshift.endpoint", "");
++ public static final String CERTS_BASE_DIR = System.getProperty("test.brooklyn-container-service.openshift.certsBaseDir", Os.mergePaths(System.getProperty("user.home"), "openshift-certs"));
++ public static final String CA_CERT_FILE = System.getProperty("test.brooklyn-container-service.openshift.caCert", Os.mergePaths(CERTS_BASE_DIR, "ca.crt"));
++ public static final String CLIENT_CERT_FILE = System.getProperty("test.brooklyn-container-service.openshift.clientCert", Os.mergePaths(CERTS_BASE_DIR, "admin.crt"));
++ public static final String CLIENT_KEY_FILE = System.getProperty("test.brooklyn-container-service.openshift.clientKey", Os.mergePaths(CERTS_BASE_DIR, "admin.key"));
++ public static final String NAMESPACE = System.getProperty("test.brooklyn-container-service.openshift.namespace", "");
++
++ @SuppressWarnings("unused")
++ private static final Logger LOG = LoggerFactory.getLogger(OpenShiftLocationLiveTest.class);
++
++ @Override
++ protected OpenShiftLocation newKubernetesLocation(Map<String, ?> flags) throws Exception {
++ Map<String, ?> allFlags = MutableMap.<String, Object>builder()
++ .put("endpoint", OPENSHIFT_ENDPOINT)
++ .put("caCert", CA_CERT_FILE)
++ .put("clientCert", CLIENT_CERT_FILE)
++ .put("clientKey", CLIENT_KEY_FILE)
++ .put("namespace", NAMESPACE)
++ .put("privileged", true)
++ .putAll(flags)
++ .build();
++ return (OpenShiftLocation) mgmt.getLocationRegistry().getLocationManaged("openshift", allFlags);
++ }
++}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationResolverTest.java
----------------------------------------------------------------------
diff --cc locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationResolverTest.java
index 0000000,0000000..88f5d86
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationResolverTest.java
@@@ -1,0 -1,0 +1,103 @@@
++/*
++ * 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.openshift;
++
++import org.apache.brooklyn.api.location.LocationSpec;
++import org.apache.brooklyn.core.internal.BrooklynProperties;
++import org.apache.brooklyn.core.test.BrooklynMgmtUnitTestSupport;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++import org.testng.annotations.BeforeMethod;
++import org.testng.annotations.Test;
++
++import java.util.Map;
++
++import static org.testng.Assert.assertEquals;
++import static org.testng.Assert.assertTrue;
++
++public class OpenShiftLocationResolverTest extends BrooklynMgmtUnitTestSupport {
++
++ private static final Logger LOG = LoggerFactory.getLogger(OpenShiftLocationResolverTest.class);
++
++ private BrooklynProperties brooklynProperties;
++
++ @BeforeMethod(alwaysRun = true)
++ @Override
++ public void setUp() throws Exception {
++ super.setUp();
++ brooklynProperties = mgmt.getBrooklynProperties();
++
++ brooklynProperties.put("brooklyn.location.openshift.identity", "openshift-id");
++ brooklynProperties.put("brooklyn.location.openshift.credential", "openshift-cred");
++ }
++
++ @Test
++ public void testGivesCorrectLocationType() {
++ LocationSpec<?> spec = getLocationSpec("openshift");
++ assertEquals(spec.getType(), OpenShiftLocation.class);
++
++ OpenShiftLocation loc = resolve("openshift");
++ assertTrue(loc instanceof OpenShiftLocation, "loc=" + loc);
++ }
++
++ @Test
++ public void testParametersInSpecString() {
++ OpenShiftLocation loc = resolve("openshift(endpoint=myMasterUrl)");
++ assertEquals(loc.getConfig(OpenShiftLocation.MASTER_URL), "myMasterUrl");
++ }
++
++ @Test
++ public void testTakesDotSeparateProperty() {
++ brooklynProperties.put("brooklyn.location.openshift.endpoint", "myMasterUrl");
++ OpenShiftLocation loc = resolve("openshift");
++ assertEquals(loc.getConfig(OpenShiftLocation.MASTER_URL), "myMasterUrl");
++ }
++
++ @Test
++ public void testPropertiesPrecedence() {
++ // prefer those in "spec" over everything else
++ brooklynProperties.put("brooklyn.location.named.myopenshift", "openshift:(loginUser=\"loginUser-inSpec\")");
++
++ brooklynProperties.put("brooklyn.location.named.myopenshift.loginUser", "loginUser-inNamed");
++ brooklynProperties.put("brooklyn.location.openshift.loginUser", "loginUser-inDocker");
++
++ // prefer those in "named" over everything else
++ brooklynProperties.put("brooklyn.location.named.myopenshift.privateKeyFile", "privateKeyFile-inNamed");
++ brooklynProperties.put("brooklyn.location.openshift.privateKeyFile", "privateKeyFile-inDocker");
++
++ // prefer those in openshift-specific
++ brooklynProperties.put("brooklyn.location.openshift.publicKeyFile", "publicKeyFile-inDocker");
++
++ Map<String, Object> conf = resolve("named:myopenshift").config().getBag().getAllConfig();
++
++ assertEquals(conf.get("loginUser"), "loginUser-inSpec");
++ assertEquals(conf.get("privateKeyFile"), "privateKeyFile-inNamed");
++ assertEquals(conf.get("publicKeyFile"), "publicKeyFile-inDocker");
++ }
++
++ private LocationSpec<?> getLocationSpec(String spec) {
++ LOG.debug("Obtaining location spec '{}'", spec);
++ return mgmt.getLocationRegistry().getLocationSpec(spec).get();
++ }
++
++ private OpenShiftLocation resolve(String spec) {
++ LOG.debug("Resolving location spec '{}'", spec);
++ return (OpenShiftLocation) mgmt.getLocationRegistry().getLocationManaged(spec);
++ }
++}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationYamlLiveTest.java
----------------------------------------------------------------------
diff --cc locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationYamlLiveTest.java
index 0000000,0000000..fae87de
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/java/org/apache/brooklyn/container/location/openshift/OpenShiftLocationYamlLiveTest.java
@@@ -1,0 -1,0 +1,141 @@@
++/*
++ * 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.openshift;
++
++import com.google.common.base.Joiner;
++import org.apache.brooklyn.api.entity.Entity;
++import org.apache.brooklyn.container.entity.openshift.OpenShiftPod;
++import org.apache.brooklyn.container.entity.openshift.OpenShiftResource;
++import org.apache.brooklyn.container.location.kubernetes.KubernetesLocationYamlLiveTest;
++import org.testng.annotations.BeforeMethod;
++import org.testng.annotations.Test;
++
++import static org.apache.brooklyn.container.location.openshift.OpenShiftLocationLiveTest.*;
++
++/**
++ * Tests YAML apps via the {@code openshift} location, to an OpenShift endpoint.
++ * By extending {@link KubernetesLocationYamlLiveTest}, we get all the k8s tests.
++ * <p>
++ * It needs configured with something like:
++ * <p>
++ * <pre>{@code
++ * -Dtest.brooklyn-container-service.openshift.endpoint=https://master.example.com:8443/
++ * -Dtest.brooklyn-container-service.openshift.certsBaseDir=/Users/aled/repos/grkvlt/40bdf09b09d5896e19a9d287f41d39bb
++ * -Dtest.brooklyn-container-service.openshift.namespace=test
++ * }</pre>
++ */
++public class OpenShiftLocationYamlLiveTest extends KubernetesLocationYamlLiveTest {
++
++ @BeforeMethod(alwaysRun = true)
++ @Override
++ public void setUp() throws Exception {
++ super.setUp();
++
++ locationYaml = Joiner.on("\n").join(
++ "location:",
++ " openshift:",
++ " " + OpenShiftLocation.CLOUD_ENDPOINT.getName() + ": \"" + OPENSHIFT_ENDPOINT + "\"",
++ " " + OpenShiftLocation.CA_CERT_FILE.getName() + ": \"" + CA_CERT_FILE + "\"",
++ " " + OpenShiftLocation.CLIENT_CERT_FILE.getName() + ": \"" + CLIENT_CERT_FILE + "\"",
++ " " + OpenShiftLocation.CLIENT_KEY_FILE.getName() + ": \"" + CLIENT_KEY_FILE + "\"",
++ " " + OpenShiftLocation.NAMESPACE.getName() + ": \"" + NAMESPACE + "\"",
++ " " + OpenShiftLocation.PRIVILEGED.getName() + ": true",
++ " " + OpenShiftLocation.LOGIN_USER_PASSWORD.getName() + ": p4ssw0rd");
++ }
++
++ @Test(groups = {"Live"})
++ public void testTomcatOpenShiftPod() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + OpenShiftPod.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts: [ \"8080\" ]");
++
++ runTomcat(yaml, OpenShiftPod.class);
++ }
++
++ @Test(groups = {"Live"})
++ public void testOpenShiftPod() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + OpenShiftPod.class.getName(),
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts:",
++ " - \"8080\"",
++ " shell.env:",
++ " CLUSTER_ID: \"id\"",
++ " CLUSTER_TOKEN: \"token\"");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkPod(app, OpenShiftPod.class);
++ }
++
++ @Test(groups = {"Live"}, enabled = false)
++ public void testOpenShiftPodCatalogEntry() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: openshift-pod-entity",
++ " brooklyn.config:",
++ " docker.container.imageName: tomcat",
++ " docker.container.inboundPorts:",
++ " - \"8080\"",
++ " shell.env:",
++ " CLUSTER_ID: \"id\"",
++ " CLUSTER_TOKEN: \"token\"");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkPod(app, OpenShiftPod.class);
++ }
++
++ @Test(groups = {"Live"})
++ public void testNginxOpenShiftResource() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: " + OpenShiftResource.class.getName(),
++ " id: nginx",
++ " name: \"nginx\"",
++ " brooklyn.config:",
++ " resource: classpath://nginx.yaml");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkNginxResource(app, OpenShiftResource.class);
++ }
++
++ @Test(groups = {"Live"}, enabled = false)
++ public void testNginxOpenShiftResourceCatalogEntry() throws Exception {
++ String yaml = Joiner.on("\n").join(
++ locationYaml,
++ "services:",
++ " - type: openshift-resource-entity",
++ " id: nginx",
++ " name: \"nginx\"",
++ " brooklyn.config:",
++ " resource: classpath://nginx.yaml");
++
++ Entity app = createStartWaitAndLogApplication(yaml);
++ checkNginxResource(app, OpenShiftResource.class);
++ }
++
++}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/resources/nginx-replication-controller.yaml
----------------------------------------------------------------------
diff --cc locations/container/src/test/resources/nginx-replication-controller.yaml
index 0000000,0000000..8aeafac
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/resources/nginx-replication-controller.yaml
@@@ -1,0 -1,0 +1,37 @@@
++# 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.
++
++apiVersion: v1
++kind: ReplicationController
++metadata:
++ name: nginx-replication-controller
++ namespace: default
++spec:
++ replicas: 2
++ selector:
++ app: nginx
++ template:
++ metadata:
++ name: nginx
++ labels:
++ app: nginx
++ spec:
++ containers:
++ - name: nginx
++ image: nginx
++ ports:
++ - containerPort: 80
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/92a65d45/locations/container/src/test/resources/nginx-service.yaml
----------------------------------------------------------------------
diff --cc locations/container/src/test/resources/nginx-service.yaml
index 0000000,0000000..db16d5b
new file mode 100644
--- /dev/null
+++ b/locations/container/src/test/resources/nginx-service.yaml
@@@ -1,0 -1,0 +1,29 @@@
++# 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.
++
++apiVersion: v1
++kind: Service
++metadata:
++ name: nginx-service
++ namespace: default
++spec:
++ type: NodePort
++ ports:
++ - name: "http"
++ port: 80
++ selector:
++ app: nginx