Merge pull request #915 from jclouds/ec2-tag-api

Issue 1110: replace TagClient in aws-ec2 with TagApi in ec2
This commit is contained in:
Adrian Cole 2012-11-12 19:17:26 -08:00
commit cf8dce0e21
28 changed files with 1915 additions and 18 deletions

View File

@ -22,6 +22,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.ec2.features.TagApi;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Region;
@ -29,7 +30,6 @@ import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.EndpointParam;
import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.inject.Provides;
@ -66,12 +66,19 @@ public interface EC2Api {
* Provides synchronous access to Windows features.
*/
@Delegate
@Beta
Optional<? extends WindowsApi> getWindowsApi();
@Delegate
@Beta
Optional<? extends WindowsApi> getWindowsApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides synchronous access to Tag features.
*/
@Delegate
Optional<? extends TagApi> getTagApi();
@Delegate
Optional<? extends TagApi> getTagApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -20,6 +20,7 @@ package org.jclouds.ec2;
import java.util.Set;
import org.jclouds.ec2.features.TagAsyncApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.Region;
@ -54,4 +55,14 @@ public interface EC2AsyncApi {
@Delegate
Optional<? extends WindowsAsyncApi> getWindowsApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
/**
* Provides asynchronous access to Tag features.
*/
@Delegate
Optional<? extends TagAsyncApi> getTagApi();
@Delegate
Optional<? extends TagAsyncApi> getTagApiForRegion(
@EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region);
}

View File

@ -83,4 +83,5 @@ public interface EC2AsyncClient extends EC2AsyncApi {
*/
@Delegate
ElasticBlockStoreAsyncClient getElasticBlockStoreServices();
}

View File

@ -0,0 +1,59 @@
/**
* 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.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;
import com.google.common.collect.Multimap;
/**
* Binds the Multimap to form parameters for filtering.
*
* <pre>
* https://ec2.amazonaws.com/?Action=DescribeTags
* &Filter.1.Name=resource-type
* &Filter.1.Value.1=instance
* &Filter.2.Name=key
* &Filter.2.Value.1=stack
* &Filter.3.Name=value
* &Filter.3.Value.1=Test
* &Filter.3.Value.2=Production
* &AUTHPARAMS
* </pre>
*
* @author Adrian Cole
*/
@Singleton
public class BindFiltersToIndexedFormParams implements Binder {
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkArgument(checkNotNull(input, "input") instanceof Multimap, "this binder is only valid for Multimap");
@SuppressWarnings("unchecked")
Multimap<String, String> filters = (Multimap<String, String>) input;
return AWSUtils.indexMultimapToFormValuesWithPrefix(request, "Filter", "Name", "Value", filters);
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.aws.ec2.binders;
package org.jclouds.ec2.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

View File

@ -0,0 +1,48 @@
/**
* 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.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;
import com.google.common.collect.Iterables;
/**
* Binds the Iterable<String> to form parameters named with Tag.index.Key
*
* @author Adrian Cole
*/
@Singleton
public class BindTagKeysToIndexedFormParams implements Binder {
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkArgument(checkNotNull(input, "input") instanceof Iterable, "This binder is only valid for Iterable<String>");
@SuppressWarnings("unchecked")
Iterable<String> keys = (Iterable<String>) input;
return AWSUtils.indexStringArrayToFormValuesWithStringFormat(request, "Tag.%s.Key",
Iterables.toArray(keys, String.class));
}
}

View File

@ -0,0 +1,56 @@
/**
* 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.ec2.binders;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.aws.util.AWSUtils.indexMapToFormValuesWithPrefix;
import java.util.Map;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
/**
* @author Adrian Cole
*/
public class BindTagsToIndexedFormParams implements Binder {
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkNotNull(input, "tags");
Map<String, String> tagValues;
if (input instanceof Iterable) {
Builder<String, String> builder = ImmutableMap.<String, String> builder();
for (String key : (Iterable<String>) input) {
builder.put(key, "");
}
tagValues = builder.build();
} else if (input instanceof Map) {
tagValues = Map.class.cast(input);
} else {
throw new IllegalArgumentException("This binder is only valid for Map<String,String> or Iterable<String>");
}
return indexMapToFormValuesWithPrefix(request, "Tag", "Key", "Value", tagValues);
}
}

View File

@ -27,6 +27,8 @@ import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.EC2AsyncApi;
import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.features.TagApi;
import org.jclouds.ec2.features.TagAsyncApi;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.services.AMIAsyncClient;
@ -82,6 +84,7 @@ public class EC2RestClientModule<S extends EC2Api, A extends EC2AsyncApi> extend
.put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)//
.put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)//
.put(WindowsApi.class, WindowsAsyncApi.class)//
.put(TagApi.class, TagAsyncApi.class)//
.build();
@SuppressWarnings("unchecked")

View File

