From e30f0f626ee40a9764f4a3f4e379158aeb656e17 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Tue, 4 Oct 2011 20:40:35 +0100 Subject: [PATCH 1/3] 612: Added required classes and tests to support CreateTags, DeleteTags and DescribeTags for TagClient and TagAsyncClient --- .../java/org/jclouds/aws/util/AWSUtils.java | 19 ++ .../jclouds/aws/ec2/AWSEC2AsyncClient.java | 7 + .../org/jclouds/aws/ec2/AWSEC2Client.java | 7 + .../BindResourceIdsToIndexedFormParams.java | 42 ++++ .../BindTagFiltersToIndexedFormParams.java | 46 +++++ .../binders/BindTagsToIndexedFormParams.java | 38 ++++ .../ec2/config/AWSEC2RestClientModule.java | 15 ++ .../java/org/jclouds/aws/ec2/domain/Tag.java | 150 +++++++++++++++ .../org/jclouds/aws/ec2/domain/TagFilter.java | 162 ++++++++++++++++ .../aws/ec2/services/TagAsyncClient.java | 93 +++++++++ .../jclouds/aws/ec2/services/TagClient.java | 85 +++++++++ .../org/jclouds/aws/ec2/util/TagFilters.java | 180 ++++++++++++++++++ .../ec2/xml/DescribeTagsResponseHandler.java | 73 +++++++ .../org/jclouds/aws/ec2/xml/TagsHandler.java | 57 ++++++ .../aws/ec2/AWSEC2AsyncClientTest.java | 2 + ...indResourceIdsToIndexedFormParamsTest.java | 62 ++++++ ...BindTagFiltersToIndexedFormParamsTest.java | 78 ++++++++ .../BindTagsToIndexedFormParamsTest.java | 62 ++++++ .../aws/ec2/services/TagAsyncClientTest.java | 121 ++++++++++++ .../aws/ec2/services/TagClientLiveTest.java | 151 +++++++++++++++ 20 files changed, 1450 insertions(+) create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindResourceIdsToIndexedFormParams.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParams.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/Tag.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindResourceIdsToIndexedFormParamsTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParamsTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParamsTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java diff --git a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java index 2d8e880f19..a51bc0a3de 100644 --- a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java +++ b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java @@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG; +import java.util.Map; + import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; @@ -146,6 +148,23 @@ public class AWSUtils { return forms.size() == 0 ? request : ModifyRequest.putFormParams(request, forms); } + public static R indexMapToFormValuesWithPrefix(R request, String prefix, String keySuffix, String valueSuffix, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map: " + + input.getClass()); + Map map = (Map) input; + Builder builder = ImmutableMultimap. builder(); + int i = 1; + for (Map.Entry e : map.entrySet()) { + builder.put(prefix + "." + i + "." + keySuffix, checkNotNull(e.getKey().toString(), keySuffix.toLowerCase() + "s[" + i + "]")); + if (e.getValue() != null) { + builder.put(prefix + "." + i + "." + valueSuffix, e.getValue().toString()); + } + i++; + } + ImmutableMultimap forms = builder.build(); + return forms.size() == 0 ? request : ModifyRequest.putFormParams(request, forms); + } + public static String getRegionFromLocationOrNull(Location location) { return location.getScope() == LocationScope.ZONE ? location.getParent().getId() : location.getId(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java index bf2681362b..1a1c7d5a13 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java @@ -25,6 +25,7 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupAsyncClient; import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; import org.jclouds.aws.ec2.services.SpotInstanceAsyncClient; +import org.jclouds.aws.ec2.services.TagAsyncClient; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.rest.annotations.Delegate; @@ -81,4 +82,10 @@ public interface AWSEC2AsyncClient extends EC2AsyncClient { */ @Delegate SpotInstanceAsyncClient getSpotInstanceServices(); + + /** + * Provides asynchronous access to SpotInstance services. + */ + @Delegate + TagAsyncClient getTagServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java index 0f586a6480..d4e1c984a1 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java @@ -27,6 +27,7 @@ import org.jclouds.aws.ec2.services.AWSSecurityGroupClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.PlacementGroupClient; import org.jclouds.aws.ec2.services.SpotInstanceClient; +import org.jclouds.aws.ec2.services.TagClient; import org.jclouds.concurrent.Timeout; import org.jclouds.ec2.EC2Client; import org.jclouds.rest.annotations.Delegate; @@ -84,4 +85,10 @@ public interface AWSEC2Client extends EC2Client { */ @Delegate SpotInstanceClient getSpotInstanceServices(); + + /** + * Provides synchronous access to Tag services. + */ + @Delegate + TagClient getTagServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindResourceIdsToIndexedFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindResourceIdsToIndexedFormParams.java new file mode 100644 index 0000000000..0f427a5a58 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindResourceIdsToIndexedFormParams.java @@ -0,0 +1,42 @@ +/** + * 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.aws.ec2.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Singleton; + +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +/** + * Binds Ids to query parameters named with ResourceId.index + * + * @author grkvlt@apache.org + */ +@Singleton +public class BindResourceIdsToIndexedFormParams implements Binder { + @Override + public R bindToRequest(R request, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Iterable, "this binder is only valid for Iterable"); + return AWSUtils.indexIterableToFormValuesWithPrefix(request, "ResourceId", input); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java new file mode 100644 index 0000000000..41489c70d9 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java @@ -0,0 +1,46 @@ +/** + * 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.aws.ec2.binders; + +import static com.google.common.base.Preconditions.*; + +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.util.TagFilters; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.utils.ModifyRequest; +import org.jclouds.rest.Binder; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableMultimap.Builder; + +/** + * @author grkvlt@apache.org + */ +public class BindTagFiltersToIndexedFormParams implements Binder { + @SuppressWarnings("unchecked") + @Override + public R bindToRequest(R request, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Iterable, "this binder is only valid for Iterable"); + Builder headers = ImmutableMultimap. builder(); + int index = 1; + for (TagFilter filter : (Iterable) input) + headers.putAll(TagFilters.buildFormParametersForIndex(index++, filter)); + return ModifyRequest.putFormParams(request, headers.build()); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParams.java new file mode 100644 index 0000000000..7dd8069b2b --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParams.java @@ -0,0 +1,38 @@ +/** + * 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.aws.ec2.binders; + +import static com.google.common.base.Preconditions.*; + +import java.util.Map; + +import org.jclouds.aws.util.AWSUtils; +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.Binder; + +/** + * @author grkvlt@apache.org + */ +public class BindTagsToIndexedFormParams implements Binder { + @Override + public R bindToRequest(R request, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Map, "This binder is only valid for Map"); + return AWSUtils.indexMapToFormValuesWithPrefix(request, "Tag", "Key", "Value", input); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java index 7c4d8ecd3a..a5b4c7105f 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java @@ -40,6 +40,8 @@ import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; import org.jclouds.aws.ec2.services.PlacementGroupClient; import org.jclouds.aws.ec2.services.SpotInstanceAsyncClient; import org.jclouds.aws.ec2.services.SpotInstanceClient; +import org.jclouds.aws.ec2.services.TagAsyncClient; +import org.jclouds.aws.ec2.services.TagClient; import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.EC2Client; import org.jclouds.ec2.config.EC2RestClientModule; @@ -86,6 +88,7 @@ public class AWSEC2RestClientModule extends EC2RestClientModule + * @author grkvlt@apache.org + */ +public class Tag implements Comparable { + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String resourceId; + private String resourceType; + private String key; + private String value; + + public void clear() { + this.resourceId = null; + this.resourceType = null; + this.key = null; + this.value = null; + } + + public Builder resourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + + public Builder resourceType(String resourceType) { + this.resourceType = resourceType; + return this; + } + + public Builder key(String key) { + this.key = key; + return this; + } + + public Builder value(String value) { + this.value = value; + return this; + } + + public Tag build() { + return new Tag(resourceId, resourceType, key, value); + } + } + + private final String resourceId; + private final String resourceType; + private final String key; + private final String value; + + public Tag(String resourceId, String resourceType, String key, String value) { + this.resourceId = checkNotNull(resourceId, "resourceId"); + this.resourceType = checkNotNull(resourceType, "resourceType"); + this.key = checkNotNull(key, "key"); + this.value = checkNotNull(value, "value"); + } + + public String getResourceId() { + return resourceId; + } + + public String getResourceType() { + return resourceType; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + @Override + public int compareTo(Tag t) { + return key.compareTo(t.key); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((resourceId == null) ? 0 : resourceId.hashCode()); + result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + result = prime * result + ((value == null) ? 0 : value.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Tag other = (Tag) obj; + if (resourceId == null) { + if (other.resourceId != null) + return false; + } else if (!resourceId.equals(other.resourceId)) + return false; + if (resourceType == null) { + if (other.resourceType != null) + return false; + } else if (!resourceType.equals(other.resourceType)) + return false; + if (value == null) { + if (other.value != null) + return false; + } else if (!value.equals(other.value)) + return false; + if (key == null) { + if (other.key != null) + return false; + } else if (!key.equals(other.key)) + return false; + return true; + } + + @Override + public String toString() { + return "[resourceId=" + resourceId + ", resourceType=" + resourceType + ", key=" + key + ", value=" + value + "]"; + } +} \ No newline at end of file diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java new file mode 100644 index 0000000000..05d6915361 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java @@ -0,0 +1,162 @@ +/** + * 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.aws.ec2.domain; + +import static com.google.common.base.Preconditions.*; + +import java.util.Collection; + +import com.google.common.base.CaseFormat; +import com.google.common.collect.ImmutableList; + +/** + * tag filter. + * + * @see + * @author grkvlt@apache.org + */ +public class TagFilter implements Comparable { + public static enum FilterName { + KEY, + RESOURCE_ID, + RESOURCE_TYPE, + VALUE; + + public String value() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); + } + + @Override + public String toString() { + return value(); + } + + public static FilterName fromValue(String name) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); + } catch (IllegalArgumentException e) { + return null; + } + } + } + + public static enum ResourceType { + CUSTOMER_GATEWAY, + DHCP_OPTIONS, + IMAGE, + INSTANCE, + INTERNET_GATEWAY, + NETWORK_ACL, + RESERVED_INSTANCES, + ROUTE_TABLE, + SECURITY_GROUP, + SNAPSHOT, + SPOT_INSTANCES_REQUEST, + SUBNET, + VOLUME, + VPC, + VPN_CONNECTION, + VPN_GATEWAY; + + public String value() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); + } + + @Override + public String toString() { + return value(); + } + + public static ResourceType fromValue(String name) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); + } catch (IllegalArgumentException e) { + return null; + } + } + } + + private final FilterName name; + private final Collection values; + + public TagFilter(FilterName name, Collection values) { + this.name = checkNotNull(name, "name"); + this.values = checkNotNull(values, "values"); + } + + @Override + public int compareTo(TagFilter o) { + return name.compareTo(o.name); + } + + /** + * @return Name of the filter type. + */ + public FilterName getName() { + return name; + } + + /** + * @return Values to filter on. + */ + public Collection getValues() { + return values; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((values == null) ? 0 : values.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TagFilter other = (TagFilter) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (values == null) { + if (other.values != null) + return false; + } else if (!values.equals(other.values)) + return false; + return true; + } + + @Override + public String toString() { + return "[name=" + name + ", values=" + values + "]"; + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java new file mode 100644 index 0000000000..812a387a9c --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java @@ -0,0 +1,93 @@ +/** + * 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.aws.ec2.services; + +import static org.jclouds.aws.reference.FormParameters.*; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.binders.BindResourceIdsToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindTagFiltersToIndexedFormParams; +import org.jclouds.aws.ec2.binders.BindTagsToIndexedFormParams; +import org.jclouds.aws.ec2.domain.Tag; +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.FormParams; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Provides access to EC2 Tags via their REST API. + * + * @author grkvlt@apache.org + */ +@RequestFilters(FormSigner.class) +@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION) +@VirtualHost +public interface TagAsyncClient { + /** + * @see TagClient#createTagsInRegion(String, Collection, Map) + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "CreateTags") + ListenableFuture createTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindResourceIdsToIndexedFormParams.class) Collection resourceIds, + @BinderParam(BindTagsToIndexedFormParams.class) Map tags); + + /** + * @see TagClient#deleteTagsInRegion(String, Collection, Map) + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DeleteTags") + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + ListenableFuture deleteTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindResourceIdsToIndexedFormParams.class) Collection resourceIds, + @BinderParam(BindTagsToIndexedFormParams.class) Map tags); + + /** + * @see TagClient#describeTagsInRegion(String, Collection) + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "DescribeTags") + @XMLResponseParser(DescribeTagsResponseHandler.class) + @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) + ListenableFuture> describeTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @BinderParam(BindTagFiltersToIndexedFormParams.class) Collection filters); +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java new file mode 100644 index 0000000000..d14658d227 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java @@ -0,0 +1,85 @@ +/** + * 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.aws.ec2.services; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.jclouds.aws.ec2.domain.Tag; +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.concurrent.Timeout; +import org.jclouds.javax.annotation.Nullable; + +/** + * Provides Tag services for EC2. For more information, refer to the Amazon EC2 + * Developer Guide. + * + * @author grkvlt@apache.org + */ +@Timeout(duration = 45, timeUnit = TimeUnit.SECONDS) +public interface TagClient { + /** + * Creates tags. + * + * @param region + * Region to create the tag in. + * @param resourceIds + * IDs of the resources to tag. + * @param tags + * The tags to create. + * @see #describeTagsInRegion(String, Collection) + * @see #deleteTagsInRegion(String, Collection, Map) + * + * @see + */ + void createTagsInRegion(@Nullable String region, Collection resourceIds, Map tags); + + /** + * Deletes tags. + * + * @param region + * Region to delete the tags from + * @param resourceIds + * IDs of the tagged resources. + * @param tags + * The tags to delete. + * + * @see #describeTagsInRegion(String, Collection) + * @see #createTagsInRegion(String, Collection, Map) + * + * @see + */ + void deleteTagsInRegion(@Nullable String region, Collection resourceIds, Map tags); + + /** + * Returns filtered information about tags. + * + * @param region + * The bundleTask ID is tied to the Region. + * @param filters + * A collection of filters to apply before selecting the tags. + * + * @see #deleteTagsInRegion(String, Collection, Map) + * @see #createTagsInRegion(String, Collection, Map) + * @see + */ + Set describeTagsInRegion(@Nullable String region, Collection filters); +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java new file mode 100644 index 0000000000..8324bcfa62 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java @@ -0,0 +1,180 @@ +/** + * 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.aws.ec2.util; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.domain.TagFilter.FilterName; +import org.jclouds.aws.ec2.domain.TagFilter.ResourceType; + +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Sets; + +/** + * @author grkvlt@apache.org + */ +public class TagFilters { + protected final FilterName name; + protected final Collection values; + + protected TagFilters(FilterName name, Collection values) { + this.name = name; + this.values = values != null ? values : Sets.newHashSet(); + } + + public static Multimap buildFormParametersForIndex(int index, TagFilter filter) { + Map headers = Maps.newLinkedHashMap(); + headers.put(String.format("Filter.%d.Name", index), filter.getName().value()); + int i = 0; + for (String value : filter.getValues()) { + headers.put(String.format("Filter.%d.Value.%d", index, ++i), value); + } + return Multimaps.forMap(headers); + } + + public static StringTagFilter key() { + return new StringTagFilter(FilterName.KEY); + } + + public static StringTagFilter resourceId() { + return new StringTagFilter(FilterName.RESOURCE_ID); + } + + public static ResourceTypeTagFilter resourceType() { + return new ResourceTypeTagFilter(); + } + + public static NamedTagFilter value() { + return new StringTagFilter(FilterName.VALUE); + } + + public static class NamedTagFilter extends TagFilters { + public NamedTagFilter(FilterName name) { + super(name, null); + } + + public TagFilter filter() { + return new TagFilter(name, values); + } + } + + public static class StringTagFilter extends NamedTagFilter { + public StringTagFilter(FilterName name) { + super(name); + } + + public StringTagFilter exact(String value) { + return value(value); + } + + public StringTagFilter contains(String value) { + return value(String.format("*%s*", value)); + } + + public StringTagFilter value(String value) { + this.values.add(value); + return this; + } + + public StringTagFilter values(String... values) { + this.values.addAll(Arrays.asList(values)); + return this; + } + } + + public static class ResourceTypeTagFilter extends NamedTagFilter { + public ResourceTypeTagFilter() { + super(FilterName.RESOURCE_TYPE); + } + + public ResourceTypeTagFilter resourceType(ResourceType resourceType) { + values.add(resourceType.value()); + return this; + } + + public ResourceTypeTagFilter customerGateway() { + return resourceType(ResourceType.CUSTOMER_GATEWAY); + } + + public ResourceTypeTagFilter dhcpOptions() { + return resourceType(ResourceType.DHCP_OPTIONS); + } + + public ResourceTypeTagFilter image() { + return resourceType(ResourceType.IMAGE); + } + + public ResourceTypeTagFilter instance() { + return resourceType(ResourceType.INSTANCE); + } + + public ResourceTypeTagFilter internetGateway() { + return resourceType(ResourceType.INTERNET_GATEWAY); + } + + public ResourceTypeTagFilter networkAcl() { + return resourceType(ResourceType.NETWORK_ACL); + } + + public ResourceTypeTagFilter reservedInstance() { + return resourceType(ResourceType.RESERVED_INSTANCES); + } + + public ResourceTypeTagFilter routeTable() { + return resourceType(ResourceType.ROUTE_TABLE); + } + + public ResourceTypeTagFilter securityGroup() { + return resourceType(ResourceType.SECURITY_GROUP); + } + + public ResourceTypeTagFilter snapshot() { + return resourceType(ResourceType.SNAPSHOT); + } + + public ResourceTypeTagFilter instancesRequest() { + return resourceType(ResourceType.SPOT_INSTANCES_REQUEST); + } + + public ResourceTypeTagFilter subnet() { + return resourceType(ResourceType.SUBNET); + } + + public ResourceTypeTagFilter volume() { + return resourceType(ResourceType.VOLUME); + } + + public ResourceTypeTagFilter vpc() { + return resourceType(ResourceType.VPC); + } + + public ResourceTypeTagFilter vpnConnection() { + return resourceType(ResourceType.VPN_CONNECTION); + } + + public ResourceTypeTagFilter vpnGateway() { + return resourceType(ResourceType.VPN_GATEWAY); + } + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java new file mode 100644 index 0000000000..1d6cbd3948 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/DescribeTagsResponseHandler.java @@ -0,0 +1,73 @@ +/** + * 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.aws.ec2.xml; + +import java.util.Set; + +import javax.inject.Inject; + +import org.jclouds.aws.ec2.domain.Tag; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.HandlerWithResult; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import com.google.common.collect.Sets; + +/** + * @author grkvlt@apache.org + */ +public class DescribeTagsResponseHandler extends ParseSax.HandlerWithResult> { + private Set bundleTasks = Sets.newLinkedHashSet(); + private final TagsHandler bundleTaskHandler; + + @Inject + public DescribeTagsResponseHandler(TagsHandler bundleTaskHandler) { + this.bundleTaskHandler = bundleTaskHandler; + } + + public Set getResult() { + return bundleTasks; + } + + @Override + public HandlerWithResult> setContext(HttpRequest request) { + bundleTaskHandler.setContext(request); + return super.setContext(request); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (!qName.equals("item")) + bundleTaskHandler.startElement(uri, localName, qName, attributes); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (qName.equals("item")) { + bundleTasks.add(bundleTaskHandler.getResult()); + } + bundleTaskHandler.endElement(uri, localName, qName); + } + + public void characters(char ch[], int start, int length) { + bundleTaskHandler.characters(ch, start, length); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java new file mode 100644 index 0000000000..c710573e38 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java @@ -0,0 +1,57 @@ +/** + * 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.aws.ec2.xml; + +import org.jclouds.aws.ec2.domain.Tag; +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.http.functions.ParseSax; + +/** + * @author grkvlt@apache.org + */ +public class TagsHandler extends ParseSax.HandlerForGeneratedRequestWithResult { + private StringBuilder currentText = new StringBuilder(); + + private String resourceId; + private TagFilter.ResourceType resourceType; + private String key; + private String value; + + public Tag getResult() { + Tag returnVal = new Tag(resourceId, resourceType.value(), key, value); + return returnVal; + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("resourceId")) { + this.resourceId = currentText.toString().trim(); + } else if (qName.equals("resourceType")) { + resourceType = TagFilter.ResourceType.fromValue(currentText.toString().trim()); + } else if (qName.equals("key")) { + key = currentText.toString().trim(); + } else if (qName.equals("value")) { + value = currentText.toString().trim(); + } + currentText = new StringBuilder(); + } + + public void characters(char ch[], int start, int length) { + currentText.append(ch, start, length); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java index b522bcef82..ac240cffa1 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/AWSEC2AsyncClientTest.java @@ -52,6 +52,7 @@ public class AWSEC2AsyncClientTest extends BaseAWSEC2AsyncClientTestbuilder().add(TagFilter.ResourceType.VPN_GATEWAY.value()).add(TagFilter.ResourceType.INTERNET_GATEWAY.value()).build())).build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=resource-type&Filter.1.Value.1=vpn-gateway&Filter.1.Value.2=internet-gateway"); + } + + public void testKeyWithValues() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, ImmutableList.builder().add(new TagFilter(FilterName.KEY, + ImmutableList.builder().add("one").add("two").build())).build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two"); + } + + public void testKeyWithoutValues() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, ImmutableList.builder().add(new TagFilter(FilterName.KEY, ImmutableList.of())).build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMustBeArray() { + HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); + binder.bindToRequest(request, new File("foo")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullIsBad() { + HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(); + binder.bindToRequest(request, null); + } +} \ No newline at end of file diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParamsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParamsTest.java new file mode 100644 index 0000000000..3f32a31283 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagsToIndexedFormParamsTest.java @@ -0,0 +1,62 @@ +/** + * 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.aws.ec2.binders; + +import static org.testng.Assert.*; + +import java.io.File; +import java.net.URI; + +import javax.ws.rs.HttpMethod; + +import org.jclouds.http.HttpRequest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code BindTagsToIndexedFormParams} + * + * @author grkvlt@apache.org + */ +@Test(groups = "unit") +public class BindTagsToIndexedFormParamsTest { + Injector injector = Guice.createInjector(); + BindTagsToIndexedFormParams binder = injector.getInstance(BindTagsToIndexedFormParams.class); + + public void test() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, ImmutableMap.builder().put("one", "alpha").put("two", "beta").build()); + assertEquals(request.getPayload().getRawContent(), "Tag.1.Key=one&Tag.1.Value=alpha&Tag.2.Key=two&Tag.2.Value=beta"); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testMustBeArray() { + HttpRequest request = new HttpRequest(HttpMethod.POST, URI.create("http://localhost")); + binder.bindToRequest(request, new File("foo")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testNullIsBad() { + HttpRequest request = HttpRequest.builder().method("GET").endpoint(URI.create("http://momma")).build(); + binder.bindToRequest(request, null); + } +} \ No newline at end of file diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java new file mode 100644 index 0000000000..87feb0a062 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java @@ -0,0 +1,121 @@ +/** + * 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.aws.ec2.services; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.domain.TagFilter.FilterName; +import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ReleasePayloadAndReturn; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code TagsAsyncClient} + * + * @author Adrian Cole + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +@Test(groups = "unit", testName = "TagsAsyncClientTest") +public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTest { + + public void testDeleteTags() throws SecurityException, NoSuchMethodException, IOException { + Method method = TagAsyncClient.class.getMethod("deleteTagsInRegion", String.class, Collection.class, Map.class); + HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2011-05-15&Action=DeleteTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, ReturnVoidOnNotFoundOr404.class); + + checkFilters(request); + } + + public void testCreateTags() throws SecurityException, NoSuchMethodException, IOException { + Method method = TagAsyncClient.class.getMethod("createTagsInRegion", String.class, Collection.class, Map.class); + HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2011-05-15&Action=CreateTags&Tag.1.Key=yyy&Tag.1.Value=zzz&ResourceId.1=xxx", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testDescribeTags() throws SecurityException, NoSuchMethodException, IOException { + Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Collection.class); + HttpRequest request = processor.createRequest(method, null, ImmutableList.of()); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeTags", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeTagsResponseHandler.class); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(request); + } + + public void testDescribeTagsArgs() throws SecurityException, NoSuchMethodException, IOException { + Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Collection.class); + HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add(new TagFilter(FilterName.KEY, + ImmutableList.builder().add("one").add("two").build())).build()); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2011-05-15&Action=DescribeTags&Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeTagsResponseHandler.class); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(request); + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } + +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java new file mode 100644 index 0000000000..ea6d933ce4 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java @@ -0,0 +1,151 @@ +/** + * 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.aws.ec2.services; + +import static com.google.common.base.Preconditions.*; +import static com.google.common.collect.Iterables.*; +import static org.testng.Assert.*; + +import java.util.Properties; +import java.util.Set; + +import org.jclouds.Constants; +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.aws.ec2.domain.AWSRunningInstance; +import org.jclouds.aws.ec2.domain.Tag; +import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.domain.TagFilter.FilterName; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.RestContext; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Module; + +/** + * Tests live behavior of {@code TagClient} + * + * @author grkvlt@apache.org + */ +@Test(groups = "live", sequential = true) +public class TagClientLiveTest { + + private TagClient client; + private RestContext context; + + protected String provider = "aws-ec2"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + protected String testGroup; + private ComputeServiceContext computeContext; + + protected void setupCredentials() { + identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); + credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider + ".credential"); + endpoint = System.getProperty("test." + provider + ".endpoint", null); + apiversion = System.getProperty("test." + provider + ".apiversion", null); + } + + protected Properties setupProperties() { + Properties overrides = new Properties(); + overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); + overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true"); + overrides.setProperty(provider + ".identity", identity); + overrides.setProperty(provider + ".credential", credential); + if (endpoint != null) + overrides.setProperty(provider + ".endpoint", endpoint); + if (apiversion != null) + overrides.setProperty(provider + ".apiversion", apiversion); + return overrides; + } + + @BeforeGroups(groups = { "live" }) + public void setupClientAndSecurityGroup() { + setupCredentials(); + Properties overrides = setupProperties(); + computeContext = new ComputeServiceContextFactory().createContext(provider, ImmutableSet. of( + new Log4JLoggingModule(), new SshjSshClientModule()), overrides); + context = computeContext.getProviderSpecificContext(); + client = context.getApi().getTagServices(); + testGroup = context.getApi().getSecurityGroupServices().createSecurityGroupInRegionAndReturnId(null, "test-group", "test-group"); + } + + @AfterGroups(groups = { "live" }) + public void deleteSecurityGroup() { + context.getApi().getSecurityGroupServices().deleteSecurityGroupInRegionById(null, testGroup); + } + + public static final String PREFIX = System.getProperty("user.name") + "-ec2"; + + @Test + void test() { + cleanupTag(testGroup, "test-key"); + try { + client.createTagsInRegion(null, ImmutableList.builder().add(testGroup).build(), ImmutableMap.builder().put("test-key", "test-value").build()); + checkTag(testGroup, TagFilter.ResourceType.SECURITY_GROUP.value(), "test-key", "test-value"); + } finally { + cleanupTag(testGroup, "test-key"); + } + } + + protected void cleanupTag(String resourceId, String key) { + try { + client.deleteTagsInRegion(null, ImmutableList.builder().add(resourceId).build(), ImmutableMap.builder().put(key, null).build()); + } catch (Exception e) { + // Ignore + } + } + + protected void checkTag(String resourceId, String resourceType, String key, String value) { + Set results = client.describeTagsInRegion(null, ImmutableList.builder() + .add(new TagFilter(FilterName.RESOURCE_ID, ImmutableList.builder().add(resourceId).build())) + .add(new TagFilter(FilterName.RESOURCE_TYPE, ImmutableList.builder().add(resourceType).build())) + .add(new TagFilter(FilterName.KEY, ImmutableList.builder().add(key).build())) + .add(new TagFilter(FilterName.VALUE, ImmutableList.builder().add(value).build())) + .build()); + assertNotNull(results); + assertEquals(results.size(), 1); + Tag tag = Iterables.getOnlyElement(results); + assertEquals(tag.getResourceId(), resourceId); + assertEquals(tag.getResourceType(), resourceType); + assertEquals(tag.getKey(), key); + assertEquals(tag.getValue(), value); + } + + protected AWSRunningInstance getInstance(AWSInstanceClient instanceClient, String id) { + return getOnlyElement(getOnlyElement(instanceClient.describeInstancesInRegion(null, id))); + } + + @AfterTest + public void shutdown() { + context.close(); + } +} From d4f71400712869d5f94f67d01341154eaa5446f4 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Wed, 5 Oct 2011 17:34:30 +0100 Subject: [PATCH 2/3] 612: Changed Tag filters to use Multimap and Collection to Iterable --- .../java/org/jclouds/aws/util/AWSUtils.java | 34 +- .../BindTagFiltersToIndexedFormParams.java | 16 +- .../java/org/jclouds/aws/ec2/domain/Tag.java | 32 +- .../org/jclouds/aws/ec2/domain/TagFilter.java | 162 -------- .../aws/ec2/services/TagAsyncClient.java | 17 +- .../jclouds/aws/ec2/services/TagClient.java | 23 +- .../org/jclouds/aws/ec2/util/TagFilters.java | 347 ++++++++++++------ .../org/jclouds/aws/ec2/xml/TagsHandler.java | 8 +- ...BindTagFiltersToIndexedFormParamsTest.java | 51 ++- .../aws/ec2/services/TagAsyncClientTest.java | 23 +- .../aws/ec2/services/TagClientLiveTest.java | 19 +- 11 files changed, 360 insertions(+), 372 deletions(-) delete mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java diff --git a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java index a51bc0a3de..f8c5201026 100644 --- a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java +++ b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java @@ -47,6 +47,7 @@ import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.Multimap; /** * Needed to sign and verify requests and responses. @@ -68,6 +69,7 @@ public class AWSUtils { private final Provider errorHandlerProvider; private final String requestId; private final String requestToken; + @Resource protected Logger logger = Logger.NULL; @@ -140,7 +142,7 @@ public class AWSUtils { checkArgument(checkNotNull(input, "input") instanceof String[], "this binder is only valid for String[] : " + input.getClass()); String[] values = (String[]) input; - Builder builder = ImmutableMultimap. builder(); + Builder builder = ImmutableMultimap.builder(); for (int i = 0; i < values.length; i++) { builder.put(prefix + "." + (i + 1), checkNotNull(values[i], prefix.toLowerCase() + "s[" + i + "]")); } @@ -149,15 +151,35 @@ public class AWSUtils { } public static R indexMapToFormValuesWithPrefix(R request, String prefix, String keySuffix, String valueSuffix, Object input) { - checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map: " - + input.getClass()); - Map map = (Map) input; + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map: " + input.getClass()); + Map map = (Map) input; Builder builder = ImmutableMultimap. builder(); int i = 1; - for (Map.Entry e : map.entrySet()) { + for (Map.Entry e : map.entrySet()) { builder.put(prefix + "." + i + "." + keySuffix, checkNotNull(e.getKey().toString(), keySuffix.toLowerCase() + "s[" + i + "]")); if (e.getValue() != null) { - builder.put(prefix + "." + i + "." + valueSuffix, e.getValue().toString()); + builder.put(prefix + "." + i + "." + valueSuffix, e.getValue().toString()); + } + i++; + } + ImmutableMultimap forms = builder.build(); + return forms.size() == 0 ? request : ModifyRequest.putFormParams(request, forms); + } + + @SuppressWarnings("unchecked") + public static R indexMultimapToFormValuesWithPrefix(R request, String prefix, String keySuffix, String valueSuffix, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Multimap, "this binder is only valid for Multimap: " + input.getClass()); + Multimap map = (Multimap) input; + Builder builder = ImmutableMultimap. builder(); + int i = 1; + for (Object k : map.keySet()) { + builder.put(prefix + "." + i + "." + keySuffix, checkNotNull(k.toString(), keySuffix.toLowerCase() + "s[" + i + "]")); + if (!map.get(k).isEmpty()) { + int j = 1; + for (Object v : map.get(k)) { + builder.put(prefix + "." + i + "." + valueSuffix + "." + j, v.toString()); + j++; + } } i++; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java index 41489c70d9..1058734569 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java @@ -20,27 +20,19 @@ package org.jclouds.aws.ec2.binders; import static com.google.common.base.Preconditions.*; -import org.jclouds.aws.ec2.domain.TagFilter; -import org.jclouds.aws.ec2.util.TagFilters; +import org.jclouds.aws.util.AWSUtils; import org.jclouds.http.HttpRequest; -import org.jclouds.http.utils.ModifyRequest; import org.jclouds.rest.Binder; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.Multimap; /** * @author grkvlt@apache.org */ public class BindTagFiltersToIndexedFormParams implements Binder { - @SuppressWarnings("unchecked") @Override public R bindToRequest(R request, Object input) { - checkArgument(checkNotNull(input, "input") instanceof Iterable, "this binder is only valid for Iterable"); - Builder headers = ImmutableMultimap. builder(); - int index = 1; - for (TagFilter filter : (Iterable) input) - headers.putAll(TagFilters.buildFormParametersForIndex(index++, filter)); - return ModifyRequest.putFormParams(request, headers.build()); + checkArgument(checkNotNull(input, "input") instanceof Multimap, "this binder is only valid for Multimap"); + return AWSUtils.indexMultimapToFormValuesWithPrefix(request, "Filter", "Name", "Value", input); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/Tag.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/Tag.java index 19fc35c48d..0951ece527 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/Tag.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/Tag.java @@ -18,7 +18,12 @@ */ package org.jclouds.aws.ec2.domain; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.*; + +import org.jclouds.aws.ec2.util.TagFilters.ResourceType; + +import com.google.common.base.Objects; +import com.google.common.collect.ComparisonChain; /** * @see @@ -31,7 +36,7 @@ public class Tag implements Comparable { public static class Builder { private String resourceId; - private String resourceType; + private ResourceType resourceType; private String key; private String value; @@ -47,7 +52,7 @@ public class Tag implements Comparable { return this; } - public Builder resourceType(String resourceType) { + public Builder resourceType(ResourceType resourceType) { this.resourceType = resourceType; return this; } @@ -68,11 +73,11 @@ public class Tag implements Comparable { } private final String resourceId; - private final String resourceType; + private final ResourceType resourceType; private final String key; private final String value; - public Tag(String resourceId, String resourceType, String key, String value) { + public Tag(String resourceId, ResourceType resourceType, String key, String value) { this.resourceId = checkNotNull(resourceId, "resourceId"); this.resourceType = checkNotNull(resourceType, "resourceType"); this.key = checkNotNull(key, "key"); @@ -83,7 +88,7 @@ public class Tag implements Comparable { return resourceId; } - public String getResourceType() { + public ResourceType getResourceType() { return resourceType; } @@ -97,18 +102,17 @@ public class Tag implements Comparable { @Override public int compareTo(Tag t) { - return key.compareTo(t.key); + return ComparisonChain.start() + .compare(resourceId, t.resourceId) + .compare(resourceType, t.resourceType) + .compare(key, t.key) + .compare(value, t.value) + .result(); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((resourceId == null) ? 0 : resourceId.hashCode()); - result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); - return result; + return Objects.hashCode(resourceId, resourceType, key, value); } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java deleted file mode 100644 index 05d6915361..0000000000 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/TagFilter.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * 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.aws.ec2.domain; - -import static com.google.common.base.Preconditions.*; - -import java.util.Collection; - -import com.google.common.base.CaseFormat; -import com.google.common.collect.ImmutableList; - -/** - * tag filter. - * - * @see - * @author grkvlt@apache.org - */ -public class TagFilter implements Comparable { - public static enum FilterName { - KEY, - RESOURCE_ID, - RESOURCE_TYPE, - VALUE; - - public String value() { - return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); - } - - @Override - public String toString() { - return value(); - } - - public static FilterName fromValue(String name) { - try { - return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); - } catch (IllegalArgumentException e) { - return null; - } - } - } - - public static enum ResourceType { - CUSTOMER_GATEWAY, - DHCP_OPTIONS, - IMAGE, - INSTANCE, - INTERNET_GATEWAY, - NETWORK_ACL, - RESERVED_INSTANCES, - ROUTE_TABLE, - SECURITY_GROUP, - SNAPSHOT, - SPOT_INSTANCES_REQUEST, - SUBNET, - VOLUME, - VPC, - VPN_CONNECTION, - VPN_GATEWAY; - - public String value() { - return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); - } - - @Override - public String toString() { - return value(); - } - - public static ResourceType fromValue(String name) { - try { - return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); - } catch (IllegalArgumentException e) { - return null; - } - } - } - - private final FilterName name; - private final Collection values; - - public TagFilter(FilterName name, Collection values) { - this.name = checkNotNull(name, "name"); - this.values = checkNotNull(values, "values"); - } - - @Override - public int compareTo(TagFilter o) { - return name.compareTo(o.name); - } - - /** - * @return Name of the filter type. - */ - public FilterName getName() { - return name; - } - - /** - * @return Values to filter on. - */ - public Collection getValues() { - return values; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((values == null) ? 0 : values.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - TagFilter other = (TagFilter) obj; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (values == null) { - if (other.values != null) - return false; - } else if (!values.equals(other.values)) - return false; - return true; - } - - @Override - public String toString() { - return "[name=" + name + ", values=" + values + "]"; - } -} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java index 812a387a9c..5b1ffd36c9 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java @@ -20,8 +20,6 @@ package org.jclouds.aws.ec2.services; import static org.jclouds.aws.reference.FormParameters.*; -import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Set; @@ -33,7 +31,7 @@ import org.jclouds.aws.ec2.binders.BindResourceIdsToIndexedFormParams; import org.jclouds.aws.ec2.binders.BindTagFiltersToIndexedFormParams; import org.jclouds.aws.ec2.binders.BindTagsToIndexedFormParams; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.util.TagFilters; import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; import org.jclouds.aws.filters.FormSigner; import org.jclouds.javax.annotation.Nullable; @@ -48,6 +46,7 @@ import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; +import com.google.common.collect.Multimap; import com.google.common.util.concurrent.ListenableFuture; /** @@ -60,28 +59,28 @@ import com.google.common.util.concurrent.ListenableFuture; @VirtualHost public interface TagAsyncClient { /** - * @see TagClient#createTagsInRegion(String, Collection, Map) + * @see TagClient#createTagsInRegion(String, Iterable, Map) */ @POST @Path("/") @FormParams(keys = ACTION, values = "CreateTags") ListenableFuture createTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindResourceIdsToIndexedFormParams.class) Collection resourceIds, + @BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable resourceIds, @BinderParam(BindTagsToIndexedFormParams.class) Map tags); /** - * @see TagClient#deleteTagsInRegion(String, Collection, Map) + * @see TagClient#deleteTagsInRegion(String, Iterable, Map) */ @POST @Path("/") @FormParams(keys = ACTION, values = "DeleteTags") @ExceptionParser(ReturnVoidOnNotFoundOr404.class) ListenableFuture deleteTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindResourceIdsToIndexedFormParams.class) Collection resourceIds, + @BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable resourceIds, @BinderParam(BindTagsToIndexedFormParams.class) Map tags); /** - * @see TagClient#describeTagsInRegion(String, Collection) + * @see TagClient#describeTagsInRegion(String, Multimap) */ @POST @Path("/") @@ -89,5 +88,5 @@ public interface TagAsyncClient { @XMLResponseParser(DescribeTagsResponseHandler.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture> describeTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindTagFiltersToIndexedFormParams.class) Collection filters); + @BinderParam(BindTagFiltersToIndexedFormParams.class) Multimap filters); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java index d14658d227..8bf144f373 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java @@ -18,16 +18,17 @@ */ package org.jclouds.aws.ec2.services; -import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.util.TagFilters; import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; +import com.google.common.collect.Multimap; + /** * Provides Tag services for EC2. For more information, refer to the Amazon EC2 * Developer Guide. @@ -45,12 +46,12 @@ public interface TagClient { * IDs of the resources to tag. * @param tags * The tags to create. - * @see #describeTagsInRegion(String, Collection) - * @see #deleteTagsInRegion(String, Collection, Map) + * @see #describeTagsInRegion(String, Multimap) + * @see #deleteTagsInRegion(String, Iterable, Map) * * @see */ - void createTagsInRegion(@Nullable String region, Collection resourceIds, Map tags); + void createTagsInRegion(@Nullable String region, Iterable resourceIds, Map tags); /** * Deletes tags. @@ -62,12 +63,12 @@ public interface TagClient { * @param tags * The tags to delete. * - * @see #describeTagsInRegion(String, Collection) - * @see #createTagsInRegion(String, Collection, Map) + * @see #describeTagsInRegion(String, Multimap) + * @see #createTagsInRegion(String, Iterable, Map) * * @see */ - void deleteTagsInRegion(@Nullable String region, Collection resourceIds, Map tags); + void deleteTagsInRegion(@Nullable String region, Iterable resourceIds, Map tags); /** * Returns filtered information about tags. @@ -77,9 +78,9 @@ public interface TagClient { * @param filters * A collection of filters to apply before selecting the tags. * - * @see #deleteTagsInRegion(String, Collection, Map) - * @see #createTagsInRegion(String, Collection, Map) + * @see #deleteTagsInRegion(String, Iterable, Map) + * @see #createTagsInRegion(String, Iterable, Map) * @see */ - Set describeTagsInRegion(@Nullable String region, Collection filters); + Set describeTagsInRegion(@Nullable String region, Multimap filters); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java index 8324bcfa62..c5406b1342 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java @@ -18,163 +18,268 @@ */ package org.jclouds.aws.ec2.util; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; +import static com.google.common.base.Preconditions.*; -import org.jclouds.aws.ec2.domain.TagFilter; -import org.jclouds.aws.ec2.domain.TagFilter.FilterName; -import org.jclouds.aws.ec2.domain.TagFilter.ResourceType; - -import com.google.common.collect.Maps; +import com.google.common.base.CaseFormat; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; -import com.google.common.collect.Sets; /** * @author grkvlt@apache.org */ public class TagFilters { - protected final FilterName name; - protected final Collection values; + public static enum FilterName { + KEY, + RESOURCE_ID, + RESOURCE_TYPE, + VALUE; - protected TagFilters(FilterName name, Collection values) { - this.name = name; - this.values = values != null ? values : Sets.newHashSet(); - } - - public static Multimap buildFormParametersForIndex(int index, TagFilter filter) { - Map headers = Maps.newLinkedHashMap(); - headers.put(String.format("Filter.%d.Name", index), filter.getName().value()); - int i = 0; - for (String value : filter.getValues()) { - headers.put(String.format("Filter.%d.Value.%d", index, ++i), value); - } - return Multimaps.forMap(headers); - } - - public static StringTagFilter key() { - return new StringTagFilter(FilterName.KEY); - } - - public static StringTagFilter resourceId() { - return new StringTagFilter(FilterName.RESOURCE_ID); - } - - public static ResourceTypeTagFilter resourceType() { - return new ResourceTypeTagFilter(); - } - - public static NamedTagFilter value() { - return new StringTagFilter(FilterName.VALUE); - } - - public static class NamedTagFilter extends TagFilters { - public NamedTagFilter(FilterName name) { - super(name, null); + public String value() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); } - public TagFilter filter() { - return new TagFilter(name, values); + @Override + public String toString() { + return value(); + } + + public static FilterName fromValue(String name) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); + } catch (IllegalArgumentException e) { + return null; + } } } - public static class StringTagFilter extends NamedTagFilter { - public StringTagFilter(FilterName name) { - super(name); + public static enum ResourceType { + CUSTOMER_GATEWAY, + DHCP_OPTIONS, + IMAGE, + INSTANCE, + INTERNET_GATEWAY, + NETWORK_ACL, + RESERVED_INSTANCES, + ROUTE_TABLE, + SECURITY_GROUP, + SNAPSHOT, + SPOT_INSTANCES_REQUEST, + SUBNET, + VOLUME, + VPC, + VPN_CONNECTION, + VPN_GATEWAY; + + public String value() { + return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, name()); } - public StringTagFilter exact(String value) { - return value(value); + @Override + public String toString() { + return value(); } - public StringTagFilter contains(String value) { - return value(String.format("*%s*", value)); - } - - public StringTagFilter value(String value) { - this.values.add(value); - return this; - } - - public StringTagFilter values(String... values) { - this.values.addAll(Arrays.asList(values)); - return this; + public static ResourceType fromValue(String name) { + try { + return valueOf(CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, checkNotNull(name, "name"))); + } catch (IllegalArgumentException e) { + return null; + } } } - public static class ResourceTypeTagFilter extends NamedTagFilter { - public ResourceTypeTagFilter() { - super(FilterName.RESOURCE_TYPE); - } + protected final ImmutableSetMultimap.Builder map; - public ResourceTypeTagFilter resourceType(ResourceType resourceType) { - values.add(resourceType.value()); - return this; - } + protected TagFilters() { + map = ImmutableSetMultimap.builder(); + } - public ResourceTypeTagFilter customerGateway() { - return resourceType(ResourceType.CUSTOMER_GATEWAY); - } + public static TagFilters filters() { + return new TagFilters(); + } - public ResourceTypeTagFilter dhcpOptions() { - return resourceType(ResourceType.DHCP_OPTIONS); - } + public Multimap build() { + return map.build(); + } - public ResourceTypeTagFilter image() { - return resourceType(ResourceType.IMAGE); - } + public TagFilters resourceId(String resourceId) { + map.put(FilterName.RESOURCE_ID, resourceId); + return this; + } - public ResourceTypeTagFilter instance() { - return resourceType(ResourceType.INSTANCE); - } + public TagFilters key(String key) { + map.put(FilterName.KEY, key); + return this; + } - public ResourceTypeTagFilter internetGateway() { - return resourceType(ResourceType.INTERNET_GATEWAY); - } + public TagFilters keys(String...keys) { + map.putAll(FilterName.KEY, ImmutableSet.copyOf(keys)); + return this; + } - public ResourceTypeTagFilter networkAcl() { - return resourceType(ResourceType.NETWORK_ACL); - } + public TagFilters keys(Iterable keys) { + map.putAll(FilterName.KEY, ImmutableSet.copyOf(keys)); + return this; + } - public ResourceTypeTagFilter reservedInstance() { - return resourceType(ResourceType.RESERVED_INSTANCES); - } + public TagFilters value(String value) { + map.put(FilterName.VALUE, value); + return this; + } - public ResourceTypeTagFilter routeTable() { - return resourceType(ResourceType.ROUTE_TABLE); - } + public TagFilters values(String...values) { + map.putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); + return this; + } - public ResourceTypeTagFilter securityGroup() { - return resourceType(ResourceType.SECURITY_GROUP); - } + public TagFilters values(Iterable values) { + map.putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); + return this; + } - public ResourceTypeTagFilter snapshot() { - return resourceType(ResourceType.SNAPSHOT); - } + public TagFilters keyContains(String key) { + return key(String.format("*%s*", key)); + } - public ResourceTypeTagFilter instancesRequest() { - return resourceType(ResourceType.SPOT_INSTANCES_REQUEST); - } + public TagFilters valueContains(String value) { + return value(String.format("*%s*", value)); + } - public ResourceTypeTagFilter subnet() { - return resourceType(ResourceType.SUBNET); - } + public TagFilters resourceIdContains(String value) { + return resourceId(String.format("*%s*", value)); + } - public ResourceTypeTagFilter volume() { - return resourceType(ResourceType.VOLUME); - } + public TagFilters keyStartsWith(String key) { + return key(String.format("%s*", key)); + } - public ResourceTypeTagFilter vpc() { - return resourceType(ResourceType.VPC); - } + public TagFilters valueStartsWith(String value) { + return value(String.format("%s*", value)); + } - public ResourceTypeTagFilter vpnConnection() { - return resourceType(ResourceType.VPN_CONNECTION); - } + public TagFilters resourceIdStartsWith(String value) { + return resourceId(String.format("%s*", value)); + } - public ResourceTypeTagFilter vpnGateway() { - return resourceType(ResourceType.VPN_GATEWAY); - } + public TagFilters keyEndsWith(String key) { + return key(String.format("*%s", key)); + } + + public TagFilters valueEndsWith(String value) { + return value(String.format("*%s", value)); + } + + public TagFilters resourceIdEndsWith(String value) { + return resourceId(String.format("*%s", value)); + } + + public TagFilters keyValuePair(String key, String value) { + return key(key).value(value); + } + + public TagFilters keyValueSet(String key, Iterable values) { + return key(key).values(values); + } + + public TagFilters keyValueSet(String key, String...values) { + return key(key).values(values); + } + + public TagFilters anyKey() { + return key("*"); + } + + public TagFilters anyValue() { + return value("*"); + } + + public TagFilters anyResourceId() { + return resourceId("*"); + } + + public TagFilters anyResourceType() { + map.put(FilterName.RESOURCE_TYPE, "*"); + return this; + } + + public TagFilters customerGateway() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.CUSTOMER_GATEWAY); + return this; + } + + public TagFilters dhcpOptions() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.DHCP_OPTIONS); + return this; + } + + public TagFilters image() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.IMAGE); + return this; + } + + public TagFilters instance() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.INSTANCE); + return this; + } + + public TagFilters internetGateway() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.INTERNET_GATEWAY); + return this; + } + + public TagFilters networkAcl() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.NETWORK_ACL); + return this; + } + + public TagFilters reservedInstance() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.RESERVED_INSTANCES); + return this; + } + + public TagFilters routeTable() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.ROUTE_TABLE); + return this; + } + + public TagFilters securityGroup() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.SECURITY_GROUP); + return this; + } + + public TagFilters snapshot() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.SNAPSHOT); + return this; + } + + public TagFilters instancesRequest() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.SPOT_INSTANCES_REQUEST); + return this; + } + + public TagFilters subnet() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.SUBNET); + return this; + } + + public TagFilters volume() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.VOLUME); + return this; + } + + public TagFilters vpc() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.VPC); + return this; + } + + public TagFilters vpnConnection() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.VPN_CONNECTION); + return this; + } + + public TagFilters vpnGateway() { + map.put(FilterName.RESOURCE_TYPE, ResourceType.VPN_GATEWAY); + return this; } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java index c710573e38..6d2a7a64ef 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/TagsHandler.java @@ -19,7 +19,7 @@ package org.jclouds.aws.ec2.xml; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.domain.TagFilter; +import org.jclouds.aws.ec2.util.TagFilters.ResourceType; import org.jclouds.http.functions.ParseSax; /** @@ -29,12 +29,12 @@ public class TagsHandler extends ParseSax.HandlerForGeneratedRequestWithResultbuilder().add(TagFilter.ResourceType.VPN_GATEWAY.value()).add(TagFilter.ResourceType.INTERNET_GATEWAY.value()).build())).build()); + request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.RESOURCE_TYPE, ResourceType.VPN_GATEWAY).put(FilterName.RESOURCE_TYPE, ResourceType.INTERNET_GATEWAY).build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=resource-type&Filter.1.Value.1=vpn-gateway&Filter.1.Value.2=internet-gateway"); } - public void testKeyWithValues() { + public void testMultipleKeys() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableList.builder().add(new TagFilter(FilterName.KEY, - ImmutableList.builder().add("one").add("two").build())).build()); + request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "one").put(FilterName.KEY, "two").build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two"); } - public void testKeyWithoutValues() { + public void testkeyWithValue() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableList.builder().add(new TagFilter(FilterName.KEY, ImmutableList.of())).build()); - assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key"); + request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "one").put(FilterName.VALUE, "alpha").build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.2.Name=value&Filter.2.Value.1=alpha"); + } + + public void testAnyKey() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "*").build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=%2A"); + } + + public void testResourceTypeWithValuesBuilder() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, TagFilters.filters().vpnGateway().internetGateway().build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=resource-type&Filter.1.Value.1=vpn-gateway&Filter.1.Value.2=internet-gateway"); + } + + public void testMultipleKeysBuilder() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, TagFilters.filters().key("one").key("two").build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two"); + } + + public void testkeyWithValueBuilder() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, TagFilters.filters().keyValuePair("one", "alpha").build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.2.Name=value&Filter.2.Value.1=alpha"); + } + + public void testAnyKeyBuilder() { + HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); + request = binder.bindToRequest(request, TagFilters.filters().anyKey().build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=%2A"); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java index 87feb0a062..ac088fc11c 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java @@ -20,11 +20,9 @@ package org.jclouds.aws.ec2.services; import java.io.IOException; import java.lang.reflect.Method; -import java.util.Collection; import java.util.Map; -import org.jclouds.aws.ec2.domain.TagFilter; -import org.jclouds.aws.ec2.domain.TagFilter.FilterName; +import org.jclouds.aws.ec2.util.TagFilters.FilterName; import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; @@ -36,19 +34,20 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; import com.google.inject.TypeLiteral; /** * Tests behavior of {@code TagsAsyncClient} * - * @author Adrian Cole + * @author grkvlt@apache.org */ // NOTE:without testName, this will not call @Before* and fail w/NPE during surefire @Test(groups = "unit", testName = "TagsAsyncClientTest") public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTest { - public void testDeleteTags() throws SecurityException, NoSuchMethodException, IOException { - Method method = TagAsyncClient.class.getMethod("deleteTagsInRegion", String.class, Collection.class, Map.class); + Method method = TagAsyncClient.class.getMethod("deleteTagsInRegion", String.class, Iterable.class, Map.class); HttpRequest request = processor.createRequest(method, null, ImmutableList.builder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); @@ -64,7 +63,7 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTestbuilder().add("xxx").build(), ImmutableMap.builder().put("yyy", "zzz").build()); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); @@ -80,8 +79,8 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTestof()); + Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Multimap.class); + HttpRequest request = processor.createRequest(method, null, ImmutableMultimap.of()); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); @@ -96,9 +95,8 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTestbuilder().add("one").add("two").build())).build()); + Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Multimap.class); + HttpRequest request = processor.createRequest(method, null, ImmutableMultimap.builder().put(FilterName.KEY, "one").put(FilterName.KEY, "two").build()); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); @@ -117,5 +115,4 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTest>() { }; } - } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java index ea6d933ce4..9dd1ccc25e 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagClientLiveTest.java @@ -30,8 +30,8 @@ import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.AWSEC2Client; import org.jclouds.aws.ec2.domain.AWSRunningInstance; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.domain.TagFilter; -import org.jclouds.aws.ec2.domain.TagFilter.FilterName; +import org.jclouds.aws.ec2.util.TagFilters.FilterName; +import org.jclouds.aws.ec2.util.TagFilters.ResourceType; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.logging.log4j.config.Log4JLoggingModule; @@ -44,6 +44,7 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.Module; @@ -110,7 +111,7 @@ public class TagClientLiveTest { cleanupTag(testGroup, "test-key"); try { client.createTagsInRegion(null, ImmutableList.builder().add(testGroup).build(), ImmutableMap.builder().put("test-key", "test-value").build()); - checkTag(testGroup, TagFilter.ResourceType.SECURITY_GROUP.value(), "test-key", "test-value"); + checkTag(testGroup, ResourceType.SECURITY_GROUP, "test-key", "test-value"); } finally { cleanupTag(testGroup, "test-key"); } @@ -124,12 +125,12 @@ public class TagClientLiveTest { } } - protected void checkTag(String resourceId, String resourceType, String key, String value) { - Set results = client.describeTagsInRegion(null, ImmutableList.builder() - .add(new TagFilter(FilterName.RESOURCE_ID, ImmutableList.builder().add(resourceId).build())) - .add(new TagFilter(FilterName.RESOURCE_TYPE, ImmutableList.builder().add(resourceType).build())) - .add(new TagFilter(FilterName.KEY, ImmutableList.builder().add(key).build())) - .add(new TagFilter(FilterName.VALUE, ImmutableList.builder().add(value).build())) + protected void checkTag(String resourceId, ResourceType resourceType, String key, String value) { + Set results = client.describeTagsInRegion(null, ImmutableMultimap.builder() + .put(FilterName.RESOURCE_ID, resourceId) + .put(FilterName.RESOURCE_TYPE, resourceType) + .put(FilterName.KEY, key) + .put(FilterName.VALUE, value) .build()); assertNotNull(results); assertEquals(results.size(), 1); From 410b94716004238197985cf3a96077b45929657a Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Thu, 6 Oct 2011 23:42:15 +0100 Subject: [PATCH 3/3] 612: Change Multimap occurences to Maps with Iterable values --- .../java/org/jclouds/aws/util/AWSUtils.java | 24 ++++- .../BindTagFiltersToIndexedFormParams.java | 8 +- .../aws/ec2/services/TagAsyncClient.java | 7 +- .../jclouds/aws/ec2/services/TagClient.java | 10 +- .../org/jclouds/aws/ec2/util/TagFilters.java | 96 ++++++++++++------- ...BindTagFiltersToIndexedFormParamsTest.java | 21 ++-- .../aws/ec2/services/TagAsyncClientTest.java | 12 +-- .../aws/ec2/services/TagClientLiveTest.java | 10 +- 8 files changed, 114 insertions(+), 74 deletions(-) diff --git a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java index f8c5201026..bb72785ad3 100644 --- a/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java +++ b/common/aws/src/main/java/org/jclouds/aws/util/AWSUtils.java @@ -47,6 +47,7 @@ import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap.Builder; +import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; /** @@ -142,7 +143,7 @@ public class AWSUtils { checkArgument(checkNotNull(input, "input") instanceof String[], "this binder is only valid for String[] : " + input.getClass()); String[] values = (String[]) input; - Builder builder = ImmutableMultimap.builder(); + Builder builder = ImmutableMultimap. builder(); for (int i = 0; i < values.length; i++) { builder.put(prefix + "." + (i + 1), checkNotNull(values[i], prefix.toLowerCase() + "s[" + i + "]")); } @@ -174,7 +175,26 @@ public class AWSUtils { int i = 1; for (Object k : map.keySet()) { builder.put(prefix + "." + i + "." + keySuffix, checkNotNull(k.toString(), keySuffix.toLowerCase() + "s[" + i + "]")); - if (!map.get(k).isEmpty()) { + int j = 1; + for (Object v : map.get(k)) { + builder.put(prefix + "." + i + "." + valueSuffix + "." + j, v.toString()); + j++; + } + i++; + } + ImmutableMultimap forms = builder.build(); + return forms.size() == 0 ? request : ModifyRequest.putFormParams(request, forms); + } + + @SuppressWarnings("unchecked") + public static R indexMapOfIterableToFormValuesWithPrefix(R request, String prefix, String keySuffix, String valueSuffix, Object input) { + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map>: " + input.getClass()); + Map> map = (Map>) input; + Builder builder = ImmutableMultimap. builder(); + int i = 1; + for (Object k : map.keySet()) { + builder.put(prefix + "." + i + "." + keySuffix, checkNotNull(k.toString(), keySuffix.toLowerCase() + "s[" + i + "]")); + if (!Iterables.isEmpty(map.get(k))) { int j = 1; for (Object v : map.get(k)) { builder.put(prefix + "." + i + "." + valueSuffix + "." + j, v.toString()); diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java index 1058734569..55d0d85e64 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParams.java @@ -20,19 +20,19 @@ package org.jclouds.aws.ec2.binders; import static com.google.common.base.Preconditions.*; +import java.util.Map; + import org.jclouds.aws.util.AWSUtils; import org.jclouds.http.HttpRequest; import org.jclouds.rest.Binder; -import com.google.common.collect.Multimap; - /** * @author grkvlt@apache.org */ public class BindTagFiltersToIndexedFormParams implements Binder { @Override public R bindToRequest(R request, Object input) { - checkArgument(checkNotNull(input, "input") instanceof Multimap, "this binder is only valid for Multimap"); - return AWSUtils.indexMultimapToFormValuesWithPrefix(request, "Filter", "Name", "Value", input); + checkArgument(checkNotNull(input, "input") instanceof Map, "this binder is only valid for Map>"); + return AWSUtils.indexMapOfIterableToFormValuesWithPrefix(request, "Filter", "Name", "Value", input); } } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java index 5b1ffd36c9..bba53f1ff7 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagAsyncClient.java @@ -31,7 +31,7 @@ import org.jclouds.aws.ec2.binders.BindResourceIdsToIndexedFormParams; import org.jclouds.aws.ec2.binders.BindTagFiltersToIndexedFormParams; import org.jclouds.aws.ec2.binders.BindTagsToIndexedFormParams; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters; +import org.jclouds.aws.ec2.util.TagFilters.FilterName; import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; import org.jclouds.aws.filters.FormSigner; import org.jclouds.javax.annotation.Nullable; @@ -46,7 +46,6 @@ import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404; -import com.google.common.collect.Multimap; import com.google.common.util.concurrent.ListenableFuture; /** @@ -80,7 +79,7 @@ public interface TagAsyncClient { @BinderParam(BindTagsToIndexedFormParams.class) Map tags); /** - * @see TagClient#describeTagsInRegion(String, Multimap) + * @see TagClient#describeTagsInRegion(String, Map) */ @POST @Path("/") @@ -88,5 +87,5 @@ public interface TagAsyncClient { @XMLResponseParser(DescribeTagsResponseHandler.class) @ExceptionParser(ReturnEmptySetOnNotFoundOr404.class) ListenableFuture> describeTagsInRegion(@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, - @BinderParam(BindTagFiltersToIndexedFormParams.class) Multimap filters); + @BinderParam(BindTagFiltersToIndexedFormParams.class) Map> filters); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java index 8bf144f373..b77ff78897 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/TagClient.java @@ -23,12 +23,10 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.aws.ec2.domain.Tag; -import org.jclouds.aws.ec2.util.TagFilters; +import org.jclouds.aws.ec2.util.TagFilters.FilterName; import org.jclouds.concurrent.Timeout; import org.jclouds.javax.annotation.Nullable; -import com.google.common.collect.Multimap; - /** * Provides Tag services for EC2. For more information, refer to the Amazon EC2 * Developer Guide. @@ -46,7 +44,7 @@ public interface TagClient { * IDs of the resources to tag. * @param tags * The tags to create. - * @see #describeTagsInRegion(String, Multimap) + * @see #describeTagsInRegion(String, Map) * @see #deleteTagsInRegion(String, Iterable, Map) * * @see @@ -63,7 +61,7 @@ public interface TagClient { * @param tags * The tags to delete. * - * @see #describeTagsInRegion(String, Multimap) + * @see #describeTagsInRegion(String, Map) * @see #createTagsInRegion(String, Iterable, Map) * * @see @@ -82,5 +80,5 @@ public interface TagClient { * @see #createTagsInRegion(String, Iterable, Map) * @see */ - Set describeTagsInRegion(@Nullable String region, Multimap filters); + Set describeTagsInRegion(@Nullable String region, Map> filters); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java index c5406b1342..8e877a0752 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/util/TagFilters.java @@ -20,10 +20,14 @@ package org.jclouds.aws.ec2.util; import static com.google.common.base.Preconditions.*; +import java.util.Map; + import com.google.common.base.CaseFormat; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** * @author grkvlt@apache.org @@ -89,52 +93,52 @@ public class TagFilters { } } - protected final ImmutableSetMultimap.Builder map; + protected final Map> map; protected TagFilters() { - map = ImmutableSetMultimap.builder(); + map = Maps.>newLinkedHashMap(); } public static TagFilters filters() { return new TagFilters(); } - public Multimap build() { - return map.build(); + public Map> build() { + return ImmutableMap.copyOf(map); } public TagFilters resourceId(String resourceId) { - map.put(FilterName.RESOURCE_ID, resourceId); + put(FilterName.RESOURCE_ID, resourceId); return this; } public TagFilters key(String key) { - map.put(FilterName.KEY, key); + put(FilterName.KEY, key); return this; } public TagFilters keys(String...keys) { - map.putAll(FilterName.KEY, ImmutableSet.copyOf(keys)); + put(FilterName.KEY, ImmutableSet.copyOf(keys)); return this; } public TagFilters keys(Iterable keys) { - map.putAll(FilterName.KEY, ImmutableSet.copyOf(keys)); + putAll(FilterName.KEY, ImmutableSet.copyOf(keys)); return this; } public TagFilters value(String value) { - map.put(FilterName.VALUE, value); + put(FilterName.VALUE, value); return this; } public TagFilters values(String...values) { - map.putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); + putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); return this; } public TagFilters values(Iterable values) { - map.putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); + putAll(FilterName.VALUE, ImmutableSet.copyOf(values)); return this; } @@ -187,99 +191,125 @@ public class TagFilters { } public TagFilters anyKey() { - return key("*"); + putAll(FilterName.KEY, ImmutableSet.of()); + return this; } public TagFilters anyValue() { - return value("*"); + putAll(FilterName.VALUE, ImmutableSet.of()); + return this; } public TagFilters anyResourceId() { - return resourceId("*"); + putAll(FilterName.RESOURCE_TYPE, ImmutableSet.of()); + return this; } public TagFilters anyResourceType() { - map.put(FilterName.RESOURCE_TYPE, "*"); + putAll(FilterName.RESOURCE_TYPE, ImmutableSet.of()); + return this; + } + + public TagFilters resourceType(ResourceType resourceType) { + put(FilterName.RESOURCE_TYPE, resourceType); return this; } public TagFilters customerGateway() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.CUSTOMER_GATEWAY); + put(FilterName.RESOURCE_TYPE, ResourceType.CUSTOMER_GATEWAY); return this; } public TagFilters dhcpOptions() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.DHCP_OPTIONS); + put(FilterName.RESOURCE_TYPE, ResourceType.DHCP_OPTIONS); return this; } public TagFilters image() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.IMAGE); + put(FilterName.RESOURCE_TYPE, ResourceType.IMAGE); return this; } public TagFilters instance() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.INSTANCE); + put(FilterName.RESOURCE_TYPE, ResourceType.INSTANCE); return this; } public TagFilters internetGateway() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.INTERNET_GATEWAY); + put(FilterName.RESOURCE_TYPE, ResourceType.INTERNET_GATEWAY); return this; } public TagFilters networkAcl() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.NETWORK_ACL); + put(FilterName.RESOURCE_TYPE, ResourceType.NETWORK_ACL); return this; } public TagFilters reservedInstance() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.RESERVED_INSTANCES); + put(FilterName.RESOURCE_TYPE, ResourceType.RESERVED_INSTANCES); return this; } public TagFilters routeTable() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.ROUTE_TABLE); + put(FilterName.RESOURCE_TYPE, ResourceType.ROUTE_TABLE); return this; } public TagFilters securityGroup() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.SECURITY_GROUP); + put(FilterName.RESOURCE_TYPE, ResourceType.SECURITY_GROUP); return this; } public TagFilters snapshot() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.SNAPSHOT); + put(FilterName.RESOURCE_TYPE, ResourceType.SNAPSHOT); return this; } public TagFilters instancesRequest() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.SPOT_INSTANCES_REQUEST); + put(FilterName.RESOURCE_TYPE, ResourceType.SPOT_INSTANCES_REQUEST); return this; } public TagFilters subnet() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.SUBNET); + put(FilterName.RESOURCE_TYPE, ResourceType.SUBNET); return this; } public TagFilters volume() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.VOLUME); + put(FilterName.RESOURCE_TYPE, ResourceType.VOLUME); return this; } public TagFilters vpc() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.VPC); + put(FilterName.RESOURCE_TYPE, ResourceType.VPC); return this; } public TagFilters vpnConnection() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.VPN_CONNECTION); + put(FilterName.RESOURCE_TYPE, ResourceType.VPN_CONNECTION); return this; } public TagFilters vpnGateway() { - map.put(FilterName.RESOURCE_TYPE, ResourceType.VPN_GATEWAY); + put(FilterName.RESOURCE_TYPE, ResourceType.VPN_GATEWAY); return this; } + + private void put(FilterName key, Object value) { + putAll(key, Sets.newHashSet(value)); + } + + private void putAll(FilterName key, Iterable values) { + if (values == null || Iterables.isEmpty(values)) { + // If we add an empty or null set of values, replace the value in the map entirely + map.put(key, ImmutableSet.of()); + } else { + // Add the values, to a new set if required + if (!map.containsKey(key)) { + map.put(key, Sets.newHashSet()); + } + Iterable entries = map.get(key); + map.put(key, Iterables.concat(entries, values)); + } + } } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParamsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParamsTest.java index c9944007ad..c8e7b4e7a0 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParamsTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/binders/BindTagFiltersToIndexedFormParamsTest.java @@ -31,7 +31,8 @@ import org.jclouds.aws.ec2.util.TagFilters.ResourceType; import org.jclouds.http.HttpRequest; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.inject.Guice; import com.google.inject.Injector; @@ -45,31 +46,31 @@ public class BindTagFiltersToIndexedFormParamsTest { Injector injector = Guice.createInjector(); BindTagFiltersToIndexedFormParams binder = injector.getInstance(BindTagFiltersToIndexedFormParams.class); - public void testResourceTypeWithValues() { + public void testMultipleResourceTypes() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.RESOURCE_TYPE, ResourceType.VPN_GATEWAY).put(FilterName.RESOURCE_TYPE, ResourceType.INTERNET_GATEWAY).build()); + request = binder.bindToRequest(request, ImmutableMap.>builder().put(FilterName.RESOURCE_TYPE, ImmutableSet.of(ResourceType.VPN_GATEWAY, ResourceType.INTERNET_GATEWAY)).build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=resource-type&Filter.1.Value.1=vpn-gateway&Filter.1.Value.2=internet-gateway"); } public void testMultipleKeys() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "one").put(FilterName.KEY, "two").build()); + request = binder.bindToRequest(request, ImmutableMap.>builder().put(FilterName.KEY, ImmutableSet.of("one", "two")).build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two"); } public void testkeyWithValue() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "one").put(FilterName.VALUE, "alpha").build()); + request = binder.bindToRequest(request, ImmutableMap.>builder().put(FilterName.KEY, ImmutableSet.of("one")).put(FilterName.VALUE, ImmutableSet.of("alpha")).build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.2.Name=value&Filter.2.Value.1=alpha"); } public void testAnyKey() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); - request = binder.bindToRequest(request, ImmutableSetMultimap.builder().put(FilterName.KEY, "*").build()); - assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=%2A"); + request = binder.bindToRequest(request, ImmutableMap.>builder().put(FilterName.KEY, ImmutableSet.of()).build()); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key"); } - public void testResourceTypeWithValuesBuilder() { + public void testMultipleResourceTypesBuilder() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); request = binder.bindToRequest(request, TagFilters.filters().vpnGateway().internetGateway().build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=resource-type&Filter.1.Value.1=vpn-gateway&Filter.1.Value.2=internet-gateway"); @@ -81,7 +82,7 @@ public class BindTagFiltersToIndexedFormParamsTest { assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.1.Value.2=two"); } - public void testkeyWithValueBuilder() { + public void testKeyWithValueBuilder() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); request = binder.bindToRequest(request, TagFilters.filters().keyValuePair("one", "alpha").build()); assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=one&Filter.2.Name=value&Filter.2.Value.1=alpha"); @@ -90,7 +91,7 @@ public class BindTagFiltersToIndexedFormParamsTest { public void testAnyKeyBuilder() { HttpRequest request = HttpRequest.builder().method("POST").endpoint(URI.create("http://localhost")).build(); request = binder.bindToRequest(request, TagFilters.filters().anyKey().build()); - assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key&Filter.1.Value.1=%2A"); + assertEquals(request.getPayload().getRawContent(), "Filter.1.Name=key"); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java index ac088fc11c..4b0292f020 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/TagAsyncClientTest.java @@ -22,7 +22,7 @@ import java.io.IOException; import java.lang.reflect.Method; import java.util.Map; -import org.jclouds.aws.ec2.util.TagFilters.FilterName; +import org.jclouds.aws.ec2.util.TagFilters; import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler; import org.jclouds.http.HttpRequest; import org.jclouds.http.functions.ParseSax; @@ -34,8 +34,6 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Multimap; import com.google.inject.TypeLiteral; /** @@ -79,8 +77,8 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTestof()); + Method method = TagAsyncClient.class.getMethod("describeTagsInRegion", String.class, Map.class); + HttpRequest request = processor.createRequest(method, null, TagFilters.filters().build()); assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); @@ -95,8 +93,8 @@ public class TagAsyncClientTest extends BaseAWSEC2AsyncClientTest results = client.describeTagsInRegion(null, ImmutableMultimap.builder() - .put(FilterName.RESOURCE_ID, resourceId) - .put(FilterName.RESOURCE_TYPE, resourceType) - .put(FilterName.KEY, key) - .put(FilterName.VALUE, value) - .build()); + Set results = client.describeTagsInRegion(null, TagFilters.filters().resourceId(resourceId).resourceType(resourceType).keyValuePair(key, value).build()); assertNotNull(results); assertEquals(results.size(), 1); Tag tag = Iterables.getOnlyElement(results);