You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2020/07/08 13:18:13 UTC

[brooklyn-server] 03/20: Add kube sensors

This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 0bbafedf754d4c1acf812679bdc9bfdaa3e4419c
Author: Duncan Grant <du...@cloudsoft.io>
AuthorDate: Thu Jun 18 08:47:18 2020 +0100

    Add kube sensors
---
 .../brooklyn/container/entity/helm/HelmDriver.java |  2 +
 .../brooklyn/container/entity/helm/HelmEntity.java |  3 ++
 .../container/entity/helm/HelmEntityImpl.java      | 33 +++++++++----
 .../container/entity/helm/HelmSshDriver.java       | 47 +++++++++++++++++-
 .../container/entity/helm/HelmEntityLiveTest.java  | 55 ++++++++++++++++++----
 5 files changed, 121 insertions(+), 19 deletions(-)

diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmDriver.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmDriver.java
index 52dbbc8..4224f7a 100644
--- a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmDriver.java
+++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmDriver.java
@@ -22,4 +22,6 @@ import java.util.concurrent.Callable;
 
 public interface HelmDriver extends SoftwareProcessDriver {
     Callable getCallable(String command);
+
+    Callable getKubeCallable();
 }
diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntity.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntity.java
index e34499f..0daa865 100644
--- a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntity.java
+++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntity.java
@@ -46,4 +46,7 @@ public interface HelmEntity extends SoftwareProcess {
 
    AttributeSensor<String> STATUS = Sensors.newStringSensor("helm.status",
            "The results of a status call");
+
+   AttributeSensor<Boolean> DEPLOYMENT_READY = Sensors.newBooleanSensor("kube.deployment.status",
+           "The status of the deploymeny");
 }
diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntityImpl.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntityImpl.java
index 72ed9f5..451d4c1 100644
--- a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntityImpl.java
+++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmEntityImpl.java
@@ -18,16 +18,12 @@
  */
 package org.apache.brooklyn.container.entity.helm;
 
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.entity.brooklynnode.BrooklynClusterImpl;
-import org.apache.brooklyn.entity.brooklynnode.BrooklynNode;
-import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
 import org.apache.brooklyn.feed.function.FunctionFeed;
 import org.apache.brooklyn.feed.function.FunctionPollConfig;
 import org.apache.brooklyn.util.time.Duration;
 
-import javax.annotation.Nullable;
 import java.util.concurrent.Callable;
 
 public class HelmEntityImpl extends SoftwareProcessImpl implements HelmEntity {
@@ -46,10 +42,30 @@ public class HelmEntityImpl extends SoftwareProcessImpl implements HelmEntity {
         super.connectSensors();
         connectServiceUpIsRunning();
 
+        addHelmFeed("status", STATUS);
+        addKubernetesFeed();
+    }
+
+    private void addKubernetesFeed() {
+        HelmDriver driver = getDriver();
+        Callable status = driver.getKubeCallable();
+        FunctionPollConfig pollConfig = new FunctionPollConfig<String, Boolean>(DEPLOYMENT_READY)
+                .callable(status)
+                ;
+
+        addFeed(FunctionFeed.builder()
+                .entity(this)
+                .poll(pollConfig)
+                .period(Duration.FIVE_SECONDS)
+                .build());
+    }
+
+    private void addHelmFeed(String command, AttributeSensor<String> sensor) {
         HelmDriver driver = getDriver();
-        Callable status = driver.getCallable("status");
-        FunctionPollConfig pollConfig = new FunctionPollConfig<Object, String>(STATUS)
-                .callable(status);
+        Callable status = driver.getCallable(command);
+        FunctionPollConfig pollConfig = new FunctionPollConfig<String, String>(sensor)
+                .callable(status)
+                ;
 
         addFeed(FunctionFeed.builder()
                 .entity(this)
@@ -58,6 +74,7 @@ public class HelmEntityImpl extends SoftwareProcessImpl implements HelmEntity {
                 .build());
     }
 
+
     @Override
     protected void disconnectSensors() {
         super.disconnectSensors();
diff --git a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmSshDriver.java b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmSshDriver.java
index c8c6728..931534f 100644
--- a/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmSshDriver.java
+++ b/locations/container/src/main/java/org/apache/brooklyn/container/entity/helm/HelmSshDriver.java
@@ -19,8 +19,16 @@
 package org.apache.brooklyn.container.entity.helm;
 
 import com.google.common.collect.ImmutableList;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.DoneableDeployment;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.container.location.kubernetes.KubernetesLocation;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver;
 import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessDriver;
@@ -28,10 +36,15 @@ import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
 import org.apache.brooklyn.util.core.internal.ssh.process.ProcessTool;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Map;
 import java.util.concurrent.Callable;
 
@@ -149,8 +162,40 @@ public class HelmSshDriver extends AbstractSoftwareProcessDriver implements Helm
                         ImmutableList.<String>of(String.format("helm %s %s", command, helm_name_install_name));
                 OutputStream out = new ByteArrayOutputStream();
                 OutputStream err = new ByteArrayOutputStream();
-                return ProcessTool.execProcesses(installHelmTemplateCommand, null, null, out, err,";", false, this);
+                ProcessTool.execProcesses(installHelmTemplateCommand, null, null, out, err,";", false, this);
+                return out.toString();
             }
         };
     }
