From b7d76e7b8d91175d0f1291aff904fbb6b6b18d5f Mon Sep 17 00:00:00 2001
From: Everett Toews
Date: Fri, 18 Jan 2013 17:00:28 -0600
Subject: [PATCH] The Metadata API for Load Balancers and Nodes in Rackspace
Cloud Load Balancers.
---
.../binders/BindMetadataToJsonPayload.java | 72 ++++++++++++
.../domain/LoadBalancer.java | 34 +++---
.../domain/LoadBalancerRequest.java | 25 +++-
.../cloudloadbalancers/domain/Metadata.java | 107 ++++++------------
.../cloudloadbalancers/domain/Node.java | 24 +++-
.../domain/internal/BaseLoadBalancer.java | 28 +----
.../features/LoadBalancerApi.java | 42 +++++++
.../features/LoadBalancerAsyncApi.java | 65 +++++++++++
.../cloudloadbalancers/features/NodeApi.java | 41 +++++++
.../features/NodeAsyncApi.java | 67 ++++++++++-
.../functions/ConvertLB.java | 10 +-
.../cloudloadbalancers/functions/LB.java | 3 +
.../functions/ParseMetadata.java | 88 ++++++++++++++
.../functions/ParseNode.java | 87 ++++++++++++++
.../features/LoadBalancerApiExpectTest.java | 86 +++++++++++++-
.../features/LoadBalancerApiLiveTest.java | 46 ++++++++
.../features/NodeApiExpectTest.java | 83 +++++++++++++-
.../features/NodeApiLiveTest.java | 48 ++++++++
.../functions/ParseLoadBalancerTest.java | 10 +-
.../src/test/resources/metadata-create.json | 1 +
.../src/test/resources/metadata-list.json | 19 ++++
21 files changed, 849 insertions(+), 137 deletions(-)
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/binders/BindMetadataToJsonPayload.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseMetadata.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseNode.java
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/metadata-create.json
create mode 100644 apis/rackspace-cloudloadbalancers/src/test/resources/metadata-list.json
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/binders/BindMetadataToJsonPayload.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/binders/BindMetadataToJsonPayload.java
new file mode 100644
index 0000000000..a1257584d2
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/binders/BindMetadataToJsonPayload.java
@@ -0,0 +1,72 @@
+/**
+ * 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.binders;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.json.Json;
+import org.jclouds.rest.Binder;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+
+/**
+ * Binds the metadata to the request as a JSON payload.
+ *
+ * @author Everett Toews
+ */
+@Singleton
+public class BindMetadataToJsonPayload implements Binder {
+
+ protected final Json jsonBinder;
+
+ @Inject
+ public BindMetadataToJsonPayload(Json jsonBinder) {
+ this.jsonBinder = checkNotNull(jsonBinder, "jsonBinder");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public R bindToRequest(R request, Object input) {
+ checkArgument(checkNotNull(input, "input") instanceof Map, "This binder is only valid for Map");
+ checkNotNull(request, "request");
+
+ Map metadata = (Map) input;
+ List
+ * Use the *Id methods when you need to get a metadata id for updating and removal.
+ *
* @author Everett Toews
*/
-public class Metadata {
- private int id;
- private String key;
- private String value;
+public class Metadata extends ForwardingMap {
+ private Map metadata = Maps.newHashMap();
+ private Map keyToId = Maps.newHashMap();
- private Metadata(Integer id, String key, String value) {
- this.id = id;
- this.key = key;
- this.value = value;
+ public Metadata(Metadata metadata) {
+ super();
+ this.metadata.putAll(metadata);
}
- public int getId() {
- return id;
- }
-
- public String getKey() {
- return key;
- }
-
- public String getValue() {
- return value;
+ public Metadata() {
+ super();
}
@Override
- public int hashCode() {
- return Objects.hashCode(id);
+ protected Map delegate() {
+ return metadata;
+ }
+
+ public int getId(String key) {
+ return keyToId.get(key);
+ }
+
+ public Integer putId(String key, int id) {
+ return keyToId.put(key, id);
}
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- Metadata that = Metadata.class.cast(obj);
+ public Iterable getIds() {
+ Set ids = Sets.newHashSet();
- return Objects.equal(this.id, that.id);
- }
-
- protected ToStringHelper string() {
- return Objects.toStringHelper(this).omitNullValues()
- .add("id", id).add("key", key).add("value", value);
- }
-
- @Override
- public String toString() {
- return string().toString();
- }
-
- public static class Builder {
- private Integer id;
- private String key;
- private String value;
-
- public Builder id(Integer id) {
- this.id = id;
- return this;
+ for (String key: keyToId.keySet()) {
+ ids.add(keyToId.get(key));
}
-
- public Builder key(String key) {
- this.key = key;
- return this;
- }
-
- public Builder value(String value) {
- this.value = value;
- return this;
- }
-
- public Metadata build() {
- return new Metadata(id, key, value);
- }
-
- public Builder from(Metadata in) {
- return id(in.getId()).key(in.getKey()).value(in.getValue());
- }
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public Builder toBuilder() {
- return new Builder().from(this);
+
+ return ids;
}
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/Node.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/Node.java
index bf0be57b19..abd5704fb3 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/Node.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/Node.java
@@ -56,16 +56,18 @@ public class Node extends BaseNode {
private int id;
private Status status;
-
+ private Metadata metadata = new Metadata();
+
// for serialization only
- Node() {
+ protected Node() {
}
- public Node(int id, String address, int port, Condition condition, Type type, Status status, Integer weight) {
+ public Node(String address, int port, Condition condition, Type type, Integer weight, int id, Status status, Metadata metadata) {
super(address, port, condition, type, weight);
checkArgument(id != -1, "id must be specified");
this.id = id;
this.status = checkNotNull(status, "status");
+ this.metadata = metadata != null ? metadata : this.metadata;
}
public int getId() {
@@ -76,10 +78,14 @@ public class Node extends BaseNode {
return status;
}
+ public Metadata getMetadata() {
+ return metadata;
+ }
+
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues()
.add("id", id).add("address", address).add("port", port).add("condition", condition)
- .add("type", type).add("weight", weight).add("status", status);
+ .add("type", type).add("weight", weight).add("status", status).add("metadata", metadata);
}
@Override
@@ -136,6 +142,7 @@ public class Node extends BaseNode {
public static class Builder extends BaseNode.Builder {
private int id = -1;
private Status status;
+ private Metadata metadata;
public Builder id(int id) {
this.id = id;
@@ -150,9 +157,14 @@ public class Node extends BaseNode {
return this;
}
+ public Builder metadata(Metadata metadata) {
+ this.metadata = checkNotNull(metadata, "metadata");
+ return this;
+ }
+
@Override
public Node build() {
- return new Node(id, address, port, condition, type, status, weight);
+ return new Node(address, port, condition, type, weight, id, status, metadata);
}
/**
@@ -196,7 +208,7 @@ public class Node extends BaseNode {
}
@Override
public Builder from(Node in) {
- return Builder.class.cast(super.from(in)).id(in.getId()).status(in.getStatus());
+ return Builder.class.cast(super.from(in)).id(in.getId()).status(in.getStatus()).metadata(in.getMetadata());
}
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/internal/BaseLoadBalancer.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/internal/BaseLoadBalancer.java
index 4808f53d31..9681645648 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/internal/BaseLoadBalancer.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/domain/internal/BaseLoadBalancer.java
@@ -28,7 +28,6 @@ import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rackspace.cloudloadbalancers.domain.ConnectionThrottle;
import org.jclouds.rackspace.cloudloadbalancers.domain.HealthMonitor;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
-import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.features.LoadBalancerApi;
import com.google.common.base.Objects;
@@ -61,7 +60,6 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
protected Map connectionLogging;
protected ConnectionThrottle connectionThrottle;
protected HealthMonitor healthMonitor;
- protected Set metadata;
// for serialization only
protected BaseLoadBalancer() {
@@ -71,7 +69,7 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
@Nullable Algorithm algorithm, @Nullable Integer timeout, @Nullable Boolean halfClosed,
@Nullable Map sessionPersistence,
@Nullable Map connectionLogging, @Nullable ConnectionThrottle connectionThrottle,
- @Nullable HealthMonitor healthMonitor, @Nullable Set metadata) {
+ @Nullable HealthMonitor healthMonitor) {
this.name = checkNotNull(name, "name");
this.protocol = protocol;// null on deleted LB
this.port = port;// null on deleted LB
@@ -83,7 +81,6 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
this.connectionLogging = connectionLogging;
this.connectionThrottle = connectionThrottle;
this.healthMonitor = healthMonitor;
- this.metadata = metadata;
}
@Override
@@ -167,20 +164,12 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
return healthMonitor;
}
- /**
- * @return metadata, which may be null if metadata has not been set.
- */
- @Nullable
- public Set getMetadata() {
- return metadata;
- }
-
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues().add("name", name).add("protocol", protocol)
.add("port", port).add("nodes", nodes).add("timeout", timeout).add("algorithm", algorithm)
.add("timeout", timeout).add("sessionPersistenceType", getSessionPersistenceType())
.add("connectionLogging", connectionLogging).add("connectionThrottle", connectionThrottle)
- .add("healthMonitor", healthMonitor).add("metadata", metadata);
+ .add("healthMonitor", healthMonitor);
}
@Override
@@ -288,7 +277,6 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
protected Map connectionLogging;
protected ConnectionThrottle connectionThrottle;
protected HealthMonitor healthMonitor;
- protected Set metadata;
/**
* Required. Name of the load balancer to create. The name must be 128 characters or less in length, and all
@@ -414,17 +402,9 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
return this;
}
- /**
- * Information (metadata) that can be associated with each load balancer for the client's personal use.
- */
- public Builder metadata(@Nullable Set metadata) {
- this.metadata = metadata;
- return this;
- }
-
public BaseLoadBalancer build() {
return new BaseLoadBalancer(name, protocol, port, nodes, algorithm, timeout, halfClosed,
- sessionPersistence, connectionLogging, connectionThrottle, healthMonitor, metadata);
+ sessionPersistence, connectionLogging, connectionThrottle, healthMonitor);
}
public Builder from(T baseLB) {
@@ -432,7 +412,7 @@ public class BaseLoadBalancer, T extends BaseLoadBalancer<
.algorithm(baseLB.getAlgorithm()).timeout(baseLB.getTimeout()).halfClosed(baseLB.isHalfClosed())
.nodes(baseLB.getNodes()).sessionPersistenceType(baseLB.getSessionPersistenceType())
.connectionLogging(baseLB.isConnectionLogging()).connectionThrottle(baseLB.getConnectionThrottle())
- .healthMonitor(baseLB.getHealthMonitor()).metadata(baseLB.getMetadata());
+ .healthMonitor(baseLB.getHealthMonitor());
}
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApi.java
index bd6e092bba..3985f18ab7 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApi.java
@@ -18,6 +18,8 @@
*/
package org.jclouds.rackspace.cloudloadbalancers.features;
+import java.util.Map;
+
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.HttpResponseException;
@@ -25,6 +27,7 @@ import org.jclouds.openstack.v2_0.options.PaginationOptions;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
/**
* Provides synchronous access to CloudLoadBalancers LoadBalancer features.
@@ -104,4 +107,43 @@ public interface LoadBalancerApi {
* to remove
*/
void remove(int id);
+
+ /**
+ * When a metadata item is added, it is assigned a unique identifier that can be used for mutating operations such
+ * as changing the value attribute or removing it. Key and value must be 256 characters or less.
+ * All UTF-8 characters are valid.
+ */
+ Metadata createMetadata(int id, Map metadata);
+
+ /**
+ * List a load balancer's metadata.
+ */
+ Metadata getMetadata(int id);
+
+ /**
+ * Update metadatum. Key and value must be 256 characters or less. All UTF-8 characters are valid.
+ *
+ * @return true on a successful update, false if the metadatum was not found
+ */
+ boolean updateMetadatum(int id, int metadatumId, String value);
+
+ /**
+ * Remove metadatum.
+ *
+ * @see LoadBalancerApi#remove(int, Iterable)
+ *
+ * @return true on a successful removal, false if the metadatum was not found
+ */
+ boolean removeMetadatum(int id, int metadatumId);
+
+ /**
+ * Batch delete metadata given the specified ids.
+ *
+ * The current default limit is ten ids per request. Any and all configuration data is immediately purged and is
+ * not recoverable. If one or more of the items in the list cannot be removed due to its current status, an
+ * exception is thrown along with the ids of the ones the system identified as potential failures for this request.
+ *
+ * @return true on a successful removal, false if the metadata was not found
+ */
+ boolean removeMetadata(int id, Iterable metadataIds);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerAsyncApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerAsyncApi.java
index c16f381155..b5e9d35f27 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerAsyncApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerAsyncApi.java
@@ -18,6 +18,8 @@
*/
package org.jclouds.rackspace.cloudloadbalancers.features;
+import java.util.Map;
+
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -25,9 +27,13 @@ import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import org.jclouds.Fallbacks.EmptyMapOnNotFoundOr404;
import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.collect.IterableWithMarker;
@@ -35,12 +41,18 @@ import org.jclouds.collect.PagedIterable;
import org.jclouds.openstack.keystone.v2_0.KeystoneFallbacks.EmptyPaginatedCollectionOnNotFoundOr404;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.jclouds.rackspace.cloudloadbalancers.binders.BindMetadataToJsonPayload;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.functions.ParseLoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.functions.ParseLoadBalancers;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseMetadata;
+import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Payload;
+import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
@@ -117,4 +129,57 @@ public interface LoadBalancerAsyncApi {
@Consumes("*/*")
ListenableFuture remove(@PathParam("id") int id);
+ /**
+ * @see LoadBalancerApi#createMetadata(int, Iterable)
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseMetadata.class)
+ @Fallback(EmptyMapOnNotFoundOr404.class)
+ @Path("/loadbalancers/{id}/metadata")
+ ListenableFuture createMetadata(
+ @PathParam("id") int id,
+ @BinderParam(BindMetadataToJsonPayload.class) Map metadata);
+
+ /**
+ * @see LoadBalancerApi#getMetadata(int)
+ */
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseMetadata.class)
+ @Fallback(EmptyMapOnNotFoundOr404.class)
+ @Path("/loadbalancers/{id}/metadata")
+ ListenableFuture getMetadata(@PathParam("id") int lb);
+
+ /**
+ * @see LoadBalancerApi#updateMetadatum(int, int, String)
+ */
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes("*/*")
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Payload("%7B\"meta\":%7B\"value\":\"{value}\"%7D%7D")
+ @Path("/loadbalancers/{id}/metadata/{metadatumId}")
+ ListenableFuture updateMetadatum(@PathParam("id") int id,
+ @PathParam("metadatumId") int metadatumId,
+ @PayloadParam("value") String value);
+
+ /**
+ * @see LoadBalancerApi#removeMetadatum(int, int)
+ */
+ @DELETE
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Consumes("*/*")
+ @Path("/loadbalancers/{id}/metadata/{metadatumId}")
+ ListenableFuture removeMetadatum(@PathParam("id") int id, @PathParam("metadatumId") int metadatumId);
+
+ /**
+ * @see LoadBalancerApi#removeMetadata(int, Iterable)
+ */
+ @DELETE
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Consumes("*/*")
+ @Path("/loadbalancers/{id}/metadata")
+ ListenableFuture removeMetadata(@PathParam("id") int id,
+ @QueryParam("id") Iterable metadataIds);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApi.java
index 22f65e87ae..a601dd9eca 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApi.java
@@ -18,12 +18,14 @@
*/
package org.jclouds.rackspace.cloudloadbalancers.features;
+import java.util.Map;
import java.util.Set;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.v2_0.options.PaginationOptions;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerAttributes;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.Node;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
@@ -111,4 +113,43 @@ public interface NodeApi {
* nodes to remove
*/
void remove(Iterable ids);
+
+ /**
+ * When a metadata item is added, it is assigned a unique identifier that can be used for mutating operations such
+ * as changing the value attribute or removing it. Key and value must be 256 characters or less.
+ * All UTF-8 characters are valid.
+ */
+ Metadata createMetadata(int id, Map metadata);
+
+ /**
+ * List a load balancer's metadata.
+ */
+ Metadata getMetadata(int id);
+
+ /**
+ * Update metadatum. Key and value must be 256 characters or less. All UTF-8 characters are valid.
+ *
+ * @return true on a successful update, false if the metadatum was not found
+ */
+ boolean updateMetadatum(int id, int metadatumId, String value);
+
+ /**
+ * Remove metadatum.
+ *
+ * @see NodeApi#remove(int, Iterable)
+ *
+ * @return true on a successful removal, false if the metadatum was not found
+ */
+ boolean removeMetadatum(int id, int metadatumId);
+
+ /**
+ * Batch delete metadata given the specified ids.
+ *
+ * The current default limit is ten ids per request. Any and all configuration data is immediately purged and is
+ * not recoverable. If one or more of the items in the list cannot be removed due to its current status, an
+ * exception is thrown along with the ids of the ones the system identified as potential failures for this request.
+ *
+ * @return true on a successful removal, false if the metadata was not found
+ */
+ boolean removeMetadata(int id, Iterable metadataIds);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeAsyncApi.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeAsyncApi.java
index e1fa433c30..6115c336c0 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeAsyncApi.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeAsyncApi.java
@@ -18,6 +18,7 @@
*/
package org.jclouds.rackspace.cloudloadbalancers.features;
+import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
@@ -27,10 +28,13 @@ import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
+import org.jclouds.Fallbacks.EmptyMapOnNotFoundOr404;
import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
+import org.jclouds.Fallbacks.FalseOnNotFoundOr404;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.collect.IterableWithMarker;
@@ -38,12 +42,19 @@ import org.jclouds.collect.PagedIterable;
import org.jclouds.openstack.keystone.v2_0.KeystoneFallbacks.EmptyPaginatedCollectionOnNotFoundOr404;
import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
import org.jclouds.openstack.v2_0.options.PaginationOptions;
+import org.jclouds.rackspace.cloudloadbalancers.binders.BindMetadataToJsonPayload;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.Node;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseMetadata;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseNode;
import org.jclouds.rackspace.cloudloadbalancers.functions.ParseNodes;
+import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Payload;
+import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
@@ -98,15 +109,15 @@ public interface NodeAsyncApi {
@ResponseParser(ParseNodes.class)
@Consumes(MediaType.APPLICATION_JSON)
@Fallback(EmptyPaginatedCollectionOnNotFoundOr404.class)
- @Path("/loadbalancers")
+ @Path("/nodes")
ListenableFuture> list(PaginationOptions options);
/**
* @see NodeApi#get(int)
*/
@GET
- @SelectJson("node")
@Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseNode.class)
@Path("/nodes/{id}")
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture get(@PathParam("id") int id);
@@ -129,5 +140,57 @@ public interface NodeAsyncApi {
@Consumes("*/*")
ListenableFuture remove(@QueryParam("id") Iterable ids);
+ /**
+ * @see NodeApi#createMetadata(int, Iterable)
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseMetadata.class)
+ @Fallback(EmptyMapOnNotFoundOr404.class)
+ @Path("/nodes/{id}/metadata")
+ ListenableFuture createMetadata(
+ @PathParam("id") int id,
+ @BinderParam(BindMetadataToJsonPayload.class) Map metadata);
+ /**
+ * @see NodeApi#getMetadata(int)
+ */
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ResponseParser(ParseMetadata.class)
+ @Fallback(EmptyMapOnNotFoundOr404.class)
+ @Path("/nodes/{id}/metadata")
+ ListenableFuture getMetadata(@PathParam("id") int lb);
+
+ /**
+ * @see NodeApi#updateMetadatum(int, int, String)
+ */
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes("*/*")
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Payload("%7B\"meta\":%7B\"value\":\"{value}\"%7D%7D")
+ @Path("/nodes/{id}/metadata/{metadatumId}")
+ ListenableFuture updateMetadatum(@PathParam("id") int id,
+ @PathParam("metadatumId") int metadatumId,
+ @PayloadParam("value") String value);
+
+ /**
+ * @see NodeApi#removeMetadatum(int, int)
+ */
+ @DELETE
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Consumes("*/*")
+ @Path("/nodes/{id}/metadata/{metadatumId}")
+ ListenableFuture removeMetadatum(@PathParam("id") int id, @PathParam("metadatumId") int metadatumId);
+
+ /**
+ * @see NodeApi#removeMetadata(int, Iterable)
+ */
+ @DELETE
+ @Fallback(FalseOnNotFoundOr404.class)
+ @Consumes("*/*")
+ @Path("/nodes/{id}/metadata")
+ ListenableFuture removeMetadata(@PathParam("id") int id,
+ @QueryParam("id") Iterable metadataIds);
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ConvertLB.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ConvertLB.java
index e94bed500d..5e7bd2c810 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ConvertLB.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ConvertLB.java
@@ -24,8 +24,9 @@ import javax.inject.Inject;
import org.jclouds.logging.Logger;
import org.jclouds.rackspace.cloudloadbalancers.domain.AccessRuleWithId;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
-import org.jclouds.rackspace.cloudloadbalancers.domain.VirtualIPWithId;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer.Builder;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
+import org.jclouds.rackspace.cloudloadbalancers.domain.VirtualIPWithId;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
@@ -58,8 +59,7 @@ public class ConvertLB implements Function {
.protocol(lb.getProtocol()).port(lb.getPort()).nodeCount(lb.nodeCount).nodes(lb.getNodes())
.timeout(lb.getTimeout()).algorithm(lb.getAlgorithm()).halfClosed(lb.isHalfClosed())
.sessionPersistenceType(lb.getSessionPersistenceType()).connectionLogging(lb.isConnectionLogging())
- .connectionThrottle(lb.getConnectionThrottle()).healthMonitor(lb.getHealthMonitor())
- .metadata(lb.getMetadata());
+ .connectionThrottle(lb.getConnectionThrottle()).healthMonitor(lb.getHealthMonitor());
if (lb.cluster.size() == 1)
builder.clusterName(Iterables.get(lb.cluster.values(), 0));
@@ -81,6 +81,10 @@ public class ConvertLB implements Function {
builder.virtualIPs(ImmutableSet. of());
else
builder.virtualIPs(lb.virtualIps);
+ if (lb.metadata == null)
+ builder.metadata(new Metadata());
+ else
+ builder.metadata(ParseMetadata.transformCLBMetadataToMetadata(lb.metadata));
return builder.build();
}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/LB.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/LB.java
index 20dc3f6b3c..3e3c72849c 100644
--- a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/LB.java
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/LB.java
@@ -19,6 +19,7 @@
package org.jclouds.rackspace.cloudloadbalancers.functions;
import java.util.Date;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -29,6 +30,7 @@ import org.jclouds.rackspace.cloudloadbalancers.domain.SSLTermination;
import org.jclouds.rackspace.cloudloadbalancers.domain.SourceAddresses;
import org.jclouds.rackspace.cloudloadbalancers.domain.VirtualIPWithId;
import org.jclouds.rackspace.cloudloadbalancers.domain.internal.BaseLoadBalancer;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseMetadata.CLBMetadata;
import com.google.common.base.Objects;
import com.google.common.collect.Maps;
@@ -49,6 +51,7 @@ class LB extends BaseLoadBalancer {
SSLTermination sslTermination;
SourceAddresses sourceAddresses;
Set accessList;
+ List metadata;
@Override
public int hashCode() {
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseMetadata.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseMetadata.java
new file mode 100644
index 0000000000..2dab0f345a
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseMetadata.java
@@ -0,0 +1,88 @@
+/**
+ * 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 org.jclouds.http.HttpUtils.releasePayload;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
+
+import com.google.inject.TypeLiteral;
+
+/**
+ * @author Everett Toews
+ */
+public class ParseMetadata extends ParseJson {
+
+ @Inject
+ public ParseMetadata(Json json, TypeLiteral type) {
+ super(json, type);
+ }
+
+ @Override
+ public Metadata apply(HttpResponse response) {
+ Map> clbMetadata;
+
+ try {
+ Type clbMetadataType = new TypeLiteral>>() {}.getType();
+ clbMetadata = apply(response.getPayload().getInput(), clbMetadataType);
+ }
+ catch (IOException e) {
+ StringBuilder message = new StringBuilder();
+ message.append("Error parsing response");
+ logger.error(e, message.toString());
+ throw new HttpResponseException(message.toString() + "\n" + response, null, response, e);
+ }
+ finally {
+ releasePayload(response);
+ }
+
+ return transformCLBMetadataToMetadata(clbMetadata.get("metadata"));
+ }
+
+ public static Metadata transformCLBMetadataToMetadata(List clbMetadatum) {
+ Metadata metadata = new Metadata();
+
+ for (CLBMetadata clbMetadata: clbMetadatum) {
+ metadata.put(clbMetadata.key, clbMetadata.value);
+ metadata.putId(clbMetadata.key, clbMetadata.id);
+ }
+
+ return metadata;
+ }
+
+ /**
+ * This class is here only to deal with the metadata format in CLB.
+ */
+ public static class CLBMetadata {
+ private int id;
+ private String key;
+ private String value;
+ }
+}
diff --git a/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseNode.java b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseNode.java
new file mode 100644
index 0000000000..663fcb844a
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/main/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseNode.java
@@ -0,0 +1,87 @@
+/**
+ * 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.List;
+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.Node;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Node.Status;
+import org.jclouds.rackspace.cloudloadbalancers.domain.internal.BaseNode;
+import org.jclouds.rackspace.cloudloadbalancers.functions.ParseMetadata.CLBMetadata;
+import org.jclouds.rest.InvocationContext;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+
+/**
+ * @author Everett Toews
+ */
+public class ParseNode implements Function, InvocationContext {
+
+ private final ParseJson> json;
+
+ @Inject
+ ParseNode(ParseJson> json) {
+ this.json = checkNotNull(json, "json");
+ }
+
+ @Override
+ public Node apply(HttpResponse response) {
+ Map map = json.apply(response);
+
+ if (map == null || map.size() == 0)
+ return null;
+
+ NodeWithCLBMetadata nodeWithCLBMetadata = Iterables.get(map.values(), 0);
+ Node node = Node.builder()
+ .address(nodeWithCLBMetadata.getAddress())
+ .port(nodeWithCLBMetadata.getPort())
+ .condition(nodeWithCLBMetadata.getCondition())
+ .type(nodeWithCLBMetadata.getType())
+ .weight(nodeWithCLBMetadata.getWeight())
+ .id(nodeWithCLBMetadata.id)
+ .status(nodeWithCLBMetadata.status)
+ .metadata(ParseMetadata.transformCLBMetadataToMetadata(nodeWithCLBMetadata.metadata))
+ .build();
+
+ return node;
+ }
+
+ @Override
+ public ParseNode setContext(HttpRequest request) {
+ return this;
+ }
+
+ /**
+ * This class is here only to deal with the metadata format in CLB.
+ */
+ private static class NodeWithCLBMetadata extends BaseNode {
+ private int id;
+ private Status status;
+ private List metadata;
+ }
+}
diff --git a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiExpectTest.java b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiExpectTest.java
index af9199072f..db10d1991a 100644
--- a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiExpectTest.java
+++ b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiExpectTest.java
@@ -19,6 +19,7 @@
package org.jclouds.rackspace.cloudloadbalancers.features;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
import java.net.URI;
import java.util.Set;
@@ -30,6 +31,7 @@ import org.jclouds.rackspace.cloudloadbalancers.CloudLoadBalancersApi;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
import org.jclouds.rackspace.cloudloadbalancers.domain.VirtualIP;
import org.jclouds.rackspace.cloudloadbalancers.functions.ParseLoadBalancerTest;
@@ -37,6 +39,7 @@ import org.jclouds.rackspace.cloudloadbalancers.functions.ParseLoadBalancersTest
import org.jclouds.rackspace.cloudloadbalancers.internal.BaseCloudLoadBalancerApiExpectTest;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
/**
@@ -55,7 +58,7 @@ public class LoadBalancerApiExpectTest extends BaseCloudLoadBalancerApiExpectTes
).getLoadBalancerApiForZone("DFW");
Set loadBalancers = api.list().concat().toSet();
- assertEquals(loadBalancers, testLoadBalancers());
+ assertEquals(loadBalancers, getExpectedLoadBalancers());
}
public void testGetLoadBalancer() throws Exception {
@@ -68,7 +71,7 @@ public class LoadBalancerApiExpectTest extends BaseCloudLoadBalancerApiExpectTes
).getLoadBalancerApiForZone("DFW");
LoadBalancer loadBalancer = api.get(2000);
- assertEquals(loadBalancer, testLoadBalancer());
+ assertEquals(loadBalancer, getExpectedLoadBalancer());
}
public void testCreateLoadBalancer() throws Exception {
@@ -109,7 +112,7 @@ public class LoadBalancerApiExpectTest extends BaseCloudLoadBalancerApiExpectTes
LoadBalancer loadBalancer = api.create(lbRequest);
- assertEquals(loadBalancer, testLoadBalancer());
+ assertEquals(loadBalancer, getExpectedLoadBalancer());
}
public void testUpdateLoadBalancerAttributes() {
@@ -146,11 +149,84 @@ public class LoadBalancerApiExpectTest extends BaseCloudLoadBalancerApiExpectTes
api.remove(2000);
}
- private Object testLoadBalancer() {
+ public void testListMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/metadata");
+ LoadBalancerApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/metadata-list.json")).build()
+ ).getLoadBalancerApiForZone("DFW");
+
+ Metadata metadata = api.getMetadata(2000);
+ assertEquals(metadata, getExpectedMetadataWithIds());
+ }
+
+ public void testCreateMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/metadata");
+ LoadBalancerApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET()
+ .method("POST")
+ .endpoint(endpoint)
+ .payload(payloadFromResourceWithContentType("/metadata-create.json", MediaType.APPLICATION_JSON)).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/metadata-list.json")).build()
+ ).getLoadBalancerApiForZone("DFW");
+
+ Metadata metadata = api.createMetadata(2000, getExpectedMetadata());
+ assertEquals(metadata, getExpectedMetadataWithIds());
+ }
+
+ public void testRemoveSingleMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/metadata/23");
+ LoadBalancerApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().method("DELETE").endpoint(endpoint).replaceHeader("Accept", MediaType.WILDCARD).build(),
+ HttpResponse.builder().statusCode(200).build()
+ ).getLoadBalancerApiForZone("DFW");
+
+ assertTrue(api.removeMetadatum(2000, 23));
+ }
+
+ public void testRemoveManyMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/metadata?id=23&id=24");
+ LoadBalancerApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().method("DELETE").endpoint(endpoint).replaceHeader("Accept", MediaType.WILDCARD).build(),
+ HttpResponse.builder().statusCode(200).build()
+ ).getLoadBalancerApiForZone("DFW");
+
+
+
+ assertTrue(api.removeMetadata(2000, ImmutableList. of(23, 24)));
+ }
+
+ private Object getExpectedLoadBalancer() {
return new ParseLoadBalancerTest().expected();
}
- private Set testLoadBalancers() {
+ private Set getExpectedLoadBalancers() {
return new ParseLoadBalancersTest().data();
}
+
+ private Metadata getExpectedMetadata() {
+ Metadata metadata = new Metadata();
+ metadata.put("color", "red");
+ metadata.put("label", "web-load-balancer");
+ metadata.put("os", "ubuntu");
+
+ return metadata;
+ }
+
+ private Metadata getExpectedMetadataWithIds() {
+ Metadata metadata = getExpectedMetadata();
+ metadata.putId("color", 1);
+ metadata.putId("label", 2);
+ metadata.putId("os", 3);
+
+ return metadata;
+ }
}
diff --git a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiLiveTest.java b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiLiveTest.java
index 04d9e0f60b..4c7d582183 100644
--- a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiLiveTest.java
+++ b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/LoadBalancerApiLiveTest.java
@@ -22,20 +22,25 @@ import static org.jclouds.rackspace.cloudloadbalancers.predicates.LoadBalancerPr
import static org.jclouds.rackspace.cloudloadbalancers.predicates.LoadBalancerPredicates.awaitDeleted;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
+import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
import org.jclouds.rackspace.cloudloadbalancers.domain.VirtualIP.Type;
import org.jclouds.rackspace.cloudloadbalancers.internal.BaseCloudLoadBalancersApiLiveTest;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -146,6 +151,39 @@ public class LoadBalancerApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
}
}
}
+
+ @Test(dependsOnMethods = "testListLoadBalancers")
+ public void testLoadBalancerMetadata() throws Exception {
+ for (LoadBalancer lb: lbs) {
+ Map metadataMap = ImmutableMap. of(
+ "key1", "value1",
+ "key2", "value2",
+ "key3", "value3");
+
+ Metadata metadata = clbApi.getLoadBalancerApiForZone(lb.getRegion()).createMetadata(lb.getId(), metadataMap);
+ assertEquals(metadata, getExpectedMetadata());
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+
+ metadata = clbApi.getLoadBalancerApiForZone(lb.getRegion()).getMetadata(lb.getId());
+ assertEquals(metadata, getExpectedMetadata());
+
+ assertTrue(clbApi.getLoadBalancerApiForZone(lb.getRegion()).updateMetadatum(lb.getId(), metadata.getId("key1"), "key1-updated"));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getLoadBalancerApiForZone(lb.getRegion()).getMetadata(lb.getId());
+ assertEquals(metadata.get("key1"), "key1-updated");
+
+ assertTrue(clbApi.getLoadBalancerApiForZone(lb.getRegion()).removeMetadatum(lb.getId(), metadata.getId("key1")));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getLoadBalancerApiForZone(lb.getRegion()).getMetadata(lb.getId());
+ assertNull(metadata.get("key1"));
+
+ assertTrue(clbApi.getLoadBalancerApiForZone(lb.getRegion()).removeMetadata(lb.getId(),
+ ImmutableList. of(metadata.getId("key2"), metadata.getId("key3"))));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getLoadBalancerApiForZone(lb.getRegion()).getMetadata(lb.getId());
+ assertEquals(metadata.size(), 0);
+ }
+ }
private void checkLBInRegion(String region, LoadBalancer lb, String name) {
assertEquals(lb.getRegion(), region);
@@ -155,4 +193,12 @@ public class LoadBalancerApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
assertEquals(Iterables.get(lb.getVirtualIPs(), 0).getType(), Type.PUBLIC);
}
+ private Metadata getExpectedMetadata() {
+ Metadata metadata = new Metadata();
+ metadata.put("key1", "value1");
+ metadata.put("key2", "value2");
+ metadata.put("key3", "value3");
+
+ return metadata;
+ }
}
diff --git a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiExpectTest.java b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiExpectTest.java
index 5f5e2c4efe..20cdac0a01 100644
--- a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiExpectTest.java
+++ b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiExpectTest.java
@@ -19,6 +19,7 @@
package org.jclouds.rackspace.cloudloadbalancers.features;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
import java.net.URI;
import java.util.Set;
@@ -27,13 +28,14 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpResponse;
import org.jclouds.rackspace.cloudloadbalancers.CloudLoadBalancersApi;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.Node;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
-import org.jclouds.rackspace.cloudloadbalancers.features.NodeApi;
import org.jclouds.rackspace.cloudloadbalancers.internal.BaseCloudLoadBalancerApiExpectTest;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
@@ -52,7 +54,7 @@ public class NodeApiExpectTest extends BaseCloudLoadBalancerApiExpectTest nodes = api.list().concat().toSet();
- assertEquals(nodes, testNodes());
+ assertEquals(nodes, getExpectedNodes());
}
public void testGetNodeInLoadBalancer() {
@@ -106,7 +108,7 @@ public class NodeApiExpectTest extends BaseCloudLoadBalancerApiExpectTest nodeRequests = ImmutableSortedSet. of(nodeRequest1, nodeRequest2, nodeRequest3);
Set nodes = api.add(nodeRequests);
- assertEquals(nodes, testNodes());
+ assertEquals(nodes, getExpectedNodes());
}
public void testUpdateAttributesForNodeInLoadBalancer() {
@@ -152,7 +154,80 @@ public class NodeApiExpectTest extends BaseCloudLoadBalancerApiExpectTest testNodes() {
+ public void testListMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/nodes/410/metadata");
+ NodeApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().endpoint(endpoint).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/metadata-list.json")).build()
+ ).getNodeApiForZoneAndLoadBalancer("DFW", 2000);
+
+ Metadata metadata = api.getMetadata(410);
+ assertEquals(metadata, getExpectedMetadataWithIds());
+ }
+
+ public void testCreateMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/nodes/410/metadata");
+ NodeApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET()
+ .method("POST")
+ .endpoint(endpoint)
+ .payload(payloadFromResourceWithContentType("/metadata-create.json", MediaType.APPLICATION_JSON)).build(),
+ HttpResponse.builder().statusCode(200).payload(payloadFromResource("/metadata-list.json")).build()
+ ).getNodeApiForZoneAndLoadBalancer("DFW", 2000);
+
+ Metadata metadata = api.createMetadata(410, getExpectedMetadata());
+ assertEquals(metadata, getExpectedMetadataWithIds());
+ }
+
+ public void testRemoveSingleMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/nodes/410/metadata/23");
+ NodeApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().method("DELETE").endpoint(endpoint).replaceHeader("Accept", MediaType.WILDCARD).build(),
+ HttpResponse.builder().statusCode(200).build()
+ ).getNodeApiForZoneAndLoadBalancer("DFW", 2000);
+
+ assertTrue(api.removeMetadatum(410, 23));
+ }
+
+ public void testRemoveManyMetadata() {
+ URI endpoint = URI.create("https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/123123/loadbalancers/2000/nodes/410/metadata?id=23&id=24");
+ NodeApi api = requestsSendResponses(
+ rackspaceAuthWithUsernameAndApiKey,
+ responseWithAccess,
+ authenticatedGET().method("DELETE").endpoint(endpoint).replaceHeader("Accept", MediaType.WILDCARD).build(),
+ HttpResponse.builder().statusCode(200).build()
+ ).getNodeApiForZoneAndLoadBalancer("DFW", 2000);
+
+
+
+ assertTrue(api.removeMetadata(410, ImmutableList. of(23, 24)));
+ }
+
+ private Metadata getExpectedMetadata() {
+ Metadata metadata = new Metadata();
+ metadata.put("color", "red");
+ metadata.put("label", "web-load-balancer");
+ metadata.put("os", "ubuntu");
+
+ return metadata;
+ }
+
+ private Metadata getExpectedMetadataWithIds() {
+ Metadata metadata = getExpectedMetadata();
+ metadata.putId("color", 1);
+ metadata.putId("label", 2);
+ metadata.putId("os", 3);
+
+ return metadata;
+ }
+
+ private Set getExpectedNodes() {
Node node1 = Node.builder()
.id(410)
.address("10.1.1.1")
diff --git a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiLiveTest.java b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiLiveTest.java
index 01aa3e3064..c03992bf08 100644
--- a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiLiveTest.java
+++ b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/features/NodeApiLiveTest.java
@@ -21,6 +21,7 @@ package org.jclouds.rackspace.cloudloadbalancers.features;
import static org.jclouds.rackspace.cloudloadbalancers.predicates.LoadBalancerPredicates.awaitAvailable;
import static org.jclouds.rackspace.cloudloadbalancers.predicates.LoadBalancerPredicates.awaitDeleted;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Arrays;
@@ -30,11 +31,14 @@ import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Logger;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancer;
import org.jclouds.rackspace.cloudloadbalancers.domain.LoadBalancerRequest;
+import org.jclouds.rackspace.cloudloadbalancers.domain.Metadata;
import org.jclouds.rackspace.cloudloadbalancers.domain.Node;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeAttributes;
import org.jclouds.rackspace.cloudloadbalancers.domain.NodeRequest;
@@ -134,6 +138,41 @@ public class NodeApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
}
}
}
+
+ @Test(dependsOnMethods = "testListNodes")
+ public void testNodeMetadata() throws Exception {
+ for (Entry> entry : nodes.entrySet()) {
+ LoadBalancer lb = entry.getKey();
+ Node node = entry.getValue().iterator().next();
+ Map metadataMap = ImmutableMap. of(
+ "key1", "value1",
+ "key2", "value2",
+ "key3", "value3");
+
+ Metadata metadata = clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).createMetadata(node.getId(), metadataMap);
+ assertEquals(metadata, getExpectedMetadata());
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+
+ metadata = clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).getMetadata(node.getId());
+ assertEquals(metadata, getExpectedMetadata());
+
+ assertTrue(clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).updateMetadatum(node.getId(), metadata.getId("key1"), "key1-updated"));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).getMetadata(node.getId());
+ assertEquals(metadata.get("key1"), "key1-updated");
+
+ assertTrue(clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).removeMetadatum(node.getId(), metadata.getId("key1")));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).getMetadata(node.getId());
+ assertNull(metadata.get("key1"));
+
+ assertTrue(clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).removeMetadata(node.getId(),
+ ImmutableList. of(metadata.getId("key2"), metadata.getId("key3"))));
+ assertTrue(awaitAvailable(clbApi.getLoadBalancerApiForZone(lb.getRegion())).apply(lb));
+ metadata = clbApi.getNodeApiForZoneAndLoadBalancer(lb.getRegion(), lb.getId()).getMetadata(node.getId());
+ assertEquals(metadata.size(), 0);
+ }
+ }
@Override
@AfterGroups(groups = "live")
@@ -150,4 +189,13 @@ public class NodeApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
}
super.tearDownContext();
}
+
+ private Metadata getExpectedMetadata() {
+ Metadata metadata = new Metadata();
+ metadata.put("key1", "value1");
+ metadata.put("key2", "value2");
+ metadata.put("key3", "value3");
+
+ return metadata;
+ }
}
diff --git a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseLoadBalancerTest.java b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseLoadBalancerTest.java
index 198db8d46a..b76f601c01 100644
--- a/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseLoadBalancerTest.java
+++ b/apis/rackspace-cloudloadbalancers/src/test/java/org/jclouds/rackspace/cloudloadbalancers/functions/ParseLoadBalancerTest.java
@@ -58,6 +58,12 @@ public class ParseLoadBalancerTest extends BaseItemParserTest {
@Override
public LoadBalancer expected() {
+ Metadata metadata = new Metadata();
+ metadata.put("color", "red");
+ metadata.putId("color", 1);
+ metadata.put("label", "web-load-balancer");
+ metadata.putId("label", 2);
+
return LoadBalancer
.builder()
.region("DFW")
@@ -88,9 +94,7 @@ public class ParseLoadBalancerTest extends BaseItemParserTest {
.clusterName("c1.dfw1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:42Z"))
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:44Z"))
- .metadata(ImmutableSet. of(
- Metadata.builder().id(1).key("color").value("red").build(),
- Metadata.builder().id(2).key("label").value("web-load-balancer").build())).build();
+ .metadata(metadata).build();
}
// add factory binding as this is not default
diff --git a/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-create.json b/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-create.json
new file mode 100644
index 0000000000..3bd30bdb0b
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-create.json
@@ -0,0 +1 @@
+{"metadata":[{"key":"os","value":"ubuntu"},{"key":"color","value":"red"},{"key":"label","value":"web-load-balancer"}]}
\ No newline at end of file
diff --git a/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-list.json b/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-list.json
new file mode 100644
index 0000000000..e1ff79c49a
--- /dev/null
+++ b/apis/rackspace-cloudloadbalancers/src/test/resources/metadata-list.json
@@ -0,0 +1,19 @@
+{
+ "metadata": [
+ {
+ "id": "1",
+ "key": "color",
+ "value": "red"
+ },
+ {
+ "id": "2",
+ "key": "label",
+ "value": "web-load-balancer"
+ },
+ {
+ "id": "3",
+ "key": "os",
+ "value": "ubuntu"
+ }
+ ]
+}
\ No newline at end of file