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
