Add KubernetesMachineLocation interface and implementations

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

Branch: refs/heads/master
Commit: fcb0db07b5afa3de7718975977349b5144e1a389
Parents: 90123e4
Author: Andrew Donald Kennedy <[email protected]>
Authored: Wed Feb 1 19:32:36 2017 +0000
Committer: Andrew Donald Kennedy <[email protected]>
Committed: Fri May 19 14:01:20 2017 +0100

----------------------------------------------------------------------
 .../kubernetes/entity/KubernetesPod.java        |  2 +
 .../KubernetesEmptyMachineLocation.java         | 68 ++++++++++++++++++
 .../kubernetes/location/KubernetesLocation.java | 75 ++++++++++++++------
 .../location/KubernetesMachineLocation.java     | 27 +++++++
 .../location/KubernetesSshMachineLocation.java  | 27 +++++++
 .../KubernetesLocationYamlLiveTest.java         | 39 +++++++++-
 6 files changed, 215 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/fcb0db07/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java
 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java
index 4c334ec..35da4b8 100644
--- 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java
+++ 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesPod.java
@@ -77,4 +77,6 @@ public interface KubernetesPod extends DockerContainer {
     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/fcb0db07/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesEmptyMachineLocation.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesEmptyMachineLocation.java
 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesEmptyMachineLocation.java
new file mode 100644
index 0000000..ffda23f
--- /dev/null
+++ 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesEmptyMachineLocation.java
@@ -0,0 +1,68 @@
+package io.cloudsoft.amp.containerservice.kubernetes.location;
+
+import java.net.InetAddress;
+import java.util.Set;
+
+import org.apache.brooklyn.api.location.MachineDetails;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.OsDetails;
+import org.apache.brooklyn.core.location.AbstractLocation;
+import org.apache.brooklyn.util.net.Networking;
+
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * A {@link MachineLocation} represemnting a Kubernetes resource that does not 
support SSH access.
+ *
+ * @see {@link KubernetesSshMachineLocation}
+ */
+public class KubernetesEmptyMachineLocation extends AbstractLocation 
implements KubernetesMachineLocation {
+
+    @Override
+    public String getHostname() {
+        return getResourceName();
+    }
+
+    @Override
+    public Set<String> getPublicAddresses() {
+        return ImmutableSet.of("0.0.0.0");
+    }
+
+    @Override
+    public Set<String> getPrivateAddresses() {
+        return ImmutableSet.of("0.0.0.0");
+    }
+
+    @Override
+    public InetAddress getAddress() {
+        return Networking.getInetAddressWithFixedName("0.0.0.0");
+    }
+
+    @Override
+    public OsDetails getOsDetails() {
+        return null;
+        // throw new UnsupportedOperationException("No OS details for empty 
KubernetesMachineLocation");
+    }
+
+    @Override
+    public MachineDetails getMachineDetails() {
+        return null;
+        // throw new UnsupportedOperationException("No machine details for 
empty KubernetesMachineLocation");
+    }
+
+    @Override
+    public String getResourceName() {
+        return config().get(KUBERNETES_RESOURCE_NAME);
+    }
+
+    @Override
+    public String getResourceType() {
+        return config().get(KUBERNETES_RESOURCE_TYPE);
+    }
+
+    @Override
+    public String getNamespace() {
+        return config().get(KUBERNETES_NAMESPACE);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/fcb0db07/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java
 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java
index 28f193c..f5123b9 100644
--- 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java
+++ 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocation.java
@@ -128,6 +128,7 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
     public static final String CLOUDSOFT_APPLICATION_ID = 
"cloudsoft.io/application-id";
     public static final String KUBERNETES_DOCKERCFG = 
"kubernetes.io/dockercfg";
 
+    public static final String PHASE_AVAILABLE = "Available";
     public static final String PHASE_TERMINATING = "Terminating";
     public static final String PHASE_ACTIVE = "Active";
 
@@ -185,15 +186,41 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         if (isKubernetesResource(entity)) {
             return createKubernetesResourceLocation(entity, setup);
         } else {
+            // Heuristic for determining whether KubernetesPod is simply a 
parent entity
+            if (isKubernetesPod(entity) &&
+                    entity.config().get(DockerContainer.IMAGE_NAME) == null &&
+                    entity.config().get(DockerContainer.INBOUND_TCP_PORTS) == 
null &&
+                    entity.getChildren().size() > 0) {
+                return createEmptyKubernetesMachineLocation(entity);
+            }
             return createKubernetesContainerLocation(entity, setup);
         }
     }
 
+    /** @deprecated This mechanism will be removed in future releases */
+    @Deprecated
+    public KubernetesEmptyMachineLocation 
createEmptyKubernetesMachineLocation(Entity entity) {
+        LOG.warn("IMPORTANT: Deprecated behaviour: Use of KubernetesPod as a 
parent entity is deprecated and support will be removed in future versions");
+
+        LocationSpec<KubernetesEmptyMachineLocation> locationSpec = 
LocationSpec.create(KubernetesEmptyMachineLocation.class)
+                .configure(KubernetesLocationConfig.CALLER_CONTEXT, entity)
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, 
KubernetesPod.EMPTY)
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, 
entity.getId());
+
+        KubernetesEmptyMachineLocation machine = 
getManagementContext().getLocationManager().createLocation(locationSpec);
+
+        return machine;
+    }
+
     @Override
     public void release(MachineLocation machine) {
         Entity entity = validateCallerContext(machine);
         if (isKubernetesResource(entity)) {
-            deleteKubernetesResourceLocation(entity);
+            if (machine instanceof KubernetesEmptyMachineLocation && 
KubernetesPod.EMPTY.equals(machine.config().get(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE)))
 {
+                // Nothing to do, the location does not represent a Kubernetes 
resource
+            } else {
+                deleteKubernetesResourceLocation(entity);
+            }
         } else {
             deleteKubernetesContainerLocation(entity, machine);
         }
@@ -346,25 +373,27 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         entity.sensors().set(KubernetesResource.RESOURCE_NAME, resourceName);
         entity.sensors().set(KubernetesResource.RESOURCE_TYPE, resourceType);
 
-        LocationSpec<SshMachineLocation> locationSpec = 
LocationSpec.create(SshMachineLocation.class)
-                        .configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT));
+        LocationSpec<? extends KubernetesMachineLocation> locationSpec = 
LocationSpec.create(KubernetesSshMachineLocation.class);
         if (!findResourceAddress(locationSpec, entity, metadata, resourceType, 
resourceName, namespace)) {
             LOG.info("Resource {} with type {} has no associated address", 
resourceName, resourceType);
-            locationSpec.configure("address", "0.0.0.0");
+            locationSpec = 
LocationSpec.create(KubernetesEmptyMachineLocation.class);
         }
