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:35:57 UTC

[15/50] [abbrv] brooklyn-server git commit: Refactoring of kubernetes location to use KubernetesPod entity

Refactoring of kubernetes location to use KubernetesPod entity


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

Branch: refs/heads/master
Commit: 422d78da0e63c10c4816f0570277e9a9b2e00419
Parents: eb94f08
Author: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Authored: Sat Jan 28 03:14:37 2017 +0000
Committer: Andrew Donald Kennedy <an...@cloudsoftcorp.com>
Committed: Fri May 19 14:01:20 2017 +0100

----------------------------------------------------------------------
 .../kubernetes/entity/KubernetesPod.java        |  84 ++++++++-
 .../kubernetes/entity/KubernetesResource.java   |  12 ++
 .../kubernetes/location/KubernetesLocation.java | 185 +++++++++++--------
 .../location/KubernetesLocationConfig.java      |  73 +-------
 .../KubernetesLocationYamlLiveTest.java         | 148 ++++++++-------
 5 files changed, 301 insertions(+), 201 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/422d78da/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 ae9bdb3..475f941 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
@@ -1,10 +1,92 @@
 package io.cloudsoft.amp.containerservice.kubernetes.entity;
 
+import java.util.List;
+import java.util.Map;
+
 import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
+import org.apache.brooklyn.core.sensor.Sensors;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
 
 import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer;
 import io.cloudsoft.amp.containerservice.kubernetes.location.KubernetesLocationConfig;
 
 @ImplementedBy(KubernetesPodImpl.class)
