Add support for health checks to the Service Configuration API.
authorDavid Bosschaert <davidb@apache.org>
Thu, 15 Jun 2017 13:23:24 +0000 (14:23 +0100)
committerDavid Bosschaert <davidb@apache.org>
Thu, 15 Jun 2017 13:23:24 +0000 (14:23 +0100)
containers-api/src/main/java/org/apache/aries/containers/HealthCheck.java [new file with mode: 0644]
containers-api/src/main/java/org/apache/aries/containers/ServiceConfig.java
containers-api/src/test/java/org/apache/aries/containers/api/ServiceConfigTest.java

diff --git a/containers-api/src/main/java/org/apache/aries/containers/HealthCheck.java b/containers-api/src/main/java/org/apache/aries/containers/HealthCheck.java
new file mode 100644 (file)
index 0000000..5d492f8
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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.aries.containers;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Define a health check. Most container system support health checks of some
+ * sort. Health checks are provided to a Service Manager via the Service Configuration.
+ *
+ * @see ServiceConfig
+ */
+@ProviderType
+public class HealthCheck {
+    /**
+     * Supported health check types, The parameters for health checks are
+     * specified in the parameters member.
+     */
+    public enum Type {
+        /**
+         * Health check defined as a HTTP request. The request should return
+         * a return code between 200 and 399. {@link HealthCheck#getParameters()}
+         * provides the URL for the HTTP request.
+         */
+        HTTP,
+
+        /**
+         * Health check defined as a HTTPS request. The request should return
+         * a return code between 200 and 399. {@link HealthCheck#getParameters()}
+         * provides the URL for the HTTP request.
+         */
+        HTTPS,
+
+        /**
+         * Health check defined as a TCP port connection. If opening a TCP port to
+         * succeeds the health check passes. The port to connect to is specified
+         * via {@link HealthCheck#getParameters()}. An port index is specified via
+         * {@code $PORT0}, {@code $PORT1} etc. An actual port is specified as a number,
+         * e.g. {@code 8080}.
+         */
+        TCP,
+
+        /**
+         * Health check defined as a command to be executed locally in the container to
+         * determine that the container is healthy. The command should have return {@code 0}
+         * to indicate that its healthy. The actual command to execute is
+         * available from {@link HealthCheck#getParameters()}.
+         */
+        COMMAND,
+
+        /**
+         * This type of health check can be used for proprietary health checks that are not
+         * covered by other types.
+         */
+        OTHER };
+
+    private final String parameters;
+    private final Type type;
+    private final int gracePeriod;
+    private final int interval;
+    private final int timeout;
+    private final int maxFailures;
+
+    private HealthCheck(String parameters, Type type, int gracePeriod, int interval,
+            int timeout, int maxFailures) {
+        this.parameters = parameters;
+        this.type = type;
+        this.gracePeriod = gracePeriod;
+        this.interval = interval;
+        this.timeout = timeout;
+        this.maxFailures = maxFailures;
+    }
+
+    /**
+     * @return The health check type.
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * @return The grace period in seconds. Health checks are ignored for the duration of the grace
+     * period until the container is healthy.
+     */
+    public int getGracePeriod() {
+        return gracePeriod;
+    }
+
+    /**
+     * @return The interval at which health checks are evaluated, specified in seconds.
+     */
+    public int getInterval() {
+        return interval;
+    }
+
+    /**
+     * @return The number of failed health check after which a task will be killed.
+     */
+    public int getMaxFailures() {
+        return maxFailures;
+    }
+
+    /**
+     * @return The parameters for a given health check, such as the HTTP URL or command line.
+     */
+    public String getParameters() {
+        return parameters;
+    }
+
+    /**
+     * @return The number of seconds after which a health check is considered a failure, regardless
+     * of the obtained result.
+     */
+    public int getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
+        result = prime * result + gracePeriod;
+        result = prime * result + interval;
+        result = prime * result + maxFailures;
+        result = prime * result + timeout;
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        HealthCheck other = (HealthCheck) obj;
+        if (parameters == null) {
+            if (other.parameters != null)
+                return false;
+        } else if (!parameters.equals(other.parameters))
+            return false;
+        if (gracePeriod != other.gracePeriod)
+            return false;
+        if (interval != other.interval)
+            return false;
+        if (maxFailures != other.maxFailures)
+            return false;
+        if (timeout != other.timeout)
+            return false;
+        if (type == null) {
+            if (other.type != null)
+                return false;
+        } else if (!type.equals(other.type))
+            return false;
+        return true;
+    }
+
+    /**
+     * Create a health check builder.
+     * @param type The type of health check.
+     * @return A health check builder.
+     */
+    public static Builder builder(Type type) {
+        return new Builder(type);
+    }
+
+    /**
+     * A builder for health checks.
+     */
+    public static class Builder {
+        private String parameters;
+        private final Type type;
+        private int gracePeriod = 300;
+        private int interval = 60;
+        private int timeout = 20;
+        private int maxFailures = 3;
+
+        Builder(Type type) {
+            this.type = type;
+        }
+
+        /**
+         * Specify the parameters for the health check, such as the HTTP URL.
+         * @param parameters The parameters for this health check.
+         * @return the current builder for further building.
+         */
+        public Builder parameters(String parameters) {
+            this.parameters = parameters;
+            return this;
+        }
+
+        /**
+         * Specify the grace period to use.
+         * @param gp The grace period in seconds.
+         * @return the current builder for further building.
+         */
+        public Builder gracePeriod(int gp) {
+            this.gracePeriod = gp;
+            return this;
+        }
+
+        /**
+         * Specify the interval to use.
+         * @param iv The interval in seconds.
+         * @return the current builder for further building.
+         */
+        public Builder interval(int iv) {
+            this.interval = iv;
+            return this;
+        }
+
+        /**
+         * Specify the maximum number of failures.
+         * @param max The maximum number of failures.
+         * @return the current builder for further building.
+         */
+        public Builder maxFailures(int max) {
+            this.maxFailures = max;
+            return this;
+        }
+
+        /**
+         * Specify the timeout.
+         * @param t The timout in seconds.
+         * @return the current builder for further building.
+         */
+        public Builder timeout(int t) {
+            this.timeout = t;
+            return this;
+        }
+
+        public HealthCheck build() {
+            return new HealthCheck(parameters, type, gracePeriod, interval, timeout, maxFailures);
+        }
+    }
+}
index bf48d57..85ca8f0 100644 (file)
@@ -43,20 +43,21 @@ public class ServiceConfig {
     private final List<Integer> containerPorts;
     private final String entryPoint;
     private final Map<String, String> envVars;
-//    private final List<HealthCheck> healthChecks; TODO add these!
+    private final List<HealthCheck> healthChecks;
     private final double requestedCPUunits;
     private final int requestedInstances;
     private final double requestedMemory; // in MiB
     private final String serviceName;
 
     private ServiceConfig(String[] commandLine, String containerImage, List<Integer> containerPorts, String entryPoint,
-            Map<String, String> envVars, double requestedCPUunits, int requestedInstances, double requestedMemory,
-            String serviceName) {
+            Map<String, String> envVars, List<HealthCheck> healtChecks, double requestedCPUunits, int requestedInstances,
+            double requestedMemory, String serviceName) {
         this.commandLine = commandLine;
         this.containerImage = containerImage;
         this.containerPorts = Collections.unmodifiableList(containerPorts);
         this.entryPoint = entryPoint;
         this.envVars = Collections.unmodifiableMap(envVars);
+        this.healthChecks = Collections.unmodifiableList(healtChecks);
         this.requestedCPUunits = requestedCPUunits;
         this.requestedInstances = requestedInstances;
         this.requestedMemory = requestedMemory;
@@ -93,6 +94,13 @@ public class ServiceConfig {
     }
 
     /**
+     * @return The health checks to be configured for this service.
+     */
+    public List<HealthCheck> getHealthChecks() {
+        return healthChecks;
+    }
+
+    /**
      * @return A map containing all the environment variables to be set.
      */
     public Map<String, String> getEnvVars() {
@@ -129,8 +137,6 @@ public class ServiceConfig {
         return serviceName;
     }
 
-
-
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -140,6 +146,7 @@ public class ServiceConfig {
         result = prime * result + ((containerPorts == null) ? 0 : containerPorts.hashCode());
         result = prime * result + ((entryPoint == null) ? 0 : entryPoint.hashCode());
         result = prime * result + ((envVars == null) ? 0 : envVars.hashCode());
+        result = prime * result + ((healthChecks == null) ? 0 : healthChecks.hashCode());
         long temp;
         temp = Double.doubleToLongBits(requestedCPUunits);
         result = prime * result + (int) (temp ^ (temp >>> 32));
@@ -181,6 +188,11 @@ public class ServiceConfig {
                 return false;
         } else if (!envVars.equals(other.envVars))
             return false;
+        if (healthChecks == null) {
+            if (other.healthChecks != null)
+                return false;
+        } else if (!healthChecks.equals(other.healthChecks))
+            return false;
         if (Double.doubleToLongBits(requestedCPUunits) != Double.doubleToLongBits(other.requestedCPUunits))
             return false;
         if (requestedInstances != other.requestedInstances)
@@ -210,15 +222,16 @@ public class ServiceConfig {
     /** A builder for service configurations */
     @ProviderType
     public static class Builder {
-        private String containerImage;
+        private final String containerImage;
         private String[] commandLine = new String [] {};
         private Map<String, String> envMap = new HashMap<>();
         private String entryPoint;
+        private List<HealthCheck> healthChecks = new ArrayList<>();
         private double requestedCpuUnits = 0.5;
         private int requestedInstances = 1;
         private double requestedMemory = 64;
         private List<Integer> ports = new ArrayList<>();
-        private String serviceName;
+        private final String serviceName;
 
         Builder(String serviceName, String containerImage) {
             this.serviceName = serviceName;
@@ -299,6 +312,18 @@ public class ServiceConfig {
         }
 
         /**
+         * Specify a health check to use for the service. This method
+         * may be called multiple times to specify multiple health
+         * checks.
+         * @param hc The health to add.
+         * @return the current builder for further building.
+         */
+        public Builder healthCheck(HealthCheck hc) {
+            this.healthChecks.add(hc);
+            return this;
+        }
+
+        /**
          * Specify the required amount of memory in million bytes (MiB).
          *
          * @param requestedMemory The amount of memory required of a container.
@@ -328,7 +353,7 @@ public class ServiceConfig {
          */
         public ServiceConfig build() {
             return new ServiceConfig(commandLine, containerImage, ports, entryPoint,
-                    envMap, requestedCpuUnits, requestedInstances, requestedMemory,
+                    envMap, healthChecks, requestedCpuUnits, requestedInstances, requestedMemory,
                     serviceName);
         }
     }
index c21519a..934ec4c 100644 (file)
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.aries.containers.HealthCheck;
 import org.apache.aries.containers.ServiceConfig;
 import org.junit.Test;
 
@@ -74,4 +75,14 @@ public class ServiceConfigTest {
         assertEquals(env, sc.getEnvVars());
     }
 
+    @Test
+    public void testHealthCheck() {
+        HealthCheck hc = HealthCheck.builder(HealthCheck.Type.HTTP).
+                parameters("/index.html").build();
+        ServiceConfig sc = ServiceConfig.builder("mysvc", "animg").
+                healthCheck(hc).build();
+
+        assertEquals(1, sc.getHealthChecks().size());
+        assertEquals(hc, sc.getHealthChecks().get(0));
+    }
 }