+        locationSpec.configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT))
+                .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, 
namespace)
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, 
resourceName)
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, 
resourceType);
 
-        SshMachineLocation machine = 
getManagementContext().getLocationManager().createLocation(locationSpec);
+        KubernetesMachineLocation machine = 
getManagementContext().getLocationManager().createLocation(locationSpec);
 
-        if (resourceType.equals(KubernetesResource.SERVICE)) {
+        if (resourceType.equals(KubernetesResource.SERVICE) && machine 
instanceof KubernetesSshMachineLocation) {
             Service service = getService(namespace, resourceName);
-            registerPortMappings(machine, entity, service);
-
+            registerPortMappings((KubernetesSshMachineLocation) machine, 
entity, service);
         }
 
         return machine;
     }
 
-    protected boolean findResourceAddress(LocationSpec<SshMachineLocation> 
locationSpec, Entity entity, HasMetadata metadata, String resourceType, String 
resourceName, String namespace) {
+    protected boolean findResourceAddress(LocationSpec<? extends 
KubernetesMachineLocation> locationSpec, Entity entity, HasMetadata metadata, 
String resourceType, String resourceName, String namespace) {
         if (resourceType.equals(KubernetesResource.DEPLOYMENT) || 
resourceType.equals(KubernetesResource.REPLICATION_CONTROLLER) || 
resourceType.equals(KubernetesResource.POD)) {
             Map<String, String> labels = MutableMap.of();
             if (resourceType.equals(KubernetesResource.DEPLOYMENT)) {
@@ -443,8 +472,12 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         entity.sensors().set(KubernetesPod.KUBERNETES_POD, 
pod.getMetadata().getName());
         entity.sensors().set(KubernetesPod.KUBERNETES_SERVICE, 
service.getMetadata().getName());
 
-        LocationSpec<SshMachineLocation> locationSpec = 
prepareLocationSpec(entity, setup, namespace, deploymentName, service, pod);
-        SshMachineLocation machine = 
getManagementContext().getLocationManager().createLocation(locationSpec);
+        LocationSpec<KubernetesSshMachineLocation> locationSpec = 
prepareSshableLocationSpec(entity, setup, namespace, deploymentName, service, 
pod)
+                .configure(KubernetesMachineLocation.KUBERNETES_NAMESPACE, 
namespace.getMetadata().getName())
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_NAME, 
deploymentName)
+                .configure(KubernetesMachineLocation.KUBERNETES_RESOURCE_TYPE, 
KubernetesResource.DEPLOYMENT);
+
+        KubernetesSshMachineLocation machine = 
getManagementContext().getLocationManager().createLocation(locationSpec);
         registerPortMappings(machine, entity, service);
         if (!isDockerContainer(entity)) {
             waitForSshable(machine, Duration.FIVE_MINUTES);
@@ -482,11 +515,11 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         }
     }
 
-    protected void registerPortMappings(SshMachineLocation machine, Entity 
entity, Service service) {
+    protected void registerPortMappings(KubernetesSshMachineLocation machine, 
Entity entity, Service service) {
         PortForwardManager portForwardManager = (PortForwardManager) 
getManagementContext().getLocationRegistry()
                 
.getLocationManaged(PortForwardManagerLocationResolver.PFM_GLOBAL_SPEC);
         List<ServicePort> ports = service.getSpec().getPorts();
-        String publicHostText = machine.getSshHostAndPort().getHostText();
+        String publicHostText = ((SshMachineLocation) 
machine).getSshHostAndPort().getHostText();
         LOG.debug("Recording port-mappings for container {} of {}: {}", new 
Object[] { machine, this, ports });
 
         for (ServicePort port : ports) {
@@ -741,10 +774,10 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         return 
client.services().inNamespace(namespace).withName(serviceName).get();
     }
 
-    protected LocationSpec<SshMachineLocation> prepareLocationSpec(Entity 
entity, ConfigBag setup, Namespace namespace, String deploymentName, Service 
service, Pod pod) {
+    protected LocationSpec<KubernetesSshMachineLocation> 
prepareSshableLocationSpec(Entity entity, ConfigBag setup, Namespace namespace, 
String deploymentName, Service service, Pod pod) {
         InetAddress node = 
Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName());
         String podAddress = pod.getStatus().getPodIP();
-        LocationSpec<SshMachineLocation> locationSpec = 
LocationSpec.create(SshMachineLocation.class)
+        LocationSpec<KubernetesSshMachineLocation> locationSpec = 
LocationSpec.create(KubernetesSshMachineLocation.class)
                 .configure("address", node)
                 .configure(SshMachineLocation.PRIVATE_ADDRESSES, 
ImmutableSet.of(podAddress))
                 .configure(CALLER_CONTEXT, setup.get(CALLER_CONTEXT));
@@ -775,13 +808,13 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
         for (final String persistentVolume : volumes) {
             PersistentVolume volume = new PersistentVolumeBuilder()
                     .withNewMetadata()
-                    .withName(persistentVolume)
-                    .withLabels(ImmutableMap.of("type", "local")) // TODO make 
it configurable
+                        .withName(persistentVolume)
+                        .withLabels(ImmutableMap.of("type", "local")) // TODO 
make it configurable
                     .endMetadata()
                     .withNewSpec()
-                    .addToCapacity("storage", new 
QuantityBuilder().withAmount("20").build()) // TODO make it configurable
-                    .addToAccessModes("ReadWriteOnce") // TODO make it 
configurable
-                    .withNewHostPath().withPath("/tmp/pv-1").endHostPath() // 
TODO make it configurable
+                        .addToCapacity("storage", new 
QuantityBuilder().withAmount("20").build()) // TODO make it configurable
+                        .addToAccessModes("ReadWriteOnce") // TODO make it 
configurable
+                        .withNewHostPath().withPath("/tmp/pv-1").endHostPath() 
// TODO make it configurable
                     .endSpec()
                     .build();
             client.persistentVolumes().create(volume);
@@ -790,7 +823,7 @@ public class KubernetesLocation extends AbstractLocation 
implements MachineProvi
                 public Boolean call() {
                     PersistentVolume pv = 
client.persistentVolumes().withName(persistentVolume).get();
                     return pv != null && pv.getStatus() != null
-                            && pv.getStatus().getPhase().equals("Available");
+                            && 
pv.getStatus().getPhase().equals(PHASE_AVAILABLE);
                 }
                 @Override
                 public String getFailureMessage() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/fcb0db07/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesMachineLocation.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesMachineLocation.java
 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesMachineLocation.java
new file mode 100644
index 0000000..9c620bb
--- /dev/null
+++ 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesMachineLocation.java
@@ -0,0 +1,27 @@
+package io.cloudsoft.amp.containerservice.kubernetes.location;
+
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+
+public interface KubernetesMachineLocation extends MachineLocation {
+
+    ConfigKey<String> KUBERNETES_NAMESPACE = ConfigKeys.builder(String.class, 
"kubernetes.namespace")
+            .description("Namespace for the KubernetesMachineLocation")
+            .build();
+
+    ConfigKey<String> KUBERNETES_RESOURCE_NAME = 
ConfigKeys.builder(String.class, "kubernetes.name")
+            .description("Name of the resource represented by the 
KubernetesMachineLocation")
+            .build();
+
+    ConfigKey<String> KUBERNETES_RESOURCE_TYPE = 
ConfigKeys.builder(String.class, "kubernetes.type")
+            .description("Type of the resource represented by the 
KubernetesMachineLocation")
+            .build();
+
+    public String getResourceName();
+
+    public String getResourceType();
+
+    public String getNamespace();
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/fcb0db07/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesSshMachineLocation.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesSshMachineLocation.java
 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesSshMachineLocation.java
new file mode 100644
index 0000000..97fabbb
--- /dev/null
+++ 
b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesSshMachineLocation.java
@@ -0,0 +1,27 @@
+package io.cloudsoft.amp.containerservice.kubernetes.location;
+
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+/**
+ * A {@link MachineLocation} represemnting a Kubernetes resource that allows 
SSH access.
+ *
+ * @see {@link KubernetesSshMachineLocation}
+ */
+public class KubernetesSshMachineLocation extends SshMachineLocation 
implements KubernetesMachineLocation {
+
+    @Override
+    public String getResourceName() {
+        return config().get(KUBERNETES_RESOURCE_NAME);
+    }
+
+    @Override
+    public String getResourceType() {
+        return config().get(KUBERNETES_RESOURCE_TYPE);
+    }
+
+    @Override
+    public String getNamespace() {
+        return config().get(KUBERNETES_NAMESPACE);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/fcb0db07/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationYamlLiveTest.java
----------------------------------------------------------------------
diff --git 
a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationYamlLiveTest.java
 
b/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationYamlLiveTest.java
index 9654d6d..f783630 100644
--- 
a/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationYamlLiveTest.java
+++ 
b/kubernetes-location/src/test/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationYamlLiveTest.java
@@ -259,9 +259,8 @@ public class KubernetesLocationYamlLiveTest extends 
AbstractYamlTest {
         return entity;
     }
 
-
     @Test(groups={"Live"})
-    public void testWordpressInContainers() throws Exception {
+    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(
@@ -297,6 +296,42 @@ public class KubernetesLocationYamlLiveTest extends 
AbstractYamlTest {
     }
 
     @Test(groups={"Live"})
+    public void testWordpressInContainersWithPodParent() 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(),
+                "    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.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 testWordpressInPods() throws Exception {
         // TODO docker.container.inboundPorts doesn't accept list of ints - 
need to use quotes
         String randomId = Identifiers.makeRandomLowercaseId(4);

Reply via email to