@ -0,0 +1,198 @@
/**
* 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.ec2.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
/**
* To help you manage your Amazon EC2 instances, images, and other Amazon EC2
* resources, you can assign your own metadata to each resource in the form of
* tags.
*
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/Using_Tags.html"
* >doc</a>
*
* @author Adrian Cole
*/
public class Tag {
/**
* Describes the well-known resource types that can be tagged.
*/
public static interface ResourceType {
public static final String CUSTOMER_GATEWAY = "customer-gateway";
public static final String DHCP_OPTIONS = "dhcp-options";
public static final String IMAGE = "image";
public static final String INSTANCE = "instance";
public static final String INTERNET_GATEWAY = "internet-gateway";
public static final String NETWORK_ACL = "network-acl";
public static final String RESERVED_INSTANCES = "reserved-instances";
public static final String ROUTE_TABLE = "route-table";
public static final String SECURITY_GROUP = "security-group";
public static final String SNAPSHOT = "snapshot";
public static final String SPOT_INSTANCES_REQUEST = "spot-instances-request";
public static final String SUBNET = "subnet";
public static final String VOLUME = "volume";
public static final String VPC = "vpc";
public static final String VPN_CONNECTION = "vpn-connection";
public static final String VPN_GATEWAY = "vpn-gateway";
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().fromTag(this);
}
public static class Builder {
protected String resourceId;
protected String resourceType;
protected String key;
protected Optional<String> value = Optional.absent();
/**
* @see Tag#getResourceId()
*/
public Builder resourceId(String resourceId) {
this.resourceId = resourceId;
return this;
}
/**
* @see Tag#getResourceType()
*/
public Builder resourceType(String resourceType) {
this.resourceType = resourceType;
return this;
}
/**
* @see Tag#getKey()
*/
public Builder key(String key) {
this.key = key;
return this;
}
/**
* @see TagGroup#getValue()
*/
public Builder value(String value) {
this.value = Optional.fromNullable(value);
return this;
}
public Tag build() {
return new Tag(resourceId, resourceType, key, value);
}
public Builder fromTag(Tag in) {
return this.resourceId(in.getResourceId()).resourceType(in.getResourceType()).key(in.getKey())
.value(in.getValue().orNull());
}
}
protected final String resourceId;
protected final String resourceType;
protected final String key;
protected final Optional<String> value;
protected Tag(String resourceId, String resourceType, String key, Optional<String> value) {
this.resourceId = checkNotNull(resourceId, "resourceId");
this.resourceType = checkNotNull(resourceType, "resourceType");
this.key = checkNotNull(key, "key");
this.value = checkNotNull(value, "value");
}
/**
* The resource ID ex. i-erf235
*/
public String getResourceId() {
return resourceId;
}
/**
* The resource type. ex. 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 getResourceType() {
return resourceType;
}
/**
* The tag key.
*/
public String getKey() {
return key;
}
/**
* The tag value.
*/
public Optional<String> getValue() {
return value;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(resourceId, key);
}
/**
* {@inheritDoc}
*/
@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;
return Objects.equal(this.resourceId, other.resourceId) && Objects.equal(this.key, other.key);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return string().toString();
}
protected ToStringHelper string() {
return Objects.toStringHelper(this).omitNullValues().add("resourceId", resourceId)
.add("resourceType", resourceType).add("key", key).add("value", value.orNull());
}
}

View File

@ -0,0 +1,156 @@
/**
* 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.ec2.features;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.jclouds.concurrent.Timeout;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.ec2.util.TagFilterBuilder;
import org.jclouds.rest.annotations.SinceApiVersion;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
/**
* Provides access to Amazon EC2 via the Query API
* <p/>
*
* This api is not available in EC2 versions below {@link #MIN_API_VERSION}
*
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >doc</a>
* @see TagAsyncApi
* @author Adrian Cole
*/
@SinceApiVersion("2010-08-31")
@Timeout(duration = 30, timeUnit = TimeUnit.SECONDS)
public interface TagApi {
/**
* Adds or overwrites one or more tags for the specified resource or
* resources. Each resource can have a maximum of 10 tags. Each tag consists
* of a key and optional value. Tag keys must be unique per resource.
*
* <h4>example</h4>
*
* <pre>
* tagApi.applyToResources(ImmutableMap.of(&quot;group&quot;, &quot;backend&quot;), ImmutableSet.of(&quot;i-1a2b3c4d&quot;));
* </pre>
*
* @param tags
* key to an optional value.
* @param resourceIds
* The ID of a resource to tag. For example, {@code ami-1a2b3c4d}
*
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateTags.html"
* >docs</href>
*/
void applyToResources(Map<String, String> tags, Iterable<String> resourceIds);
/**
* like {@link #applyToResources(Map, Iterable)} except that the tags have no
* values.
*
* <h4>example</h4>
*
* <pre>
* tagApi.applyToResources(ImmutableSet.of(&quot;production&quot;, &quot;pci-compliant&quot;), ImmutableSet.of(&quot;i-1a2b3c4d&quot;));
* </pre>
*
* @see #applyToResources(Map, Iterable)
*/
void applyToResources(Iterable<String> tags, Iterable<String> resourceIds);
/**
* Describes all of your tags for your EC2 resources.
*
* @return tags or empty if there are none
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >docs</href>
*/
FluentIterable<Tag> list();
/**
* Describes tags for your EC2 resources qualified by a filter
*
* <h4>example</h4>
*
* <pre>
* tags = tagApi.filter(new TagFilterBuilder().image().put(&quot;version&quot;, &quot;1.0&quot;).build());
* </pre>
*
* @param filter
* which is typically built by {@link TagFilterBuilder}
* @return tags or empty if there are none that match the filter
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >docs</href>
*/
FluentIterable<Tag> filter(Multimap<String, String> filter);
/**
* Deletes a specific set of tags from a specific set of resources. This call
* is designed to follow a {@link #list() list} or {@link #filter(Multimap)
* filter} call. You first determine what tags a resource has, and then you
* call {@link TagApi#deleteFromResources(Iterable, Iterable) delete} with
* the resource ID and the specific tags you want to delete.
*
* <h4>example</h4>
*
* <pre>
* tagApi.deleteFromResources(ImmutableSet.of(&quot;Purpose&quot;), ImmutableSet.of(&quot;ami-1a2b3c4d&quot;));
* </pre>
*
* @param tags
* the tag keys
* @param resourceIds
* The ID of a resource with the tag. For example,
* {@code ami-1a2b3c4d}
* @see <a href=
* "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteTags.html"
* >docs</href>
*/
void deleteFromResources(Iterable<String> tags, Iterable<String> resourceIds);
/**
* like {@link #deleteFromResources(Iterable, Iterable)}, except that the
* tags are only deleted if they match the value.
*
* <h4>example</h4>
*
* <pre>
* tagApi.deleteFromResources(ImmutableMap.of(&quot;Purpose&quot;, &quot;production&quot;), ImmutableSet.of(&quot;ami-1a2b3c4d&quot;));
* </pre>
*
* @param conditionalTagValues
* tag id to value it must match before deleting. For a tag without
* a value, supply empty string.
* @param resourceIds
* The ID of a resource with the tag. For example,
* {@code ami-1a2b3c4d}
* @see #deleteFromResources(Iterable, Iterable)
*/
void conditionallyDeleteFromResources(Map<String, String> conditionalTagValues, Iterable<String> resourceIds);
}

