The Metadata API for Load Balancers and Nodes in Rackspace Cloud Load Balancers.

This commit is contained in:
Everett Toews 2013-01-18 17:00:28 -06:00
parent 5e241d96bd
commit b7d76e7b8d
21 changed files with 849 additions and 137 deletions

View File

@ -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 extends HttpRequest> R bindToRequest(R request, Object input) {
checkArgument(checkNotNull(input, "input") instanceof Map, "This binder is only valid for Map<String, String>");
checkNotNull(request, "request");
Map<String, String> metadata = (Map<String, String>) input;
List<Map<String, String>> clbMetadata = Lists.newArrayList();
for (String key: metadata.keySet()) {
clbMetadata.add(ImmutableMap.<String, String> of(
"key", key,
"value", metadata.get(key)));
}
String json = jsonBinder.toJson(ImmutableMap.of("metadata", clbMetadata));
request.setPayload(json);
request.getPayload().getContentMetadata().setContentType("application/json");
return request;
}
}

View File

@ -50,6 +50,7 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
private final SSLTermination sslTermination;
private final SourceAddresses sourceAddresses;
private final Set<AccessRuleWithId> accessRules;
private final Metadata metadata;
public LoadBalancer(String region, int id, String name, String protocol, @Nullable Integer port, Set<Node> nodes,
@Nullable Integer timeout, @Nullable Boolean halfClosed, @Nullable Algorithm algorithm, Status status,
@ -57,10 +58,9 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
String clusterName, Date created, Date updated, @Nullable Map<String, Boolean> connectionLogging,
@Nullable ConnectionThrottle connectionThrottle, boolean contentCaching, int nodeCount,
@Nullable HealthMonitor healthMonitor, @Nullable SSLTermination sslTermination,
SourceAddresses sourceAddresses, Set<AccessRuleWithId> accessRules,
@Nullable Set<Metadata> metadata) {
SourceAddresses sourceAddresses, Set<AccessRuleWithId> accessRules, Metadata metadata) {
super(name, protocol, port, nodes, algorithm, timeout, halfClosed, sessionPersistenceType, connectionLogging,
connectionThrottle, healthMonitor, metadata);
connectionThrottle, healthMonitor);
this.region = checkNotNull(region, "region");
checkArgument(id != -1, "id must be specified");
this.id = id;
@ -74,6 +74,7 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
this.sslTermination = sslTermination;
this.sourceAddresses = sourceAddresses;
this.accessRules = accessRules == null ? ImmutableSet.<AccessRuleWithId> of() : ImmutableSet.copyOf(accessRules);
this.metadata = metadata == null ? new Metadata() : metadata;
}
public String getRegion() {
@ -153,10 +154,20 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
return sourceAddresses;
}
/**
* @see AccessRule
*/
public Set<AccessRuleWithId> getAccessRules() {
return accessRules;
}
/**
* @see Metadata
*/
public Metadata getMetadata() {
return metadata;
}
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues().add("id", id).add("region", region).add("status", status)
.add("name", name).add("protocol", protocol).add("port", port).add("nodeCount", getNodeCount())
@ -260,6 +271,7 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
private SSLTermination sslTermination;
private SourceAddresses sourceAddresses;
private Set<AccessRuleWithId> accessRules;
private Metadata metadata;
public Builder region(String region) {
this.region = region;
@ -324,6 +336,10 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
return this;
}
public Builder metadata(Metadata metadata) {
this.metadata = checkNotNull(metadata, "metadata");
return this;
}
public LoadBalancer build() {
return new LoadBalancer(region, id, name, protocol, port, nodes, timeout, halfClosed, algorithm, status,
@ -344,8 +360,8 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
* {@inheritDoc}
*/
@Override
public Builder node(Node nodes) {
this.nodes.add(checkNotNull(nodes, "nodes"));
public Builder node(Node node) {
this.nodes.add(checkNotNull(node, "nodes"));
return this;
}
@ -429,14 +445,6 @@ public class LoadBalancer extends BaseLoadBalancer<Node, LoadBalancer> {
return Builder.class.cast(super.healthMonitor(healthMonitor));
}
/**
* {@inheritDoc}
*/
@Override
public Builder metadata(@Nullable Set<Metadata> metadata) {
return Builder.class.cast(super.metadata(metadata));
}
@Override
public Builder from(LoadBalancer in) {
return Builder.class.cast(super.from(in)).region(in.getRegion()).id(in.getId()).status(in.getStatus())

View File

@ -41,13 +41,14 @@ public class LoadBalancerRequest extends BaseLoadBalancer<NodeRequest, LoadBalan
private final Set<Map<String, String>> virtualIps;
private final Set<AccessRule> accessRules;
private final Map<String, String> metadata;
public LoadBalancerRequest(String name, String protocol, @Nullable Integer port, Set<NodeRequest> nodes,
@Nullable Algorithm algorithm, @Nullable Integer timeout, @Nullable Boolean halfClosed,
@Nullable Map<String, SessionPersistenceType> sessionPersistenceType,
@Nullable Map<String, Boolean> connectionLogging, @Nullable ConnectionThrottle connectionThrottle,
@Nullable HealthMonitor healthMonitor, @Nullable Set<AccessRule> accessRules,
@Nullable Set<Metadata> metadata, VirtualIP.Type virtualIPType, Integer virtualIPId) {
@Nullable Map<String, String> metadata, VirtualIP.Type virtualIPType, Integer virtualIPId) {
this(name, protocol, port, nodes, algorithm, timeout, halfClosed, sessionPersistenceType, connectionLogging,
connectionThrottle, healthMonitor, accessRules, metadata,
getVirtualIPsFromOptions(virtualIPType, virtualIPId));
@ -58,11 +59,20 @@ public class LoadBalancerRequest extends BaseLoadBalancer<NodeRequest, LoadBalan
@Nullable Map<String, SessionPersistenceType> sessionPersistenceType,
@Nullable Map<String, Boolean> connectionLogging, @Nullable ConnectionThrottle connectionThrottle,
@Nullable HealthMonitor healthMonitor, @Nullable Set<AccessRule> accessRules,
@Nullable Set<Metadata> metadata, Set<Map<String, String>> virtualIPsFromOptions) {
@Nullable Map<String, String> metadata, Set<Map<String, String>> virtualIPsFromOptions) {
super(name, protocol, port, nodes, algorithm, timeout, halfClosed, sessionPersistenceType, connectionLogging,
connectionThrottle, healthMonitor, metadata);
connectionThrottle, healthMonitor);
this.virtualIps = checkNotNull(virtualIPsFromOptions, "virtualIPsFromOptions");
this.accessRules = accessRules;
this.metadata = metadata;
}
public Map<String, String> getMetadata() {
return metadata != null ? metadata : ImmutableMap.<String, String> of();
}
public Set<AccessRule> getAccessRules() {
return accessRules != null ? accessRules : ImmutableSet.<AccessRule> of();
}
static Set<Map<String, String>> getVirtualIPsFromOptions(VirtualIP.Type virtualIPType, Integer virtualIPId) {
@ -95,6 +105,7 @@ public class LoadBalancerRequest extends BaseLoadBalancer<NodeRequest, LoadBalan
private Integer virtualIPId;
private Set<Map<String, String>> virtualIps;
private Set<AccessRule> accessRules;
private Map<String, String> metadata;
/**
* @see VirtualIP
@ -131,6 +142,14 @@ public class LoadBalancerRequest extends BaseLoadBalancer<NodeRequest, LoadBalan
return this;
}
/**
* Information (metadata) that can be associated with each load balancer for the client's personal use.
*/
public Builder metadata(Map<String, String> metadata) {
this.metadata = ImmutableMap.<String, String> copyOf(checkNotNull(metadata, "metadata"));
return this;
}
public LoadBalancerRequest build() {
if (virtualIps == null) {
return new LoadBalancerRequest(name, protocol, port, nodes, algorithm, timeout, halfClosed,

View File

@ -18,94 +18,53 @@
*/
package org.jclouds.rackspace.cloudloadbalancers.domain;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Key and value must be 256 characters or less. All UTF-8 characters are valid.
* </p>
* 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<String, String> {
private Map<String, String> metadata = Maps.newHashMap();
private Map<String, Integer> 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<String, String> delegate() {
return metadata;
}
@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);
return Objects.equal(this.id, that.id);
public int getId(String key) {
return keyToId.get(key);
}
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues()
.add("id", id).add("key", key).add("value", value);
public Integer putId(String key, int id) {
return keyToId.put(key, id);
}
@Override
public String toString() {
return string().toString();
public Iterable<Integer> getIds() {
Set<Integer> ids = Sets.newHashSet();
for (String key: keyToId.keySet()) {
ids.add(keyToId.get(key));
}
public static class Builder {
private Integer id;
private String key;
private String value;
public Builder id(Integer id) {
this.id = id;
return this;
}
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;
}
}

View File

@ -56,16 +56,18 @@ public class Node extends BaseNode<Node> {
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<Node> {
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<Node> {
public static class Builder extends BaseNode.Builder<Node> {
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<Node> {
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<Node> {
}
@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());
}
}

View File

@ -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<N extends BaseNode<N>, T extends BaseLoadBalancer<
protected Map<String, Boolean> connectionLogging;
protected ConnectionThrottle connectionThrottle;
protected HealthMonitor healthMonitor;
protected Set<Metadata> metadata;
// for serialization only
protected BaseLoadBalancer() {
@ -71,7 +69,7 @@ public class BaseLoadBalancer<N extends BaseNode<N>, T extends BaseLoadBalancer<
@Nullable Algorithm algorithm, @Nullable Integer timeout, @Nullable Boolean halfClosed,
@Nullable Map<String, SessionPersistenceType> sessionPersistence,
@Nullable Map<String, Boolean> connectionLogging, @Nullable ConnectionThrottle connectionThrottle,
@Nullable HealthMonitor healthMonitor, @Nullable Set<Metadata> 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<N extends BaseNode<N>, T extends BaseLoadBalancer<
this.connectionLogging = connectionLogging;
this.connectionThrottle = connectionThrottle;
this.healthMonitor = healthMonitor;
this.metadata = metadata;
}
@Override
@ -167,20 +164,12 @@ public class BaseLoadBalancer<N extends BaseNode<N>, T extends BaseLoadBalancer<
return healthMonitor;
}
/**
* @return metadata, which may be null if metadata has not been set.
*/
@Nullable
public Set<Metadata> 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<N extends BaseNode<N>, T extends BaseLoadBalancer<
protected Map<String, Boolean> connectionLogging;
protected ConnectionThrottle connectionThrottle;
protected HealthMonitor healthMonitor;
protected Set<Metadata> 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<N extends BaseNode<N>, T extends BaseLoadBalancer<
return this;
}
/**
* Information (metadata) that can be associated with each load balancer for the client's personal use.
*/
public Builder<N, T> metadata(@Nullable Set<Metadata> metadata) {
this.metadata = metadata;
return this;
}
public BaseLoadBalancer<N, T> build() {
return new BaseLoadBalancer<N, T>(name, protocol, port, nodes, algorithm, timeout, halfClosed,
sessionPersistence, connectionLogging, connectionThrottle, healthMonitor, metadata);
sessionPersistence, connectionLogging, connectionThrottle, healthMonitor);
}
public Builder<N, T> from(T baseLB) {
@ -432,7 +412,7 @@ public class BaseLoadBalancer<N extends BaseNode<N>, 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());
}
}

View File

@ -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<String, String> 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<Integer> metadataIds);
}

View File

@ -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<Void> 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<Metadata> createMetadata(
@PathParam("id") int id,
@BinderParam(BindMetadataToJsonPayload.class) Map<String, String> metadata);
/**
* @see LoadBalancerApi#getMetadata(int)
*/
@GET
@Consumes(MediaType.APPLICATION_JSON)
@ResponseParser(ParseMetadata.class)
@Fallback(EmptyMapOnNotFoundOr404.class)
@Path("/loadbalancers/{id}/metadata")
ListenableFuture<Metadata> 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<Boolean> 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<Boolean> removeMetadatum(@PathParam("id") int id, @PathParam("metadatumId") int metadatumId);
/**
* @see LoadBalancerApi#removeMetadata(int, Iterable)
*/
@DELETE
@Fallback(FalseOnNotFoundOr404.class)
@Consumes("*/*")
@Path("/loadbalancers/{id}/metadata")
ListenableFuture<Boolean> removeMetadata(@PathParam("id") int id,
@QueryParam("id") Iterable<Integer> metadataIds);
}

View File

@ -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<Integer> 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<String, String> 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<Integer> metadataIds);
}