-public interface KubernetesPod extends DockerContainer, KubernetesLocationConfig {
+public interface KubernetesPod extends DockerContainer {
+
+    ConfigKey<String> NAMESPACE = KubernetesLocationConfig.NAMESPACE;
+
+    ConfigKey<String> POD = ConfigKeys.builder(String.class)
+            .name("pod")
+            .description("The name of the pod")
+            .constraint(Predicates.<String>notNull())
+            .build();
+
+    @SuppressWarnings("serial")
+    ConfigKey<List<String>> PERSISTENT_VOLUMES = ConfigKeys.builder(new TypeToken<List<String>>() {})
+            .name("persistentVolumes")
+            .description("Persistent volumes used by the pod")
+            .constraint(Predicates.<List<String>>notNull())
+            .build();
+
+    ConfigKey<String> DEPLOYMENT = ConfigKeys.builder(String.class)
+            .name("deployment")
+            .description("The name of the service the deployed pod will use")
+            .constraint(Predicates.<String>notNull())
+            .build();
+
+    ConfigKey<Integer> REPLICAS = ConfigKeys.builder(Integer.class)
+            .name("replicas")
+            .description("Number of replicas in the pod")
+            .constraint(Predicates.notNull())
+            .defaultValue(1)
+            .build();
+
+    @SuppressWarnings("serial")
+    ConfigKey<Map<String, String>> SECRETS = ConfigKeys.builder(new TypeToken<Map<String, String>>() {})
+            .name("secrets")
+            .description("Secrets to be added to the pod")
+            .build();
+
+    @SuppressWarnings("serial")
+    ConfigKey<Map<String, String>> LIMITS = ConfigKeys.builder(new TypeToken<Map<String, String>>() {})
+            .name("limits")
+            .description("Container resource limits for the pod")
+            .build();
+
+    ConfigKey<Boolean> PRIVILEGED = ConfigKeys.builder(Boolean.class)
+            .name("privileged")
+            .description("Whether the container is privileged")
+            .defaultValue(false)
+            .build();
+
+    MapConfigKey<Object> METADATA = new MapConfigKey.Builder<Object>(Object.class, "metadata")
+            .description("Metadata to set on the pod")
+            .defaultValue(ImmutableMap.<String, Object>of())
+            .typeInheritance(BasicConfigInheritance.DEEP_MERGE)
+            .runtimeInheritance(BasicConfigInheritance.NOT_REINHERITED_ELSE_DEEP_MERGE)
+            .build();
+
+    AttributeSensor<String> KUBERNETES_DEPLOYMENT = Sensors.builder(String.class, "kubernetes.deployment")
+            .description("Deployment resources run in")
+            .build();
+
+    AttributeSensor<String> KUBERNETES_NAMESPACE = Sensors.builder(String.class, "kubernetes.namespace")
+            .description("Namespace that resources run in")
+            .build();
+
+    AttributeSensor<String> KUBERNETES_SERVICE = Sensors.builder(String.class, "kubernetes.service")
+            .description("Service that exposes the deployment")
+            .build();
+
+    AttributeSensor<String> KUBERNETES_POD = Sensors.builder(String.class, "kubernetes.pod")
+            .description("Pod running the deployment")
+            .build();
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/422d78da/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java
----------------------------------------------------------------------
diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java
index 3274cb2..320c924 100644
--- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java
+++ b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/entity/KubernetesResource.java
@@ -25,4 +25,16 @@ public interface KubernetesResource extends SoftwareProcess {
                                              .description("Kubernetes resource name")
                                              .build();
 
+    AttributeSensor<String> KUBERNETES_NAMESPACE = KubernetesPod.KUBERNETES_NAMESPACE;
+
+    String POD = "Pod";
+    String DEPLOYMENT = "Deployment";
+    String REPLICA_SET = "ReplicaSet";
+    String CONFIG_MAP = "ConfigMap";
+    String PERSISTENT_VOLUME = "PersistentVolume";
+    String SECRET = "Secret";
+    String SERVICE = "Service";
+    String REPLICATION_CONTROLLER = "ReplicationController";
+    String NAMESPACE = "Namespace";
+
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/422d78da/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 1cf9545..cd44f37 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
@@ -3,6 +3,7 @@ package io.cloudsoft.amp.containerservice.kubernetes.location;
 import java.io.InputStream;
 import java.net.InetAddress;
 import java.nio.charset.Charset;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -17,6 +18,7 @@ import org.apache.brooklyn.api.location.NoMachinesAvailableException;
 import org.apache.brooklyn.api.location.PortRange;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.location.AbstractLocation;
@@ -49,6 +51,7 @@ import com.google.common.base.Functions;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
@@ -63,6 +66,7 @@ import com.google.common.net.HostAndPort;
 import io.cloudsoft.amp.containerservice.ThreadedRepeater;
 import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer;
 import io.cloudsoft.amp.containerservice.dockerlocation.DockerJcloudsLocation;
+import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesPod;
 import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesResource;
 import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.ContainerBuilder;
@@ -98,7 +102,7 @@ import io.fabric8.kubernetes.api.model.extensions.DeploymentStatus;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClientException;
 
-public class KubernetesLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>, CloudLocationConfig, KubernetesLocationConfig {
+public class KubernetesLocation extends AbstractLocation implements MachineProvisioningLocation<MachineLocation>, KubernetesLocationConfig {
 
     /*
      * TODO
@@ -173,6 +177,14 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         if (isKubernetesResource(entity)) {
             return createKubernetesResourceLocation(entity, setup);
         } else {
+            // Heuristic for determining whether KubernetesPod is simply a container
+            if (isKubernetesPod(entity) &&
+                    entity.config().get(DockerContainer.IMAGE_NAME) == null &&
+                    entity.config().get(DockerContainer.INBOUND_TCP_PORTS) == null &&
+                    entity.config().get(KubernetesPod.POD) == null &&
+                    entity.getChildren().size() > 0) {
+                return null;
+            }
             return createKubernetesContainerLocation(entity, setup);
         }
     }
@@ -188,10 +200,10 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     }
 
     protected void deleteKubernetesContainerLocation(Entity entity, MachineLocation machine) {
-        final String namespace = entity.sensors().get(KUBERNETES_NAMESPACE);
-        final String deployment = entity.sensors().get(KUBERNETES_DEPLOYMENT);
-        final String pod = entity.sensors().get(KUBERNETES_POD);
-        final String service = entity.sensors().get(KUBERNETES_SERVICE);
+        final String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE);
+        final String deployment = entity.sensors().get(KubernetesPod.KUBERNETES_DEPLOYMENT);
+        final String pod = entity.sensors().get(KubernetesPod.KUBERNETES_POD);
+        final String service = entity.sensors().get(KubernetesPod.KUBERNETES_SERVICE);
 
         undeploy(namespace, deployment, pod);
 
@@ -215,7 +227,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     }
 
     protected void deleteKubernetesResourceLocation(Entity entity) {
-        final String namespace = entity.sensors().get(KUBERNETES_NAMESPACE);
+        final String namespace = entity.sensors().get(KubernetesPod.KUBERNETES_NAMESPACE);
         final String resourceType = entity.sensors().get(KubernetesResource.RESOURCE_TYPE);
         final String resourceName = entity.sensors().get(KubernetesResource.RESOURCE_NAME);
 
@@ -227,21 +239,21 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     protected boolean handleResourceDelete(String resourceType, String resourceName, String namespace) {
         try {
             switch (resourceType) {
-                case "Deployment":
+                case KubernetesResource.DEPLOYMENT:
                     return client.extensions().deployments().inNamespace(namespace).withName(resourceName).delete();
-                case "ReplicaSet":
+                case KubernetesResource.REPLICA_SET:
                     return client.extensions().replicaSets().inNamespace(namespace).withName(resourceName).delete();
-                case "ConfigMap":
+                case KubernetesResource.CONFIG_MAP:
                     return client.configMaps().inNamespace(namespace).withName(resourceName).delete();
-                case "PersistentVolume":
+                case KubernetesResource.PERSISTENT_VOLUME:
                     return client.persistentVolumes().withName(resourceName).delete();
-                case "Secret":
+                case KubernetesResource.SECRET:
                     return client.secrets().inNamespace(namespace).withName(resourceName).delete();
-                case "Service":
+                case KubernetesResource.SERVICE:
                     return client.services().inNamespace(namespace).withName(resourceName).delete();
-                case "ReplicationController":
+                case KubernetesResource.REPLICATION_CONTROLLER:
                     return client.replicationControllers().inNamespace(namespace).withName(resourceName).delete();
-                case "Namespace":
+                case KubernetesResource.NAMESPACE:
                     return client.namespaces().withName(resourceName).delete();
             }
         } catch (KubernetesClientException kce) {
@@ -330,7 +342,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         String namespace = metadata.getMetadata().getNamespace();
         LOG.debug("Resource {} (type {}) deployed to {}", resourceName, resourceType, namespace);
 
-        entity.sensors().set(KUBERNETES_NAMESPACE, namespace);
+        entity.sensors().set(KubernetesPod.KUBERNETES_NAMESPACE, namespace);
         entity.sensors().set(KubernetesResource.RESOURCE_NAME, resourceName);
         entity.sensors().set(KubernetesResource.RESOURCE_TYPE, resourceType);
 
@@ -343,7 +355,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
 
         SshMachineLocation machine = getManagementContext().getLocationManager().createLocation(locationSpec);
 
-        if (resourceType.equals("Service")) {
+        if (resourceType.equals(KubernetesResource.SERVICE)) {
             Service service = getService(namespace, resourceName);
             registerPortMappings(machine, entity, service);
 
@@ -353,17 +365,17 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     }
 
     protected boolean findResourceAddress(LocationSpec<SshMachineLocation> locationSpec, Entity entity, HasMetadata metadata, String resourceType, String resourceName, String namespace) {
-        if (resourceType.equals("Deployment") || resourceType.equals("ReplicationController") || resourceType.equals("Pod")) {
+        if (resourceType.equals(KubernetesResource.DEPLOYMENT) || resourceType.equals(KubernetesResource.REPLICATION_CONTROLLER) || resourceType.equals(KubernetesResource.POD)) {
             Map<String, String> labels = MutableMap.of();
-            if (resourceType.equals("Deployment")) {
+            if (resourceType.equals(KubernetesResource.DEPLOYMENT)) {
                 Deployment deployment = (Deployment) metadata;
                 labels = deployment.getSpec().getTemplate().getMetadata().getLabels();
-            } else if (resourceType.equals("ReplicationController")) {
+            } else if (resourceType.equals(KubernetesResource.REPLICATION_CONTROLLER)) {
                 ReplicationController replicationController = (ReplicationController) metadata;
                 labels = replicationController.getSpec().getTemplate().getMetadata().getLabels();
             }
-            Pod pod = resourceType.equals("Pod") ? getPod(namespace, resourceName) : getPod(namespace, labels);
-            entity.sensors().set(KubernetesLocationConfig.KUBERNETES_POD, pod.getMetadata().getName());
+            Pod pod = resourceType.equals(KubernetesResource.POD) ? getPod(namespace, resourceName) : getPod(namespace, labels);
+            entity.sensors().set(KubernetesPod.KUBERNETES_POD, pod.getMetadata().getName());
 
             InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName());
             String podAddress = pod.getStatus().getPodIP();
@@ -372,7 +384,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
             locationSpec.configure(SshMachineLocation.PRIVATE_ADDRESSES, ImmutableSet.of(podAddress));
 
             return true;
-        } else if (resourceType.equals("Service")) {
+        } else if (resourceType.equals(KubernetesResource.SERVICE)) {
             getService(namespace, resourceName);
 
             Endpoints endpoints = client.endpoints().inNamespace(namespace).withName(resourceName).get();
@@ -385,7 +397,7 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
 
             try {
                 Pod pod = getPod(namespace, podName);
-                entity.sensors().set(KubernetesLocationConfig.KUBERNETES_POD, podName);
+                entity.sensors().set(KubernetesPod.KUBERNETES_POD, podName);
 
                 InetAddress node = Networking.getInetAddressWithFixedName(pod.getSpec().getNodeName());
                 locationSpec.configure("address", node);
@@ -400,22 +412,22 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     }
 
     protected MachineLocation createKubernetesContainerLocation(Entity entity, ConfigBag setup) {
-        String deploymentName = findDeploymentName(entity, setup);
-        Integer replicas = setup.get(REPLICAS);
-        List<String> volumes = setup.get(KubernetesLocationConfig.PERSISTENT_VOLUMES);
-        Map<String, String> secrets = setup.get(KubernetesLocationConfig.SECRETS);
-        Map<String, String> limits = setup.get(KubernetesLocationConfig.LIMITS);
-        Boolean privileged = setup.get(KubernetesLocationConfig.PRIVILEGED);
+        String deploymentName = lookup(KubernetesPod.DEPLOYMENT, entity, setup, entity.getId());
+        Integer replicas = lookup(KubernetesPod.REPLICAS, entity, setup);
+        List<String> volumes = lookup(KubernetesPod.PERSISTENT_VOLUMES, entity, setup);
+        Map<String, String> secrets = lookup(KubernetesPod.SECRETS, entity, setup);
+        Map<String, String> limits = lookup(KubernetesPod.LIMITS, entity, setup);
+        Boolean privileged = lookup(KubernetesPod.PRIVILEGED, entity, setup);
         String imageName = findImageName(entity, setup);
         Iterable<Integer> inboundPorts = findInboundPorts(entity, setup);
         Map<String, String> env = findEnvironmentVariables(entity, setup, imageName);
-        Map<String, String> metadata = findMetadata(entity, deploymentName);
+        Map<String, String> metadata = findMetadata(entity, setup, deploymentName);
 
         if (volumes != null) {
             createPersistentVolumes(volumes);
         }
 
-        Namespace namespace = createOrGetNamespace(setup.get(NAMESPACE), setup.get(CREATE_NAMESPACE));
+        Namespace namespace = createOrGetNamespace(lookup(NAMESPACE, entity, setup), setup.get(CREATE_NAMESPACE));
 
         if (secrets != null) {
             createSecrets(namespace.getMetadata().getName(), secrets);
@@ -426,10 +438,10 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         Service service = exposeService(namespace.getMetadata().getName(), metadata, deploymentName, inboundPorts);
         Pod pod = getPod(namespace.getMetadata().getName(), metadata);
 
-        entity.sensors().set(KubernetesLocationConfig.KUBERNETES_NAMESPACE, namespace.getMetadata().getName());
-        entity.sensors().set(KubernetesLocationConfig.KUBERNETES_DEPLOYMENT, deploymentName);
-        entity.sensors().set(KubernetesLocationConfig.KUBERNETES_POD, pod.getMetadata().getName());
-        entity.sensors().set(KubernetesLocationConfig.KUBERNETES_SERVICE, service.getMetadata().getName());
+        entity.sensors().set(KubernetesPod.KUBERNETES_NAMESPACE, namespace.getMetadata().getName());
+        entity.sensors().set(KubernetesPod.KUBERNETES_DEPLOYMENT, deploymentName);
+        entity.sensors().set(KubernetesPod.KUBERNETES_POD, pod.getMetadata().getName());
+        entity.sensors().set(KubernetesPod.KUBERNETES_SERVICE, service.getMetadata().getName());
 
         LocationSpec<SshMachineLocation> locationSpec = prepareLocationSpec(entity, setup, namespace, deploymentName, service, pod);
         SshMachineLocation machine = getManagementContext().getLocationManager().createLocation(locationSpec);
@@ -497,10 +509,6 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         entity.enrichers().add(EnricherSpec.create(OnPublicNetworkEnricher.class).configure(OnPublicNetworkEnricher.MAP_MATCHING, "kubernetes.[a-zA-Z0-9][a-zA-Z0-9-_]*.port"));
     }
 
-    protected String findDeploymentName(Entity entity, ConfigBag setup) {
-        return Optional.fromNullable(setup.get(KubernetesLocationConfig.DEPLOYMENT)).or(entity.getId());
-    }
-
     protected synchronized Namespace createOrGetNamespace(final String name, Boolean create) {
         Namespace namespace = client.namespaces().withName(name).get();
         ExitCondition namespaceReady = new ExitCondition() {
@@ -573,14 +581,13 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
 
         String json = String.format("{\"https://index.docker.io/v1/\":{\"auth\":\"%s\"}}", auth);
         String base64encoded = BaseEncoding.base64().encode(json.getBytes(Charset.defaultCharset()));
-        secret = client.secrets().inNamespace(namespace)
-                .create(new SecretBuilder()
+        secret = new SecretBuilder()
                         .withNewMetadata()
                             .withName(secretName)
                         .endMetadata()
                         .withType("kubernetes.io/dockercfg")
                         .withData(ImmutableMap.of(".dockercfg", base64encoded))
-                        .build());
+                        .build();
         try {
             client.secrets().inNamespace(namespace).create(secret);
         } catch (KubernetesClientException e) {
@@ -642,6 +649,12 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
                 .withNewSpec()
                     .addToContainers(container)
                 .endSpec();
+        if (isKubernetesPod(entity)) {
+            String podName = entity.config().get(KubernetesPod.POD);
+            if (Strings.isNonBlank(podName)) {
+                podTemplateSpecBuilder.editOrNewMetadata().withName(podName).endMetadata();
+            }
+        }
         if (secrets != null) {
             for (String secretName : secrets.keySet()) {
                 podTemplateSpecBuilder.withNewSpec()
@@ -797,53 +810,55 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     protected Entity validateCallerContext(ConfigBag setup) {
         // Lookup entity flags
         Object callerContext = setup.get(LocationConfigKeys.CALLER_CONTEXT);
-        if (callerContext == null || !(callerContext instanceof Entity)) {
+        if (callerContext instanceof Entity) {
+            return (Entity) callerContext;
+        } else {
             throw new IllegalStateException("Invalid caller context: " + callerContext);
         }
-        return (Entity) callerContext;
     }
 
     protected Entity validateCallerContext(MachineLocation machine) {
         // Lookup entity flags
         Object callerContext = machine.config().get(LocationConfigKeys.CALLER_CONTEXT);
-        if (callerContext == null || !(callerContext instanceof Entity)) {
+        if (callerContext instanceof Entity) {
+            return (Entity) callerContext;
+        } else {
             throw new IllegalStateException("Invalid caller context: " + callerContext);
         }
-        return (Entity) callerContext;
     }
 
-
-    protected Map<String, String> findMetadata(Entity entity, String value) {
-        Map<String, String> metadata = Maps.newHashMap();
-        if (!isDockerContainer(entity)) {
-            metadata.put(SSHABLE_CONTAINER, value);
+    protected Map<String, String> findMetadata(Entity entity, ConfigBag setup, String value) {
+        Map<String, String> podMetadata = Maps.newLinkedHashMap();
+        if (isDockerContainer(entity)) {
+            podMetadata.put(IMMUTABLE_CONTAINER_KEY, value);
         } else {
-            metadata.put(IMMUTABLE_CONTAINER_KEY, value);
+            podMetadata.put(SSHABLE_CONTAINER, value);
         }
-        return metadata;
+
+        Map<String, Object> metadata = MutableMap.<String, Object>builder()
+                .putAll(MutableMap.copyOf(setup.get(KubernetesPod.METADATA)))
+                .putAll(MutableMap.copyOf(entity.config().get(KubernetesPod.METADATA)))
+                .putAll(podMetadata)
+                .build();
+        return Maps.transformValues(metadata, Functions.toStringFunction());
     }
 
     /**
-     * Appends the config's "env" with the {@code CLOUDSOFT_ROOT_PASSWORD} env if appropriate.
+     * Sets the {@code CLOUDSOFT_ROOT_PASSWORD} variable in the container environment if appropriate.
      * This is (approximately) the same behaviour as the {@link DockerJcloudsLocation} used for 
      * Swarm.
      * 
-     * Side-effects the {@code config}, to set the loginPassword if one is auto-generated.
-     * 
-     * TODO Longer term, we'll add a config key to the `DockerContainer` entity for env variables.
-     * We'll inject those when launching the container (and will do the same in 
-     * `DockerJcloudsLocation` for Swarm). What would the precedence be? Would we prefer the
-     * entity's config over the location's (or vice versa)? I think the entity's would take
-     * precedence, as a location often applies to a whole app so we might well want to override
-     * or augment it for specific entities.
+     * Side-effects the location {@code config} to set the {@link KubernetesLocationConfig#LOGIN_USER_PASSWORD loginUser.password}
+     * if one is auto-generated. Note that this injected value overrides any other settings configured for the
+     * container environment.
      */
-    protected Map<String, String> findEnvironmentVariables(Entity entity, ConfigBag config, String imageName) {
-        String loginUser = config.get(LOGIN_USER);
-        String loginPassword = config.get(LOGIN_USER_PASSWORD);
+    protected Map<String, String> findEnvironmentVariables(Entity entity, ConfigBag setup, String imageName) {
+        String loginUser = setup.get(LOGIN_USER);
+        String loginPassword = setup.get(LOGIN_USER_PASSWORD);
         Map<String, String> injections = Maps.newLinkedHashMap();
 
         // Check if login credentials should be injected
-        Boolean injectLoginCredentials = config.get(INJECT_LOGIN_CREDENTIAL);
+        Boolean injectLoginCredentials = setup.get(INJECT_LOGIN_CREDENTIAL);
         if (injectLoginCredentials == null) {
             for (String regex : IMAGE_DESCRIPTION_REGEXES_REQUIRING_INJECTED_LOGIN_CREDS) {
                 if (imageName != null && imageName.matches(regex)) {
@@ -856,11 +871,11 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         if (Boolean.TRUE.equals(injectLoginCredentials)) {
             if ((Strings.isBlank(loginUser) || "root".equals(loginUser))) {
                 loginUser = "root";
-                config.configure(LOGIN_USER, loginUser);
+                setup.configure(LOGIN_USER, loginUser);
 
                 if (Strings.isBlank(loginPassword)) {
                     loginPassword = Identifiers.makeRandomPassword(12);
-                    config.configure(LOGIN_USER_PASSWORD, loginPassword);
+                    setup.configure(LOGIN_USER_PASSWORD, loginPassword);
                 }
 
                 injections.put("CLOUDSOFT_ROOT_PASSWORD", loginPassword);
@@ -868,9 +883,9 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
         }
 
         Map<String,Object> rawEnv = MutableMap.<String, Object>builder()
-                .putAll(injections)
-                .putAll(MutableMap.copyOf(config.get(ENV)))
+                .putAll(MutableMap.copyOf(setup.get(ENV)))
                 .putAll(MutableMap.copyOf(entity.config().get(DockerContainer.CONTAINER_ENVIRONMENT)))
+                .putAll(injections)
                 .build();
         return Maps.transformValues(rawEnv, Functions.toStringFunction());
     }
@@ -918,16 +933,40 @@ public class KubernetesLocation extends AbstractLocation implements MachineProvi
     }
 
     protected boolean isDockerContainer(Entity entity) {
-        return entity.getEntityType().getName().equalsIgnoreCase(DockerContainer.class.getName());
+        return implementsInterface(entity, DockerContainer.class);
+    }
+
+    protected boolean isKubernetesPod(Entity entity) {
+        return implementsInterface(entity, KubernetesPod.class);
     }
 
     protected boolean isKubernetesResource(Entity entity) {
-        return entity.getEntityType().getName().equalsIgnoreCase(KubernetesResource.class.getName());
+        return implementsInterface(entity, KubernetesResource.class);
+    }
+
+    protected boolean implementsInterface(Entity entity, Class<?> type) {
+        return Iterables.tryFind(Arrays.asList(entity.getClass().getInterfaces()), Predicates.instanceOf(type)).isPresent();
     }
 
     @Override
     public MachineProvisioningLocation<MachineLocation> newSubLocation(Map<?, ?> newFlags) {
-        return null;
+        throw new UnsupportedOperationException();
+    }
+
+    /** @see {@link #lookup(ConfigKey, Entity, ConfigBag, Object)} */
+    protected <T> T lookup(ConfigKey<T> config, Entity entity, ConfigBag setup) {
+        return lookup(config, entity, setup, null);
+    }
+
+    /**
+     * Looks up {@link ConfigKey configuration} with the entity value taking precedence over the
+     * location, and returning a default value (normally {@literal null}) if neither is present.
+     */
+    protected <T> T lookup(ConfigKey<T> config, Entity entity, ConfigBag setup, T defaultValue) {
+        Optional<T> entityValue = Optional.fromNullable(entity.config().get(config));
+        Optional<T> locationValue = Optional.fromNullable(setup.get(config));
+
+        return entityValue.or(locationValue).or(defaultValue);
     }
 
     protected void waitForExitCondition(ExitCondition exitCondition) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/422d78da/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java
----------------------------------------------------------------------
diff --git a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java
index c2e9d89..fdb658b 100644
--- a/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java
+++ b/kubernetes-location/src/main/java/io/cloudsoft/amp/containerservice/kubernetes/location/KubernetesLocationConfig.java
@@ -1,23 +1,21 @@
 package io.cloudsoft.amp.containerservice.kubernetes.location;
 
-import java.util.List;
 import java.util.Map;
 
-import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.location.LocationConfigKeys;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.core.flags.SetFromFlag;
 import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.reflect.TypeToken;
 
-public interface KubernetesLocationConfig {
+public interface KubernetesLocationConfig extends CloudLocationConfig {
 
-    @SetFromFlag("endpoint")
     ConfigKey<String> MASTER_URL = LocationConfigKeys.CLOUD_ENDPOINT;
 
     ConfigKey<String> CA_CERT_DATA = ConfigKeys.builder(String.class)
@@ -110,16 +108,11 @@ public interface KubernetesLocationConfig {
             .build();
 
     @SuppressWarnings("serial")
-    ConfigKey<List<String>> PERSISTENT_VOLUMES = ConfigKeys.builder(new TypeToken<List<String>>() {})
-            .name("persistentVolumes")
-            .description("Set up persistent volumes.")
-            .constraint(Predicates.<List<String>>notNull())
-            .build();
-
-    ConfigKey<String> DEPLOYMENT = ConfigKeys.builder(String.class)
-            .name("deployment")
-            .description("Deployment where resources will live.")
-            .constraint(Predicates.<String>notNull())
+    ConfigKey<Map<String, ?>> ENV = ConfigKeys.builder(new TypeToken<Map<String, ?>>() {})
+            .name("env")
+            .description("Environment variables to inject when starting the container")
+            .defaultValue(ImmutableMap.<String, Object>of())
+            .constraint(Predicates.<Map<String, ?>>notNull())
             .build();
 
     ConfigKey<String> IMAGE = ConfigKeys.builder(String.class)
@@ -138,41 +131,7 @@ public interface KubernetesLocationConfig {
             .description("Regular expression for the OS version to load")
             .build();
 
-    @SuppressWarnings("serial")
-    ConfigKey<Map<String, ?>> ENV = ConfigKeys.newConfigKey(
-            new TypeToken<Map<String, ?>>() {},
-            "env",
-            "Environment variables to inject when starting the container",
-            ImmutableMap.<String, Object>of());
-
-    ConfigKey<Integer> REPLICAS = ConfigKeys.builder(Integer.class)
-            .name("replicas")
-            .description("Number of replicas of the pod")
-            .constraint(Predicates.notNull())
-            .defaultValue(1)
-            .build();
-
-    @SuppressWarnings("serial")
-    ConfigKey<Map<String, String>> SECRETS = ConfigKeys.builder(
-            new TypeToken<Map<String, String>>() {})
-            .name("secrets")
-            .description("Kubernetes secrets to be added to the pod")
-            .build();
-
-    @SuppressWarnings("serial")
-    ConfigKey<Map<String, String>> LIMITS = ConfigKeys.builder(
-            new TypeToken<Map<String, String>>() {})
-            .name("limits")
-            .description("Kubernetes resource limits")
-            .build();
-
-    ConfigKey<Boolean> PRIVILEGED = ConfigKeys.builder(Boolean.class)
-            .name("privileged")
-            .description("Whether Kubernetes should allow privileged containers")
-            .defaultValue(false)
-            .build();
-
-    ConfigKey<KubernetesClientRegistry> KUBERNETES_CLIENT_REGISTRY = ConfigKeys.builder(KubernetesClientRegistry.class)
+    ConfigKey<KubernetesClientRegistry> KUBERNETES_CLIENT_REGISTRY = ConfigKeys.builder(KubernetesClientRegistry.class) 
             .name("kubernetesClientRegistry")
             .description("Registry/Factory for creating Kubernetes client; default is almost always fine, "
                     + "except where tests want to customize behaviour")
@@ -197,21 +156,5 @@ public interface KubernetesLocationConfig {
             .description("Whether to inject login credentials (if null, will infer from image choice); ignored if explicit 'loginUser.password' supplied")
             .build();
 
-    AttributeSensor<String> KUBERNETES_DEPLOYMENT = Sensors.builder(String.class, "kubernetes.deployment")
-            .description("Deployment resources run in")
-            .build();
-
-    AttributeSensor<String> KUBERNETES_NAMESPACE = Sensors.builder(String.class, "kubernetes.namespace")
-            .description("Namespace that resources run in")
-            .build();
-
-    AttributeSensor<String> KUBERNETES_SERVICE = Sensors.builder(String.class, "kubernetes.service")
-            .description("Service that exposes the deployment")
-            .build();
-
-    AttributeSensor<String> KUBERNETES_POD = Sensors.builder(String.class, "kubernetes.pod")
-            .description("Pod running the deployment")
-            .build();
-
 }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/422d78da/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 962be36..d623efa 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
@@ -30,7 +30,6 @@ 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.net.Networking;
 import org.apache.brooklyn.util.text.Identifiers;
@@ -43,6 +42,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.net.HostAndPort;
 
+import io.cloudsoft.amp.containerservice.dockercontainer.DockerContainer;
 import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesPod;
 import io.cloudsoft.amp.containerservice.kubernetes.entity.KubernetesResource;
 
@@ -82,10 +82,11 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
         String yaml = Joiner.on("\n").join(
                 locationYaml,
                 "services:",
-                "- type: " + EmptySoftwareProcess.class.getName(),
-                "  brooklyn.config:",
-                "    provisioning.properties:",
-                "      " + KubernetesLocationConfig.LOGIN_USER_PASSWORD.getName() + ": " + customPassword);
+                "  - 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));
 
@@ -101,19 +102,17 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
         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",
-                "    checkRunning.command: |",
-                "      true",
+                "  - 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\")",
@@ -122,6 +121,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "    brooklyn.config:",
                 "      " + OnPublicNetworkEnricher.SENSORS.getName() + ":",
                 "      - netcat.port");
+
         Entity app = createStartWaitAndLogApplication(yaml);
         VanillaSoftwareProcess entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, VanillaSoftwareProcess.class));
 
@@ -139,29 +139,26 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
         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",
-                "    checkRunning.command: |",
-                "      true",
-                "- type: " + VanillaSoftwareProcess.class.getName(),
-                "  name: server2",
-                "  brooklyn.config:",
-                "    install.command: |",
-                "      yum install -y nc",
-                "    launch.command: |",
-                "      true",
-                "    checkRunning.command: |",
-                "      true");
+                "  - 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);
 
@@ -191,16 +188,37 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
     }
 
     @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);
+    }
+
+    @Test(groups={"Live"})
     public void testTomcatContainer() throws Exception {
         String yaml = Joiner.on("\n").join(
                 locationYaml,
                 "services:",
-                "- type: " + KubernetesPod.class.getName(),
-                "  brooklyn.config:",
-                "    docker.container.imageName: tomcat",
-                "    docker.container.inboundPorts: [ \"8080\" ]");
+                "  - type: " + DockerContainer.class.getName(),
+                "    brooklyn.config:",
+                "      docker.container.imageName: tomcat",
+                "      docker.container.inboundPorts: [ \"8080\" ]");
+
+        runTomcat(yaml);
+    }
+
+    /**
+     * Assumes that the {@link DockerContainer} entity uses port 8080.
+     */
+    protected void runTomcat(String yaml) throws Exception {
         Entity app = createStartWaitAndLogApplication(yaml);
-        KubernetesPod entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, KubernetesPod.class));
+        DockerContainer entity = Iterables.getOnlyElement(Entities.descendantsAndSelf(app, DockerContainer.class));
 
         Entities.dumpInfo(app);
         String publicMapped = assertAttributeEventuallyNonNull(entity, Sensors.newStringSensor("docker.port.8080.mapped.public"));
@@ -220,7 +238,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "services:",
                 "  - type: " + KubernetesPod.class.getName(),
                 "    brooklyn.children:",
-                "      - type: " + KubernetesPod.class.getName(),
+                "      - type: " + DockerContainer.class.getName(),
                 "        id: wordpress-mysql",
                 "        name: mysql",
                 "        brooklyn.config:",
@@ -231,7 +249,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "            MYSQL_ROOT_PASSWORD: \"password\"",
                 "          provisioning.properties:",
                 "            deployment: wordpress-mysql-" + randomId,
-                "      - type: " + KubernetesPod.class.getName(),
+                "      - type: " + DockerContainer.class.getName(),
                 "        id: wordpress",
                 "        name: wordpress",
                 "        brooklyn.config:",
@@ -239,16 +257,16 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "          docker.container.inboundPorts:",
                 "            - \"80\"",
                 "          docker.container.environment:",
-                "            WORDPRESS_DB_HOST: \"wordpress-mysql\"",
+                "            WORDPRESS_DB_HOST: \"wordpress-mysql" + randomId + "\"",
                 "            WORDPRESS_DB_PASSWORD: \"password\"",
                 "          provisioning.properties:",
                 "            deployment: wordpress-" + randomId);
 
-        runWordpress(yaml);
+        runWordpress(yaml, randomId);
     }
 
     @Test(groups={"Live"})
-    public void testWordpressInSeperatePods() throws Exception {
+    public void testWordpressInPods() throws Exception {
         // TODO docker.container.inboundPorts doesn't accept list of ints - need to use quotes
         String randomId = Identifiers.makeRandomId(4);
         String yaml = Joiner.on("\n").join(
@@ -263,6 +281,8 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "        - \"3306\"",
                 "      docker.container.environment:",
                 "        MYSQL_ROOT_PASSWORD: \"password\"",
+                "      deployment: wordpress-mysql-" + randomId,
+                "      pod: wordpress-mysql-pod-" + randomId,
                 "  - type: " + KubernetesPod.class.getName(),
                 "    id: wordpress",
                 "    name: wordpress",
@@ -271,31 +291,35 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "      docker.container.inboundPorts:",
                 "        - \"80\"",
                 "      docker.container.environment:",
-                "        WORDPRESS_DB_HOST: \"wordpress-mysql\"",
+                "        WORDPRESS_DB_HOST: \"wordpress-mysql" + randomId + "\"",
                 "        WORDPRESS_DB_PASSWORD: \"password\"",
-                "      provisioning.properties:",
-                "        deployment: wordpress-" + randomId);
+                "      deployment: wordpress-" + randomId);
 
-        runWordpress(yaml);
+        runWordpress(yaml, randomId);
     }
 
     /**
-     * Assumes that the {@link KubernetesPod} entities have display names of "mysql" and "wordpress",
+     * 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) throws Exception {
+    protected void runWordpress(String yaml, String randomId) throws Exception {
         Entity app = createStartWaitAndLogApplication(yaml);
         Entities.dumpInfo(app);
 
-        Iterable<KubernetesPod> containers = Entities.descendantsAndSelf(app, KubernetesPod.class);
-        KubernetesPod mysql = Iterables.find(containers, EntityPredicates.displayNameEqualTo("mysql"));
-        KubernetesPod wordpress = Iterables.find(containers, EntityPredicates.displayNameEqualTo("wordpress"));
+        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));
+        assertAttributeEqualsEventually(mysql, KubernetesPod.KUBERNETES_POD, "wordpress-mysql-pod-" + randomId);
+        assertAttributeEqualsEventually(mysql, KubernetesPod.KUBERNETES_NAMESPACE, "amp");
+        assertAttributeEqualsEventually(mysql, KubernetesPod.KUBERNETES_SERVICE, "wordpress-mysql-" + randomId);
 
         String wordpressPublicPort = assertAttributeEventuallyNonNull(wordpress, Sensors.newStringSensor("docker.port.80.mapped.public"));
         assertReachableEventually(HostAndPort.fromString(wordpressPublicPort));
+        assertAttributeEqualsEventually(wordpress, KubernetesPod.KUBERNETES_NAMESPACE, "amp");
+        assertAttributeEqualsEventually(wordpress, KubernetesPod.KUBERNETES_SERVICE, "wordpress-" + randomId);
 
         // TODO more assertions (e.g. wordpress can successfully reach the database)
     }
@@ -309,7 +333,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
                 "    brooklyn.config:",
                 "      docker.container.imageName: tomcat",
                 "      docker.container.inboundPorts:",
-                "      - \"8080\"",
+                "        - \"8080\"",
                 "      shell.env:",
                 "        CLUSTER_ID: \"id\"",
                 "        CLUSTER_TOKEN: \"token\"");
@@ -343,7 +367,7 @@ public class KubernetesLocationYamlLiveTest extends AbstractYamlTest {
         assertEntityHealthy(entity);
         assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_NAME, "nginx-replication-controller");
         assertAttributeEqualsEventually(entity, KubernetesResource.RESOURCE_TYPE, "ReplicationController");
-        assertAttributeEqualsEventually(entity, KubernetesLocationConfig.KUBERNETES_NAMESPACE, "default");
+        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"))));
     }