View File

@ -0,0 +1,133 @@
/**
* 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.ec2.features;
import static org.jclouds.aws.reference.FormParameters.ACTION;
import java.util.Map;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.jclouds.aws.filters.FormSigner;
import org.jclouds.ec2.binders.BindFiltersToIndexedFormParams;
import org.jclouds.ec2.binders.BindResourceIdsToIndexedFormParams;
import org.jclouds.ec2.binders.BindTagKeysToIndexedFormParams;
import org.jclouds.ec2.binders.BindTagsToIndexedFormParams;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.ec2.xml.DescribeTagsResponseHandler;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SinceApiVersion;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.rest.functions.ReturnEmptyFluentIterableOnNotFoundOr404;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides access to Amazon EC2 via the Query API
* <p/>
*
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >doc</a>
* @see TagApi
* @author Adrian Cole
*/
@SinceApiVersion("2010-08-31")
@RequestFilters(FormSigner.class)
@VirtualHost
public interface TagAsyncApi {
/**
* @see TagApi#applyToResources(Iterable, Iterable)
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "CreateTags")
ListenableFuture<Void> applyToResources(@BinderParam(BindTagsToIndexedFormParams.class) Iterable<String> tags,
@BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable<String> resourceIds);
/**
* @see TagApi#applyToResources(Map, Iterable)
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "CreateTags")
ListenableFuture<Void> applyToResources(@BinderParam(BindTagsToIndexedFormParams.class) Map<String, String> tags,
@BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable<String> resourceIds);
/**
* @see TagApi#list()
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "DescribeTags")
@XMLResponseParser(DescribeTagsResponseHandler.class)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<FluentIterable<Tag>> list();
/**
* @see TagApi#filter
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "DescribeTags")
@XMLResponseParser(DescribeTagsResponseHandler.class)
@ExceptionParser(ReturnEmptyFluentIterableOnNotFoundOr404.class)
ListenableFuture<FluentIterable<Tag>> filter(
@BinderParam(BindFiltersToIndexedFormParams.class) Multimap<String, String> filter);
/**
* @see TagApi#deleteFromResources
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "DeleteTags")
ListenableFuture<Void> deleteFromResources(
@BinderParam(BindTagKeysToIndexedFormParams.class) Iterable<String> tags,
@BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable<String> resourceIds);
/**
* @see TagApi#conditionallyDeleteFromResources
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteTags.html">docs</a>
*/
@POST
@Path("/")
@FormParams(keys = ACTION, values = "DeleteTags")
ListenableFuture<Void> conditionallyDeleteFromResources(
@BinderParam(BindTagsToIndexedFormParams.class) Map<String, String> conditionalTagValues,
@BinderParam(BindResourceIdsToIndexedFormParams.class) Iterable<String> resourceIds);
}

View File