+
+    @Override
+    public Callable getKubeCallable() {
+        return new Callable() {
+            @Override
+            public Object call() throws Exception {
+                String helm_name_install_name = getEntity().getConfig(HelmEntity.HELM_TEMPLATE_INSTALL_NAME);
+                String config = getLocation().getConfig(KubernetesLocation.KUBECONFIG);
+                KubernetesClient client = getClient(config);
+
+                Deployment deploy = client.apps().deployments().inNamespace("default").withName(helm_name_install_name).get();
+                Integer availableReplicas = deploy.getStatus().getAvailableReplicas();
+                Integer replicas = deploy.getStatus().getReplicas();
+                Boolean ready = availableReplicas.equals(replicas);
+                return ready;
+            } ;
+        };
+    }
+
+    KubernetesClient getClient(String configFile) {
+        Path configPath = Paths.get(configFile);
+        try {
+            Config clientConfig = Config.fromKubeconfig(new String(Files.readAllBytes(configPath)));
+            ConfigBuilder configBuilder = new ConfigBuilder(clientConfig);
+            return new DefaultKubernetesClient(configBuilder.build());
+        }catch (IOException ioe) {
+            Exceptions.propagate(ioe);
+            return null;
+        }
+    }
+
 }
diff --git a/locations/container/src/test/java/org/apache/brooklyn/container/entity/helm/HelmEntityLiveTest.java b/locations/container/src/test/java/org/apache/brooklyn/container/entity/helm/HelmEntityLiveTest.java
index ffd021b..7d9a9b8 100644
--- a/locations/container/src/test/java/org/apache/brooklyn/container/entity/helm/HelmEntityLiveTest.java
+++ b/locations/container/src/test/java/org/apache/brooklyn/container/entity/helm/HelmEntityLiveTest.java
@@ -18,7 +18,9 @@
  */
 package org.apache.brooklyn.container.entity.helm;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.container.location.kubernetes.KubernetesLocation;
@@ -26,11 +28,14 @@ import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.time.Duration;
 import org.testng.annotations.Test;
 
+import javax.annotation.Nullable;
 import java.util.Map;
 
 import static org.apache.brooklyn.core.entity.EntityAsserts.assertAttributeEqualsEventually;
+import static org.apache.brooklyn.core.entity.EntityAsserts.assertPredicateEventuallyTrue;
 import static org.testng.Assert.*;
 
 public class HelmEntityLiveTest extends BrooklynAppLiveTestSupport {
@@ -43,20 +48,50 @@ public class HelmEntityLiveTest extends BrooklynAppLiveTestSupport {
                 .configure(HelmEntity.HELM_TEMPLATE_INSTALL_NAME, "wordpress-test")
                 .configure(HelmEntity.HELM_TEMPLATE, "bitnami/wordpress"));
 
-        app.start(ImmutableList.<Location>of(app.newLocalhostProvisioningLocation()));
+        app.start(newLocalhostLocation());
 
         assertAttributeEqualsEventually(andManageChild, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        assertAttributeEqualsEventually(andManageChild, Attributes.SERVICE_UP, true);
         app.stop();
     }
 
-    protected KubernetesLocation newKubernetesLocation(Map<String, ?> flags) throws Exception {
-        Map<String, ?> allFlags = MutableMap.<String, Object>builder()
-                .put("kubeconfig", "/Users/duncangrant/.kube/config")
-                .put("image", "cloudsoft/centos:7")
-                .put("loginUser", "root")
-                .put("loginUser.password", "p4ssw0rd")
-                .putAll(flags)
-                .build();
-        return (KubernetesLocation) mgmt.getLocationRegistry().getLocationManaged("kubernetes", allFlags);
+    @Test
+    public void testCanSenseHelmStatus() {
+        HelmEntity andManageChild = app.createAndManageChild(EntitySpec.create(HelmEntity.class)
+                .configure(HelmEntity.REPO_NAME, "bitnami")
+                .configure(HelmEntity.REPO_URL, "https://charts.bitnami.com/bitnami")
+                .configure(HelmEntity.HELM_TEMPLATE_INSTALL_NAME, "wordpress-test")
+                .configure(HelmEntity.HELM_TEMPLATE, "bitnami/wordpress"));
+
+        app.start(newLocalhostLocation());
+
+        assertPredicateEventuallyTrue(andManageChild, new Predicate<HelmEntity>() {
+            @Override
+            public boolean apply(@Nullable HelmEntity input) {
+                String status = input.getAttribute(HelmEntity.STATUS);
+                return status == null? false : status.contains("STATUS: deployed");
+            }
+        });
+        app.stop();
+    }
+
+    @Test
+    public void testCanSenseDeploymentStatus() {
+        HelmEntity andManageChild = app.createAndManageChild(EntitySpec.create(HelmEntity.class)
+                .configure(HelmEntity.REPO_NAME, "bitnami")
+                .configure(HelmEntity.REPO_URL, "https://charts.bitnami.com/bitnami")
+                .configure(HelmEntity.HELM_TEMPLATE_INSTALL_NAME, "nginx-test")
+                .configure(HelmEntity.HELM_TEMPLATE, "bitnami/nginx"));
+
+        app.start(newLocalhostLocation());
+
+        assertAttributeEqualsEventually(andManageChild, HelmEntity.DEPLOYMENT_READY, true);
+        app.stop();
+    }
+
+    private ImmutableList<Location> newLocalhostLocation() {
+        return ImmutableList.<Location>of(
+                app.newLocalhostProvisioningLocation(
+                        ImmutableMap.of(KubernetesLocation.KUBECONFIG, "/Users/duncangrant/.kube/config")));
     }
 }
\ No newline at end of file