View File

@ -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<IterableWithMarker<LoadBalancer>> list(PaginationOptions options);
/**
* @see NodeApi#get(int)
*/
@GET
@SelectJson("node")
@Consumes(MediaType.APPLICATION_JSON)
@ResponseParser(ParseNode.class)
@Path("/nodes/{id}")
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture<Node> get(@PathParam("id") int id);
@ -129,5 +140,57 @@ public interface NodeAsyncApi {
@Consumes("*/*")
ListenableFuture<Void> remove(@QueryParam("id") Iterable<Integer> ids);
/**
* @see NodeApi#createMetadata(int, Iterable)
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@ResponseParser(ParseMetadata.class)
@Fallback(EmptyMapOnNotFoundOr404.class)
@Path("/nodes/{id}/metadata")
ListenableFuture<Metadata> createMetadata(
@PathParam("id") int id,
@BinderParam(BindMetadataToJsonPayload.class) Map<String, String> metadata);
/**
* @see NodeApi#getMetadata(int)
*/
@GET
@Consumes(MediaType.APPLICATION_JSON)
@ResponseParser(ParseMetadata.class)
@Fallback(EmptyMapOnNotFoundOr404.class)
@Path("/nodes/{id}/metadata")
ListenableFuture<Metadata> 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<Boolean> 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<Boolean> removeMetadatum(@PathParam("id") int id, @PathParam("metadatumId") int metadatumId);
/**
* @see NodeApi#removeMetadata(int, Iterable)
*/
@DELETE
@Fallback(FalseOnNotFoundOr404.class)
@Consumes("*/*")
@Path("/nodes/{id}/metadata")
ListenableFuture<Boolean> removeMetadata(@PathParam("id") int id,
@QueryParam("id") Iterable<Integer> metadataIds);
}

