From e30f0f626ee40a9764f4a3f4e379158aeb656e17 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Tue, 4 Oct 2011 20:40:35 +0100 Subject: [PATCH] 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(); + } +}