From f4b64fd00df41fc89d3d033a1109b6f3a431ee98 Mon Sep 17 00:00:00 2001
From: Everett Toews
Date: Mon, 21 Jan 2013 15:48:13 -0600
Subject: [PATCH] The Health Monitor API for Rackspace Cloud Load Balancers.
---
.../CloudLoadBalancersApi.java | 9 ++
.../CloudLoadBalancersAsyncApi.java | 13 +-
.../CloudLoadBalancersRestClientModule.java | 3 +
.../domain/HealthMonitor.java | 74 ++++++---
.../features/HealthMonitorApi.java | 54 +++++++
.../features/HealthMonitorAsyncApi.java | 82 ++++++++++
.../functions/ParseHealthMonitor.java | 65 ++++++++
.../features/ConnectionApiExpectTest.java | 16 +-
.../features/HealthMonitorApiExpectTest.java | 150 ++++++++++++++++++
.../features/HealthMonitorApiLiveTest.java | 88 ++++++++++
.../connectionthrottle-get-deleted.json | 4 +
.../test/resources/healthmonitor-create.json | 1 +
.../resources/healthmonitor-get-deleted.json | 4 +
.../src/test/resources/healthmonitor-get.json | 8 +
14 files changed, 546 insertions(+), 25 deletions(-)
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApi.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorAsyncApi.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseHealthMonitor.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApiExpectTest.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApiLiveTest.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/connectionthrottle-get-deleted.json
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/healthmonitor-create.json
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/healthmonitor-get-deleted.json
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/healthmonitor-get.json
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersApi.java
index 45e117e9ef..77c55b23b1 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersApi.java
@@ -27,6 +27,7 @@ import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.rackspace.cloudloadbalancers.features.AccessRuleApi;
import org.jclouds.rackspace.cloudloadbalancers.features.ConnectionApi;
+import org.jclouds.rackspace.cloudloadbalancers.features.HealthMonitorApi;
import org.jclouds.rackspace.cloudloadbalancers.features.LoadBalancerApi;
import org.jclouds.rackspace.cloudloadbalancers.features.NodeApi;
import org.jclouds.rackspace.cloudloadbalancers.features.VirtualIPApi;
@@ -88,4 +89,12 @@ public interface CloudLoadBalancersApi {
@Path("/loadbalancers/{lbId}")
ConnectionApi getConnectionApiForZoneAndLoadBalancer(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone, @PathParam("lbId") int lbId);
+
+ /**
+ * Provides synchronous access to Health Monitor features.
+ */
+ @Delegate
+ @Path("/loadbalancers/{lbId}")
+ HealthMonitorApi getHealthMonitorApiForZoneAndLoadBalancer(
+ @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone, @PathParam("lbId") int lbId);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersAsyncApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersAsyncApi.java
index 33db013de1..454dea7dfd 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersAsyncApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/CloudLoadBalancersAsyncApi.java
@@ -27,7 +27,8 @@ import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Zone;
import org.jclouds.location.functions.ZoneToEndpoint;
import org.jclouds.rackspace.cloudloadbalancers.features.AccessRuleAsyncApi;
-import org.jclouds.rackspace.cloudloadbalancers.features.ConnectionApi;
+import org.jclouds.rackspace.cloudloadbalancers.features.ConnectionAsyncApi;
+import org.jclouds.rackspace.cloudloadbalancers.features.HealthMonitorAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.LoadBalancerAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.NodeAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.VirtualIPAsyncApi;
@@ -87,6 +88,14 @@ public interface CloudLoadBalancersAsyncApi {
*/
@Delegate
@Path("/loadbalancers/{lbId}")
- ConnectionApi getConnectionApiForZoneAndLoadBalancer(
+ ConnectionAsyncApi getConnectionApiForZoneAndLoadBalancer(
+ @EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone, @PathParam("lbId") int lbId);
+
+ /**
+ * Provides synchronous access to Health Monitor features.
+ */
+ @Delegate
+ @Path("/loadbalancers/{lbId}")
+ HealthMonitorAsyncApi getHealthMonitorApiForZoneAndLoadBalancer(
@EndpointParam(parser = ZoneToEndpoint.class) @Nullable String zone, @PathParam("lbId") int lbId);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
index 435de2bc12..a1ed967403 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/config/CloudLoadBalancersRestClientModule.java
@@ -32,6 +32,8 @@ import org.jclouds.rackspace.cloudloadbalancers.features.AccessRuleApi;
import org.jclouds.rackspace.cloudloadbalancers.features.AccessRuleAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.ConnectionApi;
import org.jclouds.rackspace.cloudloadbalancers.features.ConnectionAsyncApi;
+import org.jclouds.rackspace.cloudloadbalancers.features.HealthMonitorApi;
+import org.jclouds.rackspace.cloudloadbalancers.features.HealthMonitorAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.LoadBalancerAsyncApi;
import org.jclouds.rackspace.cloudloadbalancers.features.LoadBalancerApi;
import org.jclouds.rackspace.cloudloadbalancers.features.NodeAsyncApi;
@@ -61,6 +63,7 @@ public class CloudLoadBalancersRestClientModule extends
.put(AccessRuleApi.class, AccessRuleAsyncApi.class)
.put(VirtualIPApi.class, VirtualIPAsyncApi.class)
.put(ConnectionApi.class, ConnectionAsyncApi.class)
+ .put(HealthMonitorApi.class, HealthMonitorAsyncApi.class)
.build();
public CloudLoadBalancersRestClientModule() {
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/HealthMonitor.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/HealthMonitor.java
index cd1e159dc4..8da0c3287c 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/HealthMonitor.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/HealthMonitor.java
@@ -20,8 +20,13 @@ package org.jclouds.rackspace.cloudloadbalancers.domain;
import static com.google.common.base.Preconditions.checkNotNull;
+import java.beans.ConstructorProperties;
+
+import org.jclouds.javax.annotation.Nullable;
+
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
/**
* The load balancing service includes a health monitoring operation which periodically checks your back-end nodes to
@@ -29,11 +34,11 @@ import com.google.common.base.Objects.ToStringHelper;
* monitor determines that the node is functional. In addition to being performed periodically, the health check also
* is performed against every node that is added to ensure that the node is operating properly before allowing it to
* service traffic. Only one health monitor is allowed to be enabled on a load balancer at a time.
- *
+ *
* As part of your strategy for monitoring connections, you should consider defining secondary nodes that provide
* failover for effectively routing traffic in case the primary node fails. This is an additional feature that will
* ensure you remain up in case your primary node fails.
- *
+ *
* @author Everett Toews
*/
public class HealthMonitor {
@@ -42,21 +47,31 @@ public class HealthMonitor {
private final int delay;
private final int timeout;
private final int attemptsBeforeDeactivation;
- private final String bodyRegex;
- private final String statusRegex;
- private final String path;
- private final String hostHeader;
+ private final Optional bodyRegex;
+ private final Optional statusRegex;
+ private final Optional path;
+ private final Optional hostHeader;
- protected HealthMonitor(Type type, int delay, int timeout, int attemptsBeforeDeactivation, String bodyRegex,
- String statusRegex, String path, String hostHeader) {
+ @ConstructorProperties({
+ "type", "delay", "timeout", "attemptsBeforeDeactivation", "bodyRegex", "statusRegex", "path", "hostHeader"
+ })
+ protected HealthMonitor(Type type, int delay, int timeout, int attemptsBeforeDeactivation,
+ @Nullable String bodyRegex, @Nullable String statusRegex, @Nullable String path,
+ @Nullable String hostHeader) {
this.type = checkNotNull(type, "type");
this.delay = delay;
this.timeout = timeout;
this.attemptsBeforeDeactivation = attemptsBeforeDeactivation;
- this.bodyRegex = bodyRegex;
- this.statusRegex = statusRegex;
- this.path = path;
- this.hostHeader = hostHeader;
+ this.bodyRegex = Optional.fromNullable(bodyRegex);
+ this.statusRegex = Optional.fromNullable(statusRegex);
+ this.path = Optional.fromNullable(path);
+ this.hostHeader = Optional.fromNullable(hostHeader);
+
+ if (!isValid())
+ if (type.equals(Type.CONNECT))
+ throw new IllegalArgumentException("Only delay, timeout, and attemptsBeforeDeactivation must be set.");
+ else
+ throw new IllegalArgumentException("At least delay, timeout, attemptsBeforeDeactivation, and path must be set.");
}
public Type getType() {
@@ -75,25 +90,38 @@ public class HealthMonitor {
return attemptsBeforeDeactivation;
}
- public String getBodyRegex() {
+ public Optional getBodyRegex() {
return bodyRegex;
}
- public String getStatusRegex() {
+ public Optional getStatusRegex() {
return statusRegex;
}
- public String getPath() {
+ public Optional getPath() {
return path;
}
- public String getHostHeader() {
+ public Optional getHostHeader() {
return hostHeader;
}
+
+ /**
+ * @return true if this HealthMonitor is valid, false otherwise
+ */
+ public boolean isValid() {
+ boolean required = delay != 0 && timeout != 0 && attemptsBeforeDeactivation != 0;
+
+ if (type.equals(Type.CONNECT))
+ return required && !path.isPresent() && !statusRegex.isPresent()
+ && !bodyRegex.isPresent() && !hostHeader.isPresent();
+ else
+ return required && path.isPresent();
+ }
@Override
public int hashCode() {
- return Objects.hashCode(type, delay, timeout, attemptsBeforeDeactivation, bodyRegex, statusRegex, path,
+ return Objects.hashCode(type, delay, timeout, attemptsBeforeDeactivation, bodyRegex, statusRegex, path,
hostHeader);
}
@@ -113,9 +141,10 @@ public class HealthMonitor {
}
protected ToStringHelper string() {
- return Objects.toStringHelper(this).add("type", type).add("delay", delay).add("timeout", timeout)
- .add("attemptsBeforeDeactivation", attemptsBeforeDeactivation).add("bodyRegex", bodyRegex)
- .add("statusRegex", statusRegex).add("path", path).add("hostHeader", hostHeader);
+ return Objects.toStringHelper(this).omitNullValues().add("type", type).add("delay", delay)
+ .add("timeout", timeout).add("attemptsBeforeDeactivation", attemptsBeforeDeactivation)
+ .add("bodyRegex", bodyRegex).add("statusRegex", statusRegex).add("path", path)
+ .add("hostHeader", hostHeader);
}
@Override
@@ -225,8 +254,9 @@ public class HealthMonitor {
public Builder from(HealthMonitor in) {
return this.type(in.getType()).delay(in.getDelay()).timeout(in.getTimeout())
- .attemptsBeforeDeactivation(in.getAttemptsBeforeDeactivation()).bodyRegex(in.getBodyRegex())
- .statusRegex(in.getStatusRegex()).path(in.getPath()).hostHeader(in.getHostHeader());
+ .attemptsBeforeDeactivation(in.getAttemptsBeforeDeactivation()).bodyRegex(in.getBodyRegex().orNull())
+ .statusRegex(in.getStatusRegex().orNull()).path(in.getPath().orNull())
+ .hostHeader(in.getHostHeader().orNull());
}
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApi.java
new file mode 100644
index 0000000000..c6cc266a81
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorApi.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.jclouds.rackspace.cloudloadbalancers.features;
+
+import org.jclouds.rackspace.cloudloadbalancers.domain.HealthMonitor;
+
+/**
+ * The load balancing service includes a health monitoring operation which periodically checks your back-end nodes to
+ * ensure they are responding correctly. If a node is not responding, it is removed from rotation until the health
+ * monitor determines that the node is functional. In addition to being performed periodically, the health check also
+ * is performed against every node that is added to ensure that the node is operating properly before allowing it to
+ * service traffic. Only one health monitor is allowed to be enabled on a load balancer at a time.
+ *
+ * As part of your strategy for monitoring connections, you should consider defining secondary nodes that provide
+ * failover for effectively routing traffic in case the primary node fails. This is an additional feature that will
+ * ensure you remain up in case your primary node fails.
+ *
+ * @see HealthMonitorAsyncApi
+ * @author Everett Toews
+ */
+public interface HealthMonitorApi {
+ /**
+ * Create or update a health monitor.
+ */
+ void createOrUpdate(HealthMonitor healthMonitor);
+
+ /**
+ * Get health monitor.
+ */
+ HealthMonitor get();
+
+ /**
+ * Remove health monitor.
+ *
+ * @return true on a successful removal, false if the connection throttle was not found
+ */
+ boolean remove();
+}
\ No newline at end of file
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorAsyncApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorAsyncApi.java
new file mode 100644
index 0000000000..59ba880702
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/HealthMonitorAsyncApi.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.jclouds.rackspace.cloudloadbalancers.features;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.FalseOnNotFoundOr422;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.HealthMonitor;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseHealthMonitor;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.WrapWith;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides asynchronous access to Rackspace Cloud Load Balancers via their REST API.
+ *
+ *
+ * @see HealthMonitorApi
+ * @author Everett Toews
+ */
+@RequestFilters(AuthenticateRequest.class)
+public interface HealthMonitorAsyncApi {
+
+ /**
+ * @see HealthMonitorApi#createOrUpdate(HealthMonitor)
+ */
+ @Named("healthmonitor:create")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Fallback(VoidOnNotFoundOr404.class)
+ @Path("/healthmonitor")
+ ListenableFuture createOrUpdate(@WrapWith("healthMonitor") HealthMonitor healthMonitor);
+
+ /**
+ * @see HealthMonitorApi#get()
+ */
+ @Named("healthmonitor:get")
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseHealthMonitor.class)
+ @Fallback(NullOnNotFoundOr404.class)
+ @Path("/healthmonitor")
+ ListenableFuture get();
+
+ /**
+ * @see HealthMonitorApi#remove()
+ */
+ @Named("healthmonitor:remove")
+ @DELETE
+ @Fallback(FalseOnNotFoundOr422.class)
+ @Path("/healthmonitor")
+ @Consumes("*/*")
+ ListenableFuture remove();
+}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseHealthMonitor.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseHealthMonitor.java
new file mode 100644
index 0000000000..0b9b5e03a3
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseHealthMonitor.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.jclouds.rackspace.cloudloadbalancers.functions;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.rackspace.cloudloadbalancers.domain.HealthMonitor;
+import org.jclouds.rest.InvocationContext;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Everett Toews
+ */
+public class ParseHealthMonitor implements
+ Function, InvocationContext {
+
+ private final ParseJson