View File

@ -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<LB, LoadBalancer> {
.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<LB, LoadBalancer> {
builder.virtualIPs(ImmutableSet.<VirtualIPWithId> of());
else
builder.virtualIPs(lb.virtualIps);
if (lb.metadata == null)
builder.metadata(new Metadata());
else
builder.metadata(ParseMetadata.transformCLBMetadataToMetadata(lb.metadata));
return builder.build();
}

View File

@ -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<Node, LB> {
SSLTermination sslTermination;
SourceAddresses sourceAddresses;
Set<AccessRuleWithId> accessList;
List<CLBMetadata> metadata;
@Override
public int hashCode() {

View File

@ -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<Metadata> {
@Inject
public ParseMetadata(Json json, TypeLiteral<Metadata> type) {
super(json, type);
}
@Override
public Metadata apply(HttpResponse response) {
Map<String, List<CLBMetadata>> clbMetadata;
try {
Type clbMetadataType = new TypeLiteral<Map<String, List<CLBMetadata>>>() {}.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<CLBMetadata> 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;
}
}

View File

@ -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<HttpResponse, Node>, InvocationContext<ParseNode> {
private final ParseJson<Map<String, NodeWithCLBMetadata>> json;
@Inject
ParseNode(ParseJson<Map<String, NodeWithCLBMetadata>> json) {
this.json = checkNotNull(json, "json");
}
@Override
public Node apply(HttpResponse response) {
Map<String, NodeWithCLBMetadata> 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<Node> {
private int id;
private Status status;
private List<CLBMetadata> metadata;
}
}

View File

@ -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<LoadBalancer> 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.<Integer> of(23, 24)));
}
private Object getExpectedLoadBalancer() {
return new ParseLoadBalancerTest().expected();
}
private Set<LoadBalancer> testLoadBalancers() {
private Set<LoadBalancer> 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;
}
}

View File

@ -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;
@ -147,6 +152,39 @@ public class LoadBalancerApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
}
}
@Test(dependsOnMethods = "testListLoadBalancers")
public void testLoadBalancerMetadata() throws Exception {
for (LoadBalancer lb: lbs) {
Map<String, String> metadataMap = ImmutableMap.<String, String> 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.<Integer> 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);
assertEquals(lb.getName(), name);
@ -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;
}
}