@ -0,0 +1,226 @@
/**
* 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.ec2.util;
import java.util.Comparator;
import java.util.Map.Entry;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.ec2.features.TagApi;
import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
/**
* You can use filters to limit the results when describing tags. For example,
* you could get only the tags for a particular resource type. You can specify
* multiple values for a filter. A tag must match at least one of the specified
* values for it to be included in the results.
*
* You can specify multiple filters (for example, limit the results to a
* specific resource type, and get only tags with values that contain the string
* database). The result includes information for a particular tag only if it
* matches all your filters. If there's no match, no special message is
* returned; the response is simply empty.
*
* <h4>Wildcards</h4> You can use wildcards with the filter values: {@code *}
* matches zero or more characters, and ? matches exactly one character. You can
* escape special characters using a backslash before the character. For
* example, a value of {@code \*amazon\?\\} searches for the literal string
* {@code *amazon?\}.
*
* @author Adrian Cole
* @see TagApi
*/
public class TagFilterBuilder extends ImmutableMultimap.Builder<String, String> {
private static final String KEY = "key";
private static final String VALUE = "value";
private static final String RESOURCE_ID = "resource-id";
private static final String RESOURCE_TYPE = "resource-type";
public TagFilterBuilder key(String key) {
return put(KEY, key);
}
public TagFilterBuilder keys(String... keys) {
return putAll(KEY, keys);
}
public TagFilterBuilder keys(Iterable<String> keys) {
return putAll(KEY, keys);
}
public TagFilterBuilder anyKey() {
return putAll(KEY, ImmutableSet.<String> of());
}
public TagFilterBuilder value(String value) {
return put(VALUE, value);
}
public TagFilterBuilder values(String... values) {
return putAll(VALUE, ImmutableSet.<String> copyOf(values));
}
public TagFilterBuilder values(Iterable<String> values) {
return putAll(VALUE, ImmutableSet.<String> copyOf(values));
}
public TagFilterBuilder anyValue() {
return putAll(VALUE, ImmutableSet.<String> of());
}
public TagFilterBuilder resourceId(String resourceId) {
return put(RESOURCE_ID, resourceId);
}
public TagFilterBuilder resourceIds(String... resourceIds) {
return putAll(RESOURCE_ID, resourceIds);
}
public TagFilterBuilder resourceIds(Iterable<String> resourceIds) {
return putAll(RESOURCE_ID, resourceIds);
}
public TagFilterBuilder anyResourceId() {
return putAll(RESOURCE_TYPE, ImmutableSet.<String> of());
}
public TagFilterBuilder resourceType(String resourceType) {
return put(RESOURCE_TYPE, resourceType);
}
public TagFilterBuilder resourceTypes(String... resourceTypes) {
return putAll(RESOURCE_TYPE, resourceTypes);
}
public TagFilterBuilder resourceTypes(Iterable<String> resourceTypes) {
return putAll(RESOURCE_TYPE, resourceTypes);
}
public TagFilterBuilder anyResourceType() {
return putAll(RESOURCE_TYPE, ImmutableSet.<String> of());
}
public TagFilterBuilder customerGateway() {
return put(RESOURCE_TYPE, Tag.ResourceType.CUSTOMER_GATEWAY);
}
public TagFilterBuilder dhcpOptions() {
return put(RESOURCE_TYPE, Tag.ResourceType.DHCP_OPTIONS);
}
public TagFilterBuilder image() {
return put(RESOURCE_TYPE, Tag.ResourceType.IMAGE);
}
public TagFilterBuilder instance() {
return put(RESOURCE_TYPE, Tag.ResourceType.INSTANCE);
}
public TagFilterBuilder internetGateway() {
return put(RESOURCE_TYPE, Tag.ResourceType.INTERNET_GATEWAY);
}
public TagFilterBuilder networkAcl() {
return put(RESOURCE_TYPE, Tag.ResourceType.NETWORK_ACL);
}
public TagFilterBuilder reservedInstance() {
return put(RESOURCE_TYPE, Tag.ResourceType.RESERVED_INSTANCES);
}
public TagFilterBuilder routeTable() {
return put(RESOURCE_TYPE, Tag.ResourceType.ROUTE_TABLE);
}
public TagFilterBuilder securityGroup() {
return put(RESOURCE_TYPE, Tag.ResourceType.SECURITY_GROUP);
}
public TagFilterBuilder snapshot() {
return put(RESOURCE_TYPE, Tag.ResourceType.SNAPSHOT);
}
public TagFilterBuilder instancesRequest() {
return put(RESOURCE_TYPE, Tag.ResourceType.SPOT_INSTANCES_REQUEST);
}
public TagFilterBuilder subnet() {
return put(RESOURCE_TYPE, Tag.ResourceType.SUBNET);
}
public TagFilterBuilder volume() {
return put(RESOURCE_TYPE, Tag.ResourceType.VOLUME);
}
public TagFilterBuilder vpc() {
return put(RESOURCE_TYPE, Tag.ResourceType.VPC);
}
public TagFilterBuilder vpnConnection() {
return put(RESOURCE_TYPE, Tag.ResourceType.VPN_CONNECTION);
}
public TagFilterBuilder vpnGateway() {
return put(RESOURCE_TYPE, Tag.ResourceType.VPN_GATEWAY);
}
// to set correct return val in chain
@Override
public TagFilterBuilder put(String key, String value) {
return TagFilterBuilder.class.cast(super.put(key, value));
}
@Override
public TagFilterBuilder put(Entry<? extends String, ? extends String> entry) {
return TagFilterBuilder.class.cast(super.put(entry));
}
@Override
public TagFilterBuilder putAll(String key, Iterable<? extends String> values) {
return TagFilterBuilder.class.cast(super.putAll(key, values));
}
@Override
public TagFilterBuilder putAll(String key, String... values) {
return TagFilterBuilder.class.cast(super.putAll(key, values));
}
@Override
public TagFilterBuilder putAll(Multimap<? extends String, ? extends String> multimap) {
return TagFilterBuilder.class.cast(super.putAll(multimap));
}
@Override
@Beta
public TagFilterBuilder orderKeysBy(Comparator<? super String> keyComparator) {
return TagFilterBuilder.class.cast(super.orderKeysBy(keyComparator));
}
@Override
@Beta
public TagFilterBuilder orderValuesBy(Comparator<? super String> valueComparator) {
return TagFilterBuilder.class.cast(super.orderValuesBy(valueComparator));
}
}

View File

@ -0,0 +1,101 @@
/**
* 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.ec2.xml;
import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.http.functions.ParseSax;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.inject.Inject;
/**
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >xml</a>
*
* @author Adrian Cole
*/
public class DescribeTagsResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<FluentIterable<Tag>> {
private final TagHandler tagHander;
private StringBuilder currentText = new StringBuilder();
private Builder<Tag> tags = ImmutableSet.<Tag> builder();
private boolean inTags;
@Inject
public DescribeTagsResponseHandler(TagHandler tagHander) {
this.tagHander = tagHander;
}
/**
* {@inheritDoc}
*/
@Override
public FluentIterable<Tag> getResult() {
return FluentIterable.from(tags.build());
}
/**
* {@inheritDoc}
*/
@Override
public void startElement(String url, String name, String qName, Attributes attributes) throws SAXException {
if (equalsOrSuffix(qName, "tagSet")) {
inTags = true;
}
if (inTags) {
tagHander.startElement(url, name, qName, attributes);
}
}
/**
* {@inheritDoc}
*/
@Override
public void endElement(String uri, String name, String qName) throws SAXException {
if (equalsOrSuffix(qName, "tagSet")) {
inTags = false;
} else if (equalsOrSuffix(qName, "item")) {
tags.add(tagHander.getResult());
} else if (inTags) {
tagHander.endElement(uri, name, qName);
}
currentText = new StringBuilder();
}
/**
* {@inheritDoc}
*/
@Override
public void characters(char ch[], int start, int length) {
if (inTags) {
tagHander.characters(ch, start, length);
} else {
currentText.append(ch, start, length);
}
}
}