View File

@ -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<CloudL
).getNodeApiForZoneAndLoadBalancer("DFW", 2000);
Set<Node> nodes = api.list().concat().toSet();
assertEquals(nodes, testNodes());
assertEquals(nodes, getExpectedNodes());
}
public void testGetNodeInLoadBalancer() {
@ -106,7 +108,7 @@ public class NodeApiExpectTest extends BaseCloudLoadBalancerApiExpectTest<CloudL
Set<NodeRequest> nodeRequests = ImmutableSortedSet.<NodeRequest> of(nodeRequest1, nodeRequest2, nodeRequest3);
Set<Node> nodes = api.add(nodeRequests);
assertEquals(nodes, testNodes());
assertEquals(nodes, getExpectedNodes());
}
public void testUpdateAttributesForNodeInLoadBalancer() {
@ -152,7 +154,80 @@ public class NodeApiExpectTest extends BaseCloudLoadBalancerApiExpectTest<CloudL
api.remove(nodeIds);
}
private Set<Node> 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.<Integer> 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<Node> getExpectedNodes() {
Node node1 = Node.builder()
.id(410)
.address("10.1.1.1")

View File

@ -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;
@ -135,6 +139,41 @@ public class NodeApiLiveTest extends BaseCloudLoadBalancersApiLiveTest {
}
}
@Test(dependsOnMethods = "testListNodes")
public void testNodeMetadata() throws Exception {
for (Entry<LoadBalancer, Set<Node>> entry : nodes.entrySet()) {
LoadBalancer lb = entry.getKey();
Node node = entry.getValue().iterator().next();
Map<String, String> metadataMap = ImmutableMap.<String, String> 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.<Integer> 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")
protected void tearDownContext() {
@ -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;
}
}

View File

@ -58,6 +58,12 @@ public class ParseLoadBalancerTest extends BaseItemParserTest<LoadBalancer> {
@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<LoadBalancer> {
.clusterName("c1.dfw1")
.created(new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:42Z"))
.updated(new SimpleDateFormatDateService().iso8601SecondsDateParse("2010-11-30T03:23:44Z"))
.metadata(ImmutableSet.<Metadata> 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

View File

@ -0,0 +1 @@
{"metadata":[{"key":"os","value":"ubuntu"},{"key":"color","value":"red"},{"key":"label","value":"web-load-balancer"}]}

View File

@ -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"
}
]
}