View File

@ -0,0 +1,79 @@
/**
* 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.ec2.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.http.functions.ParseSax;
import org.xml.sax.SAXException;
import com.google.common.base.Strings;
/**
* @see <a
* href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeTags.html"
* >xml</a>
*
* @author Adrian Cole
*/
public class TagHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Tag> {
private StringBuilder currentText = new StringBuilder();
private Tag.Builder builder = Tag.builder();
/**
* {@inheritDoc}
*/
@Override
public Tag getResult() {
try {
return builder.build();
} finally {
builder = Tag.builder();
}
}
/**
* {@inheritDoc}
*/
@Override
public void endElement(String uri, String name, String qName) throws SAXException {
if (equalsOrSuffix(qName, "resourceId")) {
builder.resourceId(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "resourceType")) {
builder.resourceType(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "key")) {
builder.key(currentOrNull(currentText));
} else if (equalsOrSuffix(qName, "value")) {
// empty is same as not present
builder.value(Strings.emptyToNull(currentOrNull(currentText)));
}
currentText = new StringBuilder();
}
/**
* {@inheritDoc}
*/
@Override
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -0,0 +1,65 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.ec2.binders;
import static org.testng.Assert.assertEquals;
import java.io.File;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code BindFiltersToIndexedFormParams}
*
* @author Adrian Cole
*/
@Test(groups = "unit")
public class BindFiltersToIndexedFormParamsTest {
Injector injector = Guice.createInjector();
BindFiltersToIndexedFormParams binder = injector.getInstance(BindFiltersToIndexedFormParams.class);
public void test() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = binder.bindToRequest(request, ImmutableMultimap.<String, String> builder()
.put("resource-type", "instance")
.put("key", "stack")
.putAll("value", "Test", "Production")
.build());
assertEquals(
request.getPayload().getRawContent(),
"Filter.1.Name=resource-type&Filter.1.Value.1=instance&Filter.2.Name=key&Filter.2.Value.1=stack&Filter.3.Name=value&Filter.3.Value.1=Test&Filter.3.Value.2=Production");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustBeMultimap() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();;
binder.bindToRequest(request, new File("foo"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullIsBad() {
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").build();
binder.bindToRequest(request, null);
}
}

View File

@ -0,0 +1,41 @@
package org.jclouds.ec2.binders;
import static org.testng.Assert.assertEquals;
import java.io.File;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import com.google.inject.Injector;
/**
* Tests behavior of {@code BindResourceIdsToIndexedFormParams}
*
* @author grkvlt@apache.org
*/
@Test(groups = "unit")
public class BindResourceIdsToIndexedFormParamsTest {
Injector injector = Guice.createInjector();
BindResourceIdsToIndexedFormParams binder = injector.getInstance(BindResourceIdsToIndexedFormParams.class);
public void test() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = binder.bindToRequest(request, ImmutableList.builder().add("alpha").add("omega").build());
assertEquals(request.getPayload().getRawContent(), "ResourceId.1=alpha&ResourceId.2=omega");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustBeArray() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();;
binder.bindToRequest(request, new File("foo"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullIsBad() {
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").build();
binder.bindToRequest(request, null);
}
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.aws.ec2.binders;
package org.jclouds.ec2.binders;
import static org.testng.Assert.assertEquals;
@ -25,29 +25,26 @@ import java.io.File;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.common.collect.ImmutableSet;
/**
* Tests behavior of {@code BindResourceIdsToIndexedFormParams}
* Tests behavior of {@code BindTagKeysToIndexedFormParams}
*
* @author grkvlt@apache.org
* @author Adrian Cole
*/
@Test(groups = "unit")
public class BindResourceIdsToIndexedFormParamsTest {
Injector injector = Guice.createInjector();
BindResourceIdsToIndexedFormParams binder = injector.getInstance(BindResourceIdsToIndexedFormParams.class);
public class BindTagKeysToIndexedFormParamsTest {
BindTagKeysToIndexedFormParams binder = new BindTagKeysToIndexedFormParams();
public void test() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = binder.bindToRequest(request, ImmutableList.builder().add("alpha").add("omega").build());
assertEquals(request.getPayload().getRawContent(), "ResourceId.1=alpha&ResourceId.2=omega");
request = binder.bindToRequest(request, ImmutableSet.of("one", "two"));
assertEquals(request.getPayload().getRawContent(), "Tag.1.Key=one&Tag.2.Key=two");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustBeArray() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();;
public void testMustBeIterable() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
binder.bindToRequest(request, new File("foo"));
}

View File

@ -0,0 +1,64 @@
/**
* 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.ec2.binders;
import static org.testng.Assert.assertEquals;
import java.io.File;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Tests behavior of {@code BindTagsToIndexedFormParams}
*
* @author grkvlt@apache.org
*/
@Test(groups = "unit")
public class BindTagsToIndexedFormParamsTest {
BindTagsToIndexedFormParams binder = new BindTagsToIndexedFormParams();
public void test() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = binder.bindToRequest(request, ImmutableMap.of("one", "alpha", "two", "beta"));
assertEquals(request.getPayload().getRawContent(),
"Tag.1.Key=one&Tag.1.Value=alpha&Tag.2.Key=two&Tag.2.Value=beta");
}
public void testEmpty() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
request = binder.bindToRequest(request, ImmutableSet.of("empty"));
assertEquals(request.getPayload().getRawContent(), "Tag.1.Key=empty&Tag.1.Value=");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testMustBeArray() {
HttpRequest request = HttpRequest.builder().method("POST").endpoint("http://localhost").build();
binder.bindToRequest(request, new File("foo"));
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullIsBad() {
HttpRequest request = HttpRequest.builder().method("GET").endpoint("http://momma").build();
binder.bindToRequest(request, null);
}
}

View File

@ -0,0 +1,293 @@
/**
* 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
*
* Unles 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 expres or implied. See the License for the
* specific language governing permisions and limitations
* under the License.
*/
package org.jclouds.ec2.features;
import static org.testng.Assert.assertEquals;
import java.util.Properties;
import java.util.TimeZone;
import org.jclouds.Constants;
import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.internal.BaseEC2ApiExpectTest;
import org.jclouds.ec2.parse.DescribeTagsResponseTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.rest.annotations.SinceApiVersion;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit")
public class TagApiExpectTest extends BaseEC2ApiExpectTest<EC2Api> {
public TagApiExpectTest() {
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
}
/**
* @see TagApi
* @see SinceApiVersion
*/
protected Properties setupProperties() {
Properties props = super.setupProperties();
props.put(Constants.PROPERTY_API_VERSION, "2010-08-31");
return props;
}
HttpRequest apply = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-43532" +
"&Signature=Trp5e5%2BMqeBeBZbLYa9s9gxahQ9nkx6ETfsGl82IV8Y%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=tag" +
"&Tag.1.Value=" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testApplyWhenResponseIs2xx() throws Exception {
HttpResponse applyResponse = HttpResponse.builder().statusCode(200).build();
EC2Api apiWhenExist = requestSendsResponse(apply, applyResponse);
apiWhenExist.getTagApi().get().applyToResources(ImmutableSet.of("tag"), ImmutableSet.of("i-43532"));
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testApplyWhenResponseIs404() throws Exception {
HttpResponse applyResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(apply, applyResponse);
apiWhenDontExist.getTagApi().get().applyToResources(ImmutableSet.of("tag"), ImmutableSet.of("i-43532"));
}
HttpRequest applyWithValues = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=CreateTags" +
"&ResourceId.1=i-43532" +
"&Signature=jwCQr50j%2BvGkav4t0BN0G8RmNJ7VaFK6%2F7N%2FHKUmHL8%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=tag" +
"&Tag.1.Value=value" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testApplyWithValuesWhenResponseIs2xx() throws Exception {
HttpResponse applyResponse = HttpResponse.builder().statusCode(200).build();
EC2Api apiWhenExist = requestSendsResponse(applyWithValues, applyResponse);
apiWhenExist.getTagApi().get().applyToResources(ImmutableMap.of("tag", "value"), ImmutableSet.of("i-43532"));
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testApplyWithValuesWhenResponseIs404() throws Exception {
HttpResponse applyResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(applyWithValues, applyResponse);
apiWhenDontExist.getTagApi().get().applyToResources(ImmutableMap.of("tag", "value"), ImmutableSet.of("i-43532"));
}
HttpRequest list = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=DescribeTags" +
"&Signature=DYUjPGiRl9copBtmpocMZYVAy4OTrK2AJzcAH5QiBuw%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testListWhenResponseIs2xx() throws Exception {
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/describe_tags.xml", "text/xml")).build();
EC2Api apiWhenExist = requestSendsResponse(
list, listResponse);
assertEquals(apiWhenExist.getTagApi().get().list().toString(), new DescribeTagsResponseTest().expected().toString());
}
public void testListWhenResponseIs404() throws Exception {
HttpResponse listResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(
list, listResponse);
assertEquals(apiWhenDontExist.getTagApi().get().list().toImmutableSet(), ImmutableSet.of());
}
HttpRequest filter =
HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(payloadFromStringWithContentType(
"Action=DescribeTags" +
"&Filter.1.Name=resource-type" +
"&Filter.1.Value.1=instance" +
"&Filter.2.Name=key" +
"&Filter.2.Value.1=stack" +
"&Signature=doNEEZHEzXV%2FSD2eSZ6PpB1PADcsAF99lXGvsh3MbS4%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testFilterWhenResponseIs2xx() throws Exception {
HttpResponse filterResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/describe_tags.xml", "text/xml")).build();
EC2Api apiWhenExist = requestSendsResponse(filter, filterResponse);
assertEquals(apiWhenExist.getTagApi().get().filter(ImmutableMultimap.<String, String> builder()
.put("resource-type", "instance")
.put("key", "stack")
.build()).toString(),
new DescribeTagsResponseTest().expected().toString());
}
public void testFilterWhenResponseIs404() throws Exception {
HttpResponse filterResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(filter, filterResponse);
assertEquals(apiWhenDontExist.getTagApi().get().filter(ImmutableMultimap.<String, String> builder()
.put("resource-type", "instance")
.put("key", "stack")
.build()).toImmutableSet(), ImmutableSet.of());
}
HttpRequest delete = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=DeleteTags" +
"&ResourceId.1=i-43532" +
"&Signature=ytM605menR00re60wXMgBDpozrQCi0lVupf755%2FMpck%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=tag" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testDeleteWhenResponseIs2xx() throws Exception {
HttpResponse deleteResponse = HttpResponse.builder().statusCode(200).build();
EC2Api apiWhenExist = requestSendsResponse(delete, deleteResponse);
apiWhenExist.getTagApi().get().deleteFromResources(ImmutableSet.of("tag"), ImmutableSet.of("i-43532"));
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testDeleteWhenResponseIs404() throws Exception {
HttpResponse deleteResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(delete, deleteResponse);
apiWhenDontExist.getTagApi().get().deleteFromResources(ImmutableSet.of("tag"), ImmutableSet.of("i-43532"));
}
HttpRequest conditionallyDelete = HttpRequest.builder()
.method("POST")
.endpoint("https://ec2.us-east-1.amazonaws.com/")
.addHeader("Host", "ec2.us-east-1.amazonaws.com")
.payload(
payloadFromStringWithContentType(
"Action=DeleteTags" +
"&ResourceId.1=i-43532" +
"&Signature=vRvgPegVDDjIEKudZ5Tpck0GQrVts%2F%2F1jzk4W5RgI9k%3D" +
"&SignatureMethod=HmacSHA256" +
"&SignatureVersion=2" +
"&Tag.1.Key=tag" +
"&Tag.1.Value=value" +
"&Timestamp=2012-04-16T15%3A54%3A08.897Z" +
"&Version=2010-08-31" +
"&AWSAccessKeyId=identity",
"application/x-www-form-urlencoded"))
.build();
public void testConditionallyDeleteWhenResponseIs2xx() throws Exception {
HttpResponse conditionallyDeleteResponse = HttpResponse.builder().statusCode(200).build();
EC2Api apiWhenExist = requestSendsResponse(conditionallyDelete, conditionallyDeleteResponse);
apiWhenExist.getTagApi().get().conditionallyDeleteFromResources(ImmutableMap.of("tag", "value"), ImmutableSet.of("i-43532"));
}
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testConditionallyDeleteWhenResponseIs404() throws Exception {
HttpResponse conditionallyDeleteResponse = HttpResponse.builder().statusCode(404).build();
EC2Api apiWhenDontExist = requestSendsResponse(conditionallyDelete, conditionallyDeleteResponse);
apiWhenDontExist.getTagApi().get().conditionallyDeleteFromResources(ImmutableMap.of("tag", "value"), ImmutableSet.of("i-43532"));
}
}

View File

@ -0,0 +1,164 @@
/**
* 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.ec2.features.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.ec2.features.TagApi;
import org.jclouds.ec2.internal.BaseEC2ApiLiveTest;
import org.jclouds.ec2.util.TagFilterBuilder;
import org.jclouds.predicates.RetryablePredicate;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* tests ability to tag, filter, and delete tags from a resource.
*
* @author Adrian Cole
*/
@Test(groups = "live")
public abstract class BaseTagApiLiveTest extends BaseEC2ApiLiveTest {
private Resource resource;
private Tag tag;
private Tag tag2;
public void testApplyTag() {
api().applyToResources(ImmutableSet.of("foo"), ImmutableSet.of(resource.id));
tag = api().filter(new TagFilterBuilder().resourceId(resource.id).key("foo").build()).get(0);
Logger.getAnonymousLogger().info("created tag: " + tag);
assertEquals(tag.getKey(), "foo");
assertEquals(tag.getResourceId(), resource.id);
assertEquals(tag.getResourceType(), resource.type);
assertFalse(tag.getValue().isPresent());
}
public void testApplyTagWithValue() {
api().applyToResources(ImmutableMap.of("type", "bar"), ImmutableSet.of(resource.id));
tag2 = api().filter(new TagFilterBuilder().resourceId(resource.id).key("type").build()).get(0);
Logger.getAnonymousLogger().info("created tag: " + tag2);
assertEquals(tag2.getKey(), "type");
assertEquals(tag2.getResourceId(), resource.id);
assertEquals(tag2.getResourceType(), resource.type);
assertEquals(tag2.getValue().get(), "bar");
}
@Test(dependsOnMethods = { "testApplyTag", "testApplyTagWithValue" })
protected void testList() {
assertTrue(new RetryablePredicate<Iterable<Tag>>(new Predicate<Iterable<Tag>>() {
@Override
public boolean apply(Iterable<Tag> input) {
return api().list().filter(new Predicate<Tag>() {
@Override
public boolean apply(Tag in) {
return in.getResourceId().equals(resource.id);
}
}).toImmutableSet().equals(input);
}
}, 600, 200, 200, TimeUnit.MILLISECONDS).apply(ImmutableSet.of(tag, tag2)));
}
@Test(dependsOnMethods = "testList")
public void testDeleteTags() {
// shouldn't delete with the incorrect values
api().conditionallyDeleteFromResources(ImmutableMap.of(tag.getKey(), "FOO", tag2.getKey(), "FOO"),
ImmutableSet.of(tag.getResourceId(), tag2.getResourceId()));
assertEquals(tagsForResource().size(), 2);
api().deleteFromResources(ImmutableSet.of(tag.getKey(), tag2.getKey()),
ImmutableSet.of(tag.getResourceId(), tag2.getResourceId()));
assertEquals(tagsForResource().size(), 0);
Logger.getAnonymousLogger().info("tags deleted: " + tag + ", " + tag2);
}
private FluentIterable<Tag> tagsForResource() {
return api().filter(new TagFilterBuilder().resourceId(resource.id).build());
}
@Override
@BeforeClass(groups = "live")
public void setupContext() {
super.setupContext();
resource = checkNotNull(createResourceForTagging(System.getProperty("user.name") + "-tag"), "resource");
}
public static class Resource {
public String id;
public String type;
/**
*
* @param id
* ex. {sg-abc23d}
* @param type
* a type listed in {@link Tag.ResourceType}
*/
public Resource(String id, String type) {
this.id = checkNotNull(id, "id");
this.type = checkNotNull(type, "type of %s", id);
}
}
protected abstract Resource createResourceForTagging(String prefix);
protected abstract void cleanupResource(Resource resource);
protected TagApi api() {
Optional<? extends TagApi> tagOption = context.getApi().getTagApi();
if (!tagOption.isPresent())
throw new SkipException("tag api not present");
return tagOption.get();
}
@AfterClass(groups = "live")
protected void tearDownContext() {
if (resource != null)
cleanupResource(resource);
super.tearDownContext();
}
}

View File

@ -0,0 +1,75 @@
/**
* 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.ec2.parse;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.ec2.domain.Tag;
import org.jclouds.ec2.xml.DescribeTagsResponseHandler;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit")
public class DescribeTagsResponseTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/describe_tags.xml");
FluentIterable<Tag> expected = expected();
DescribeTagsResponseHandler handler = injector.getInstance(DescribeTagsResponseHandler.class);
FluentIterable<Tag> result = factory.create(handler).parse(is);
assertEquals(result.toString(), expected.toString());
}
public FluentIterable<Tag> expected() {
return FluentIterable.from(ImmutableSet.<Tag>builder()
.add(Tag.builder()
.resourceId("i-5f4e3d2a")
.resourceType("instance")
.key("webserver")
.build())
.add(Tag.builder()
.resourceId("i-5f4e3d2a")
.resourceType("instance")
.key("stack")
.value("Production")
.build())
.add(Tag.builder()
.resourceId("i-12345678")
.resourceType("instance")
.key("database_server")
.build())
.add(Tag.builder()
.resourceId("i-12345678")
.resourceType("instance")
.key("stack")
.value("Test")
.build()).build());
}
}

View File

@ -0,0 +1,29 @@
<DescribeTagsResponse xmlns="http://ec2.amazonaws.com/doc/2012-10-01/">
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
<tagSet>
<item>
<resourceId>i-5f4e3d2a</resourceId>
<resourceType>instance</resourceType>
<key>webserver</key>
<value/>
</item>
<item>
<resourceId>i-5f4e3d2a</resourceId>
<resourceType>instance</resourceType>
<key>stack</key>
<value>Production</value>
</item>
<item>
<resourceId>i-12345678</resourceId>
<resourceType>instance</resourceType>
<key>database_server</key>
<value/>
</item>
<item>
<resourceId>i-12345678</resourceId>
<resourceType>instance</resourceType>
<key>stack</key>
<value>Test</value>
</item>
</tagSet>
</DescribeTagsResponse>

View File

@ -46,6 +46,8 @@ import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.config.EC2RestClientModule;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.ec2.features.TagApi;
import org.jclouds.ec2.features.TagAsyncApi;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.options.RunInstancesOptions;
@ -91,6 +93,7 @@ public class AWSEC2RestClientModule extends EC2RestClientModule<AWSEC2Client, AW
.put(SpotInstanceClient.class, SpotInstanceAsyncClient.class)//
.put(TagClient.class, TagAsyncClient.class)//
.put(WindowsApi.class, WindowsAsyncApi.class)//
.put(TagApi.class, TagAsyncApi.class)//
.build();
public AWSEC2RestClientModule() {

View File

@ -26,9 +26,14 @@ import com.google.common.base.Objects;
import com.google.common.collect.ComparisonChain;
/**
* <h3>Important</h3>
* This will be removed in jclouds version 1.6
*
* @see <a href= "http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-ItemType-TagSetItemType.html" />
* @see org.jclouds.ec2.domain.Tag
* @author grkvlt@apache.org
*/
@Deprecated
public class Tag implements Comparable<Tag> {
public static Builder builder() {
return new Builder();

View File

@ -26,13 +26,13 @@ import java.util.Set;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
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.FilterName;
import org.jclouds.aws.ec2.xml.DescribeTagsResponseHandler;
import org.jclouds.aws.filters.FormSigner;
import org.jclouds.ec2.binders.BindResourceIdsToIndexedFormParams;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
import org.jclouds.rest.annotations.BinderParam;
@ -50,8 +50,12 @@ import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides access to EC2 Tags via their REST API.
*
* <h3>Important</h3>
* This will be removed in jclouds version 1.6
*
* @author grkvlt@apache.org
*/
@Deprecated
@RequestFilters(FormSigner.class)
@VirtualHost
public interface TagAsyncClient {

View File

@ -28,11 +28,17 @@ 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.
*
* <h3>Important</h3>
* This will be removed in jclouds version 1.6
*
* @author grkvlt@apache.org
* @see TagApi
*/
@Deprecated
@Timeout(duration = 45, timeUnit = TimeUnit.SECONDS)
public interface TagClient {
/**

View File

@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Map;
import org.jclouds.ec2.features.TagApi;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@ -30,8 +32,14 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* <h3>Important</h3>
* This will be removed in jclouds version 1.6
*
* @author grkvlt@apache.org
*
* @see TagApi
*/
@Deprecated
public class TagFilters {
public static enum FilterName {
KEY, RESOURCE_ID, RESOURCE_TYPE, VALUE;
@ -54,6 +62,12 @@ public class TagFilters {
}
}
/**
* <h3>Important</h3> This will be removed in jclouds version 1.6
*
* @see TagApi
*/
@Deprecated
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;

View File

@ -0,0 +1,59 @@
/**
* 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.features;
import static org.jclouds.ec2.domain.Tag.ResourceType.SECURITY_GROUP;
import org.jclouds.aws.ec2.AWSEC2Client;
import org.jclouds.aws.ec2.services.AWSSecurityGroupClient;
import org.jclouds.ec2.features.internal.BaseTagApiLiveTest;
import org.testng.annotations.Test;
import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
@Test(groups = "live", singleThreaded = true)
public class TagSecurityGroupLiveTest extends BaseTagApiLiveTest {
public TagSecurityGroupLiveTest() {
provider = "aws-ec2";
}
@Override
protected Resource createResourceForTagging(String prefix) {
try {
return new Resource(securityGroupApi().createSecurityGroupInRegionAndReturnId(null, prefix, prefix),
SECURITY_GROUP);
} catch (IllegalStateException e) {
return new Resource(Iterables.get(securityGroupApi().describeSecurityGroupsInRegion(null, prefix), 0).getId(),
SECURITY_GROUP);
}
}
@Override
protected void cleanupResource(Resource resource) {
securityGroupApi().deleteSecurityGroupInRegionById(null, resource.id);
}
private AWSSecurityGroupClient securityGroupApi() {
return AWSEC2Client.class.cast(context.getApi()).getSecurityGroupServices();
}
}