complete route53 api

This commit is contained in:
adriancole 2013-01-31 00:47:23 -08:00
parent 964ae3e2aa
commit c993eda9fb
49 changed files with 1879 additions and 727 deletions

View File

@ -18,15 +18,15 @@
*/
package org.jclouds.aws.route53.features;
import org.jclouds.route53.features.ResourceRecordSetApiLiveTest;
import org.jclouds.route53.features.RecordSetApiLiveTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "AWSResourceRecordSetApiLiveTest")
public class AWSResourceRecordSetApiLiveTest extends ResourceRecordSetApiLiveTest {
public AWSResourceRecordSetApiLiveTest() {
public class AWSRecordSetApiLiveTest extends RecordSetApiLiveTest {
public AWSRecordSetApiLiveTest() {
provider = "aws-route53";
}
}

View File

@ -0,0 +1,45 @@
/**
* 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.route53;
import org.jclouds.http.HttpResponseException;
import com.google.common.collect.ImmutableList;
/**
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html"
* />
*
* @author Adrian Cole
*/
public class InvalidChangeBatchException extends IllegalArgumentException {
private static final long serialVersionUID = 1L;
private final ImmutableList<String> messages;
public InvalidChangeBatchException(ImmutableList<String> messages, HttpResponseException cause) {
super(messages.toString(), cause);
this.messages = messages;
}
public ImmutableList<String> getMessages() {
return messages;
}
}

View File

@ -22,7 +22,7 @@ import javax.ws.rs.PathParam;
import org.jclouds.rest.annotations.Delegate;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.features.ResourceRecordSetApi;
import org.jclouds.route53.features.RecordSetApi;
import org.jclouds.route53.features.ZoneApi;
/**
@ -52,8 +52,8 @@ public interface Route53Api {
ZoneApi getZoneApi();
/**
* Provides synchronous access to ResourceRecordSet features.
* Provides synchronous access to record set features.
*/
@Delegate
ResourceRecordSetApi getResourceRecordSetApiForZone(@PathParam("zoneId") String zoneId);
RecordSetApi getRecordSetApiForZone(@PathParam("zoneId") String zoneId);
}

View File

@ -18,8 +18,6 @@
*/
package org.jclouds.route53;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;

View File

@ -30,7 +30,7 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.features.ResourceRecordSetAsyncApi;
import org.jclouds.route53.features.RecordSetAsyncApi;
import org.jclouds.route53.features.ZoneAsyncApi;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.xml.ChangeHandler;
@ -67,8 +67,8 @@ public interface Route53AsyncApi {
ZoneAsyncApi getZoneApi();
/**
* Provides asynchronous access to ResourceRecordSet features.
* Provides asynchronous access to record set features.
*/
@Delegate
ResourceRecordSetAsyncApi getResourceRecordSetApiForZone(@PathParam("zoneId") String zoneId);
RecordSetAsyncApi getRecordSetApiForZone(@PathParam("zoneId") String zoneId);
}

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.route53.binders;
import static org.jclouds.io.Payloads.newStringPayload;
import javax.inject.Singleton;
import org.jclouds.http.HttpRequest;
import org.jclouds.io.Payload;
import org.jclouds.rest.Binder;
import org.jclouds.route53.domain.ChangeBatch;
import org.jclouds.route53.domain.ChangeBatch.ActionOnResourceRecordSet;
import org.jclouds.route53.functions.SerializeRRS;
/**
*
* @author Adrian Cole
*/
@Singleton
public class BindChangeBatch implements Binder {
private static final SerializeRRS xml = new SerializeRRS();
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object payload) {
ChangeBatch from = ChangeBatch.class.cast(payload);
StringBuilder b = new StringBuilder();
b.append("<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2012-02-29/\"><ChangeBatch>");
if (from.getComment().isPresent())
b.append("<Comment>").append(from.getComment().get()).append("</Comment>");
b.append("<Changes>");
for (ActionOnResourceRecordSet change : from)
b.append("<Change>").append("<Action>").append(change.getAction()).append("</Action>")
.append(xml.apply(change.getRRS())).append("</Change>");
b.append("</Changes>");
b.append("</ChangeBatch></ChangeResourceRecordSetsRequest>");
Payload xmlPayload = newStringPayload(b.toString());
xmlPayload.getContentMetadata().setContentType("application/xml");
return (R) request.toBuilder().payload(xmlPayload).build();
}
}

View File

@ -23,7 +23,7 @@ import javax.inject.Singleton;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequest.Builder;
import org.jclouds.rest.Binder;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.domain.RecordSetIterable.NextRecord;
/**
*

View File

@ -28,15 +28,20 @@ import javax.inject.Singleton;
import org.jclouds.aws.config.AWSRestClientModule;
import org.jclouds.date.DateService;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RequestSigner;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.Route53AsyncApi;
import org.jclouds.route53.features.ResourceRecordSetApi;
import org.jclouds.route53.features.ResourceRecordSetAsyncApi;
import org.jclouds.route53.features.RecordSetApi;
import org.jclouds.route53.features.RecordSetAsyncApi;
import org.jclouds.route53.features.ZoneApi;
import org.jclouds.route53.features.ZoneAsyncApi;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.handlers.Route53ErrorHandler;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Provides;
@ -50,7 +55,7 @@ import com.google.inject.Provides;
public class Route53RestClientModule extends AWSRestClientModule<Route53Api, Route53AsyncApi> {
public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()//
.put(ZoneApi.class, ZoneAsyncApi.class)
.put(ResourceRecordSetApi.class, ResourceRecordSetAsyncApi.class).build();
.put(RecordSetApi.class, RecordSetAsyncApi.class).build();
public Route53RestClientModule() {
super(typeToken(Route53Api.class), typeToken(Route53AsyncApi.class), DELEGATE_MAP);
@ -67,4 +72,11 @@ public class Route53RestClientModule extends AWSRestClientModule<Route53Api, Rou
RequestSigner provideRequestSigner(RestAuthentication in) {
return in;
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(Route53ErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(Route53ErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(Route53ErrorHandler.class);
}
}

View File

@ -94,8 +94,8 @@ public final class Change {
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Change other = Change.class.cast(obj);
return equal(this.id, other.id);
Change that = Change.class.cast(obj);
return equal(this.id, that.id);
}
@Override

View File

@ -0,0 +1,174 @@
/**
* 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.route53.domain;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import org.jclouds.route53.domain.ChangeBatch.ActionOnResourceRecordSet;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingList;
import com.google.common.collect.ImmutableList;
/**
*
* @author Adrian Cole
*/
public class ChangeBatch extends ForwardingList<ActionOnResourceRecordSet> {
public static ChangeBatch createAll(Iterable<RecordSet> toCreate) {
return builder().createAll(toCreate).build();
}
public static ChangeBatch deleteAll(Iterable<RecordSet> toDelete) {
return builder().deleteAll(toDelete).build();
}
private final Optional<String> comment;
private final List<ActionOnResourceRecordSet> changes;
public static enum Action {
CREATE, DELETE;
}
public static class ActionOnResourceRecordSet {
private final Action action;
private final RecordSet rrs;
private ActionOnResourceRecordSet(Action action, RecordSet rrs) {
this.action = action;
this.rrs = rrs;
}
public Action getAction() {
return action;
}
public RecordSet getRRS() {
return rrs;
}
@Override
public int hashCode() {
return Objects.hashCode(action, rrs);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ActionOnResourceRecordSet that = ActionOnResourceRecordSet.class.cast(obj);
return equal(this.action, that.rrs);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("action", action).add("rrs", rrs).toString();
}
}
private ChangeBatch(Optional<String> comment, ImmutableList<ActionOnResourceRecordSet> changes) {
this.comment = checkNotNull(comment, "comment");
this.changes = checkNotNull(changes, "changes%s", comment.isPresent() ? " for %s " + comment.get() : "");
checkArgument(!changes.isEmpty(), "no changes%s", comment.isPresent() ? " for %s " + comment.get() : "");
}
/**
* Any comments you want to include about the changes in this change batch.
*/
public Optional<String> getComment() {
return comment;
}
@Override
protected List<ActionOnResourceRecordSet> delegate() {
return changes;
}
@Override
public int hashCode() {
return Objects.hashCode(comment, changes);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ChangeBatch that = ChangeBatch.class.cast(obj);
return equal(this.comment, that.comment) && equal(this.changes, that.changes);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("comment", comment.orNull()).add("changes", changes).toString();
}
public static Builder builder() {
return new Builder();
}
public final static class Builder {
private Optional<String> comment = Optional.absent();
private ImmutableList.Builder<ActionOnResourceRecordSet> changes = ImmutableList.builder();
/**
* @see ChangeBatch#getComment()
*/
public Builder comment(String comment) {
this.comment = Optional.fromNullable(comment);
return this;
}
public Builder create(RecordSet rrs) {
this.changes.add(new ActionOnResourceRecordSet(Action.CREATE, rrs));
return this;
}
public Builder createAll(Iterable<RecordSet> toCreate) {
for (RecordSet rrs : toCreate)
create(rrs);
return this;
}
public Builder delete(RecordSet rrs) {
this.changes.add(new ActionOnResourceRecordSet(Action.DELETE, rrs));
return this;
}
public Builder deleteAll(Iterable<RecordSet> toDelete) {
for (RecordSet rrs : toDelete)
delete(rrs);
return this;
}
public ChangeBatch build() {
return new ChangeBatch(comment, changes.build());
}
}
}

View File

@ -0,0 +1,394 @@
/**
* 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.route53.domain;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Set;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Latency;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Weighted;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
/**
*
* @author Adrian Cole
*/
public class RecordSet {
protected final String name;
protected final Type type;
protected final Optional<Integer> ttl;
protected final Set<String> values;
protected final Optional<AliasTarget> aliasTarget;
public enum Type {
A, AAAA, CNAME, MX, NS, PTR, SOA, SPF, SRV, TXT;
}
/**
* In this case, the rrs is an alias, and it points to another Route53 hosted
* resource, such as an ELB, S3 bucket, or zone.
*/
public static class AliasTarget {
public static AliasTarget dnsNameInZone(String dnsName, String zoneId) {
return new AliasTarget(dnsName, zoneId);
}
private final String dnsName;
private final String zoneId;
private AliasTarget(String dnsName, String zoneId) {
this.dnsName = checkNotNull(dnsName, "dnsName");
this.zoneId = checkNotNull(zoneId, "zoneId of %s", dnsName);
}
public String getDNSName() {
return dnsName;
}
public String getZoneId() {
return zoneId;
}
@Override
public int hashCode() {
return Objects.hashCode(zoneId, dnsName);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
AliasTarget other = AliasTarget.class.cast(obj);
return equal(this.dnsName, other.dnsName) && equal(this.zoneId, other.zoneId);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("dnsName", dnsName).add("zoneId", zoneId).toString();
}
}
/**
* A portion of a RRs who share the same name and type
*/
public static abstract class RecordSubset extends RecordSet {
public static final class Weighted extends RecordSubset {
private final int weight;
private Weighted(String id, String name, Type type, int weight, Optional<Integer> ttl, Set<String> values,
Optional<AliasTarget> aliasTarget) {
super(id, name, type, ttl, values, aliasTarget);
this.weight = weight;
}
/**
* determines what portion of traffic for the current resource record
* set is routed to this subset.
*/
public int getWeight() {
return weight;
}
@Override
ToStringHelper differentiate(ToStringHelper in) {
return in.add("weight", weight);
}
}
public static final class Latency extends RecordSubset {
private final String region;
private Latency(String id, String name, Type type, String region, Optional<Integer> ttl, Set<String> values,
Optional<AliasTarget> aliasTarget) {
super(id, name, type, ttl, values, aliasTarget);
this.region = checkNotNull(region, "region of %s", name);
}
/**
* The Amazon EC2 region where the resource that is specified in this
* resource record set resides.
*/
public String getRegion() {
return region;
}
@Override
ToStringHelper differentiate(ToStringHelper in) {
return in.add("region", region);
}
}
private final String id;
private RecordSubset(String id, String name, Type type, Optional<Integer> ttl, Set<String> values,
Optional<AliasTarget> aliasTarget) {
super(name, type, ttl, values, aliasTarget);
this.id = checkNotNull(id, "id of %s", name);
}
/**
* The identifier that differentiates beyond {@code name} and {@code type}
*/
public String getId() {
return id;
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), id);
}
@Override
public boolean equals(Object obj) {
if (super.equals(obj) && obj instanceof RecordSubset) {
RecordSubset that = RecordSubset.class.cast(obj);
return equal(this.id, that.id);
}
return false;
}
abstract ToStringHelper differentiate(ToStringHelper in);
@Override
public String toString() {
return differentiate(toStringHelper("").omitNullValues().add("id", id).add("name", name).add("type", type))
.add("ttl", ttl.orNull()).add("values", values.isEmpty() ? null : values).add("aliasTarget", aliasTarget.orNull())
.toString();
}
}
private RecordSet(String name, Type type, Optional<Integer> ttl, Set<String> values, Optional<AliasTarget> aliasTarget) {
this.name = checkNotNull(name, "name");
this.type = checkNotNull(type, "type of %s", name);
this.ttl = checkNotNull(ttl, "ttl for %s", name);
this.values = checkNotNull(values, "values for %s", name);
this.aliasTarget = checkNotNull(aliasTarget, "aliasTarget for %s", aliasTarget);
}
/**
* The name of the domain.
*/
public String getName() {
return name;
}
/**
* The resource record set type.
*/
public Type getType() {
return type;
}
/**
* Present in all resource record sets except aliases. The resource record
* cache time to live (TTL), in seconds.
*/
public Optional<Integer> getTTL() {
return ttl;
}
/**
* Type-specific values that differentiates the RRs in this set. Empty if
* {@link #getType()} is {@link Type#A} or {@link Type#AAAA} and
* {@link #getAliasTarget} is present.
*/
public Set<String> getValues() {
return values;
}
/**
* When present, {@link #getType()} is {@link Type#A} or {@link Type#AAAA}.
* Instead of {@link #getValues()} containing the corresponding IP addresses,
* the server will follow this link and resolve one on-demand.
*/
public Optional<AliasTarget> getAliasTarget() {
return aliasTarget;
}
@Override
public int hashCode() {
return Objects.hashCode(name, type);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
RecordSet other = RecordSet.class.cast(obj);
return equal(this.name, other.name) && equal(this.type, other.type);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("name", name).add("type", type).add("ttl", ttl.orNull())
.add("values", values.isEmpty() ? null : values).add("aliasTarget", aliasTarget.orNull()).toString();
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().from(this);
}
public final static class Builder {
private String id;
private String name;
private Type type;
private Optional<Integer> ttl = Optional.absent();
private ImmutableSet.Builder<String> values = ImmutableSet.<String> builder();
private String dnsName;
private String zoneId;
private Integer weight;
private String region;
/**
* @see RecordSubset#getId()
*/
public Builder id(String id) {
this.id = id;
return this;
}
/**
* @see RecordSet#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see RecordSet#getType()
*/
public Builder type(Type type) {
this.type = type;
return this;
}
/**
* @see RecordSet#getTTL()
*/
public Builder ttl(Integer ttl) {
this.ttl = Optional.fromNullable(ttl);
return this;
}
/**
* @see RecordSet#getAliasTarget()
*/
public Builder dnsName(String dnsName) {
this.dnsName = dnsName;
return this;
}
/**
* @see RecordSet#getAliasTarget()
*/
public Builder zoneId(String zoneId) {
this.zoneId = zoneId;
return this;
}
/**
* @see RecordSet#getAliasTarget()
*/
public Builder aliasTarget(AliasTarget aliasTarget) {
if (aliasTarget == null) {
dnsName = null;
zoneId = null;
} else {
dnsName = aliasTarget.dnsName;
zoneId = aliasTarget.zoneId;
}
return this;
}
/**
* @see RecordSet#getValues()
*/
public Builder add(String values) {
this.values.add(values);
return this;
}
/**
* @see RecordSet#getValues()
*/
public Builder addAll(Iterable<String> values) {
this.values.addAll(values);
return this;
}
/**
* @see RecordSubset.Weighted
*/
public Builder weight(Integer weight) {
this.weight = weight;
return this;
}
/**
* @see RecordSubset.Latency
*/
public Builder region(String region) {
this.region = region;
return this;
}
public RecordSet build() {
Optional<AliasTarget> aliasTarget = dnsName != null ? Optional.fromNullable(AliasTarget.dnsNameInZone(dnsName, zoneId))
: Optional.<AliasTarget> absent();
if (weight != null) {
return new RecordSubset.Weighted(id, name, type, weight, ttl, values.build(), aliasTarget);
} else if (region != null) {
return new RecordSubset.Latency(id, name, type, region, ttl, values.build(), aliasTarget);
}
return new RecordSet(name, type, ttl, values.build(), aliasTarget);
}
public Builder from(RecordSet in) {
if (in instanceof RecordSubset)
id(RecordSubset.class.cast(in).id);
if (in instanceof Weighted) {
weight(Weighted.class.cast(in).weight);
} else if (in instanceof Latency) {
region(Latency.class.cast(in).region);
}
return this.name(in.name).type(in.type).ttl(in.ttl.orNull()).addAll(in.values)
.aliasTarget(in.aliasTarget.orNull());
}
}
}

View File

@ -27,7 +27,7 @@ import java.util.Iterator;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.RecordSet.Type;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
@ -37,12 +37,12 @@ import com.google.common.collect.ImmutableList;
*
* @author Adrian Cole
*/
public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecordSet> {
public class RecordSetIterable extends IterableWithMarker<RecordSet> {
private final Iterable<ResourceRecordSet> items;
private final Iterable<RecordSet> items;
private final Optional<NextRecord> nextRecord;
private ResourceRecordSetIterable(Iterable<ResourceRecordSet> items, @Nullable NextRecord nextRecord) {
private RecordSetIterable(Iterable<RecordSet> items, @Nullable NextRecord nextRecord) {
this.items = checkNotNull(items, "items");
this.nextRecord = Optional.fromNullable(nextRecord);
}
@ -61,7 +61,7 @@ public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecord
}
@Override
public Iterator<ResourceRecordSet> iterator() {
public Iterator<RecordSet> iterator() {
return items.iterator();
}
@ -76,7 +76,7 @@ public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecord
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ResourceRecordSetIterable that = ResourceRecordSetIterable.class.cast(obj);
RecordSetIterable that = RecordSetIterable.class.cast(obj);
return equal(this.items, that.items) && equal(this.nextRecord, that.nextRecord);
}
@ -139,9 +139,9 @@ public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecord
return true;
if (obj == null || getClass() != obj.getClass())
return false;
NextRecord other = NextRecord.class.cast(obj);
return equal(this.name, other.name) && equal(this.type, other.type)
&& equal(this.identifier, other.identifier);
NextRecord that = NextRecord.class.cast(obj);
return equal(this.name, that.name) && equal(this.type, that.type)
&& equal(this.identifier, that.identifier);
}
@Override
@ -157,17 +157,17 @@ public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecord
public static final class Builder {
private ImmutableList.Builder<ResourceRecordSet> items = ImmutableList.<ResourceRecordSet> builder();
private ImmutableList.Builder<RecordSet> items = ImmutableList.<RecordSet> builder();
private String nextRecordName;
private Type nextRecordType;
private String nextRecordIdentifier;
public Builder add(ResourceRecordSet item) {
public Builder add(RecordSet item) {
this.items.add(item);
return this;
}
public Builder addAll(Iterable<ResourceRecordSet> items) {
public Builder addAll(Iterable<RecordSet> items) {
this.items.addAll(items);
return this;
}
@ -187,10 +187,10 @@ public class ResourceRecordSetIterable extends IterableWithMarker<ResourceRecord
return this;
}
public ResourceRecordSetIterable build() {
public RecordSetIterable build() {
NextRecord nextRecord = nextRecordName != null ? new NextRecord(nextRecordName, nextRecordType,
nextRecordIdentifier) : null;
return new ResourceRecordSetIterable(items.build(), nextRecord);
return new RecordSetIterable(items.build(), nextRecord);
}
}
}

View File

@ -1,167 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.domain;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Objects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ForwardingList;
import com.google.common.collect.ImmutableList;
/**
*
* @author Adrian Cole
*/
public final class ResourceRecordSet extends ForwardingList<String> {
private final String name;
private final Type type;
private final Optional<Integer> ttl;
private final ImmutableList<String> items;
private ResourceRecordSet(String name, Type type, Optional<Integer> ttl, ImmutableList<String> items) {
this.name = checkNotNull(name, "name");
this.type = checkNotNull(type, "type of %s", name);
this.ttl = checkNotNull(ttl, "ttl for %s", name);
this.items = checkNotNull(items, "items for %s", name);
}
/**
* The name of the domain.
*/
public String getName() {
return name;
}
/**
* The resource record set type.
*/
public Type getType() {
return type;
}
/**
* Present in all resource record sets except aliases. The resource record
* cache time to live (TTL), in seconds.
*/
public Optional<Integer> getTTL() {
return ttl;
}
@Override
public int hashCode() {
return Objects.hashCode(name, type, ttl, items);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
ResourceRecordSet other = ResourceRecordSet.class.cast(obj);
return equal(this.name, other.name) && equal(this.type, other.type) && equal(this.ttl, other.ttl)
&& equal(this.items, other.items);
}
@Override
public String toString() {
return toStringHelper("").omitNullValues().add("name", name).add("type", type).add("ttl", ttl.orNull())
.add("resourceRecords", items).toString();
}
@Override
protected List<String> delegate() {
return items;
}
public enum Type {
A, AAAA, CNAME, MX, NS, PTR, SOA, SPF, SRV, TXT, UNRECOGNIZED;
public static Type fromValue(String type) {
try {
return valueOf(checkNotNull(type, "type"));
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().from(this);
}
public final static class Builder {
private String name;
private Type type;
private Optional<Integer> ttl = Optional.absent();
private ImmutableList.Builder<String> items = ImmutableList.<String> builder();
/**
* @see ResourceRecordSet#getName()
*/
public Builder name(String name) {
this.name = name;
return this;
}
/**
* @see ResourceRecordSet#getType()
*/
public Builder type(Type type) {
this.type = type;
return this;
}
/**
* @see ResourceRecordSet#getTTL()
*/
public Builder ttl(Integer ttl) {
this.ttl = Optional.fromNullable(ttl);
return this;
}
public Builder add(String item) {
this.items.add(item);
return this;
}
public Builder addAll(Iterable<String> items) {
this.items.addAll(items);
return this;
}
public ResourceRecordSet build() {
return new ResourceRecordSet(name, type, ttl, items.build());
}
public Builder from(ResourceRecordSet in) {
return this.name(in.name).type(in.type).ttl(in.ttl.orNull()).addAll(in.items);
}
}
}

View File

@ -88,9 +88,9 @@ public final class Zone {
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Zone other = Zone.class.cast(obj);
return equal(this.id, other.id) && equal(this.name, other.name)
&& equal(this.callerReference, other.callerReference);
Zone that = Zone.class.cast(obj);
return equal(this.id, that.id) && equal(this.name, that.name)
&& equal(this.callerReference, that.callerReference);
}
@Override

View File

@ -19,32 +19,55 @@
package org.jclouds.route53.features;
import org.jclouds.collect.PagedIterable;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.domain.ChangeBatch;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSetIterable;
import org.jclouds.route53.domain.RecordSetIterable.NextRecord;
/**
* @see ResourceRecordSetAsyncApi
* @see RecordSetAsyncApi
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/ActionsOnRRS.html"
* />
* @author Adrian Cole
*/
public interface ResourceRecordSetApi {
public interface RecordSetApi {
/**
* schedules creation of the resource record set.
*/
Change create(RecordSet rrs);
/**
* applies a batch of changes atomically.
*/
Change apply(ChangeBatch changes);
/**
* returns all resource record sets in order.
*/
PagedIterable<ResourceRecordSet> list();
PagedIterable<RecordSet> list();
/**
* retrieves up to 100 resource record sets in order.
*/
ResourceRecordSetIterable listFirstPage();
RecordSetIterable listFirstPage();
/**
* retrieves up to 100 resource record sets in order, starting at {@code nextRecord}
* retrieves up to 100 resource record sets in order, starting at
* {@code nextRecord}
*/
ResourceRecordSetIterable listAt(NextRecord nextRecord);
RecordSetIterable listAt(NextRecord nextRecord);
/**
* This action deletes a resource record set.
*
* @param rrs
* the resource record set to delete
* @return null if not found or the change in progress
*/
@Nullable
Change delete(RecordSet rrs);
}

View File

@ -0,0 +1,126 @@
/**
* 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.route53.features;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.collect.PagedIterable;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.Payload;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.route53.binders.BindChangeBatch;
import org.jclouds.route53.binders.BindNextRecord;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.domain.ChangeBatch;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSetIterable;
import org.jclouds.route53.domain.RecordSetIterable.NextRecord;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.functions.RecordSetIterableToPagedIterable;
import org.jclouds.route53.functions.SerializeRRS;
import org.jclouds.route53.xml.ChangeHandler;
import org.jclouds.route53.xml.ListResourceRecordSetsResponseHandler;
import com.google.common.util.concurrent.ListenableFuture;
/**
* @see RecordSetApi
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/ActionsOnRRS.html"
* />
* @author Adrian Cole
*/
@RequestFilters(RestAuthentication.class)
@VirtualHost
@Path("/{jclouds.api-version}/hostedzone/{zoneId}")
public interface RecordSetAsyncApi {
/**
* @see RecordSetApi#create
*/
@Named("ChangeResourceRecordSets")
@POST
@Produces(APPLICATION_XML)
@Path("/rrset")
@Payload("<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2012-02-29/\"><ChangeBatch><Changes><Change><Action>CREATE</Action>{rrs}</Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>")
@XMLResponseParser(ChangeHandler.class)
ListenableFuture<Change> create(@PayloadParam("rrs") @ParamParser(SerializeRRS.class) RecordSet rrs);
/**
* @see RecordSetApi#apply
*/
@Named("ChangeResourceRecordSets")
@POST
@Produces(APPLICATION_XML)
@Path("/rrset")
@XMLResponseParser(ChangeHandler.class)
ListenableFuture<Change> apply(@BinderParam(BindChangeBatch.class) ChangeBatch changes);
/**
* @see RecordSetApi#list()
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
@Transform(RecordSetIterableToPagedIterable.class)
ListenableFuture<PagedIterable<RecordSet>> list();
/**
* @see RecordSetApi#listFirstPage
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<RecordSetIterable> listFirstPage();
/**
* @see RecordSetApi#listAt(NextRecord)
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<RecordSetIterable> listAt(@BinderParam(BindNextRecord.class) NextRecord nextRecord);
/**
* @see RecordSetApi#delete
*/
@Named("ChangeResourceRecordSets")
@POST
@Produces(APPLICATION_XML)
@Path("/rrset")
@Payload("<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2012-02-29/\"><ChangeBatch><Changes><Change><Action>DELETE</Action>{rrs}</Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>")
@XMLResponseParser(ChangeHandler.class)
@Fallback(NullOnNotFoundOr404.class)
ListenableFuture<Change> delete(@PayloadParam("rrs") @ParamParser(SerializeRRS.class) RecordSet rrs);
}

View File

@ -1,80 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.jclouds.collect.PagedIterable;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.annotations.XMLResponseParser;
import org.jclouds.route53.binders.BindNextRecord;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.functions.ResourceRecordSetsToPagedIterable;
import org.jclouds.route53.xml.ListResourceRecordSetsResponseHandler;
import com.google.common.util.concurrent.ListenableFuture;
/**
* @see ResourceRecordSetApi
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/ActionsOnRRS.html"
* />
* @author Adrian Cole
*/
@RequestFilters(RestAuthentication.class)
@VirtualHost
@Path("/{jclouds.api-version}/hostedzone/{zoneId}")
public interface ResourceRecordSetAsyncApi {
/**
* @see ResourceRecordSetApi#list()
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
@Transform(ResourceRecordSetsToPagedIterable.class)
ListenableFuture<PagedIterable<ResourceRecordSet>> list();
/**
* @see ResourceRecordSetApi#listFirstPage
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<ResourceRecordSetIterable> listFirstPage();
/**
* @see ResourceRecordSetApi#listAt(NextRecord)
*/
@Named("ListResourceRecordSets")
@GET
@Path("/rrset")
@XMLResponseParser(ListResourceRecordSetsResponseHandler.class)
ListenableFuture<ResourceRecordSetIterable> listAt(@BinderParam(BindNextRecord.class) NextRecord nextRecord);
}

View File

@ -26,7 +26,6 @@ import org.jclouds.route53.domain.Change.Status;
import org.jclouds.route53.domain.NewZone;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.domain.ZoneAndNameServers;
import org.jclouds.route53.options.ListZonesOptions;
/**
* @see ZoneAsyncApi
@ -60,23 +59,21 @@ public interface ZoneApi {
*/
NewZone createWithReferenceAndComment(String name, String callerReference, String comment);
/**
* The action retrieves a specified number of zones in order.
*
* <br/>
* You can paginate the results using the
* {@link ListZonesOptions parameter}
*
* @param options
* the options describing the zones query
*/
IterableWithMarker<Zone> list(ListZonesOptions options);
/**
* returns all zones in order.
*/
PagedIterable<Zone> list();
/**
* retrieves up to 100 zones in order.
*/
IterableWithMarker<Zone> listFirstPage();
/**
* retrieves up to 100 zones in order, starting at {@code nextMarker}
*/
IterableWithMarker<Zone> listAt(String nextMarker);
/**
* Retrieves information about the specified zone, including its nameserver
* configuration

View File

@ -27,6 +27,7 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.collect.IterableWithMarker;
@ -44,7 +45,6 @@ import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.domain.ZoneAndNameServers;
import org.jclouds.route53.filters.RestAuthentication;
import org.jclouds.route53.functions.ZonesToPagedIterable;
import org.jclouds.route53.options.ListZonesOptions;
import org.jclouds.route53.xml.ChangeHandler;
import org.jclouds.route53.xml.CreateHostedZoneResponseHandler;
import org.jclouds.route53.xml.GetHostedZoneResponseHandler;
@ -98,13 +98,22 @@ public interface ZoneAsyncApi {
ListenableFuture<PagedIterable<Zone>> list();
/**
* @see ZoneApi#list(ListZonesOptions)
* @see ZoneApi#listFirstPage
*/
@Named("ListHostedZones")
@GET
@Path("/hostedzone")
@XMLResponseParser(ListHostedZonesResponseHandler.class)
ListenableFuture<IterableWithMarker<Zone>> list(ListZonesOptions options);
ListenableFuture<IterableWithMarker<Zone>> listFirstPage();
/**
* @see ZoneApi#listAt(String)
*/
@Named("ListHostedZones")
@GET
@Path("/hostedzone")
@XMLResponseParser(ListHostedZonesResponseHandler.class)
ListenableFuture<IterableWithMarker<Zone>> listAt(@QueryParam("marker") String nextMarker);
/**
* @see ZoneApi#get()

View File

@ -25,9 +25,9 @@ import javax.inject.Inject;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.internal.CallerArg0ToPagedIterable;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.features.ResourceRecordSetApi;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSetIterable.NextRecord;
import org.jclouds.route53.features.RecordSetApi;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
@ -36,23 +36,23 @@ import com.google.common.base.Function;
* @author Adrian Cole
*/
@Beta
public class ResourceRecordSetsToPagedIterable extends
CallerArg0ToPagedIterable<ResourceRecordSet, ResourceRecordSetsToPagedIterable> {
public class RecordSetIterableToPagedIterable extends
CallerArg0ToPagedIterable<RecordSet, RecordSetIterableToPagedIterable> {
private final Route53Api api;
@Inject
protected ResourceRecordSetsToPagedIterable(Route53Api api) {
protected RecordSetIterableToPagedIterable(Route53Api api) {
this.api = checkNotNull(api, "api");
}
@Override
protected Function<Object, IterableWithMarker<ResourceRecordSet>> markerToNextForCallingArg0(String zoneId) {
final ResourceRecordSetApi resourceRecordSetApi = api.getResourceRecordSetApiForZone(zoneId);
return new Function<Object, IterableWithMarker<ResourceRecordSet>>() {
protected Function<Object, IterableWithMarker<RecordSet>> markerToNextForCallingArg0(String zoneId) {
final RecordSetApi resourceRecordSetApi = api.getRecordSetApiForZone(zoneId);
return new Function<Object, IterableWithMarker<RecordSet>>() {
@Override
public IterableWithMarker<ResourceRecordSet> apply(Object input) {
public IterableWithMarker<RecordSet> apply(Object input) {
return resourceRecordSetApi.listAt(NextRecord.class.cast(input));
}

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.route53.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.RecordSubset;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Latency;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Weighted;
import com.google.common.base.Function;
/**
* @author Adrian Cole
* @see ParamParser
*/
public class SerializeRRS implements Function<Object, String> {
@Override
public String apply(Object in) {
RecordSet rrs = RecordSet.class.cast(checkNotNull(in, "rrs"));
StringBuilder builder = new StringBuilder().append("<ResourceRecordSet>");
builder.append("<Name>").append(rrs.getName()).append("</Name>");
builder.append("<Type>").append(rrs.getType()).append("</Type>");
if (rrs instanceof RecordSubset) {
String id = RecordSubset.class.cast(rrs).getId();
builder.append("<ResourceRecordSubset>").append(id).append("</ResourceRecordSubset>");
}
if (rrs instanceof Weighted)
builder.append("<Weight>").append(Weighted.class.cast(rrs).getWeight()).append("</Weight>");
if (rrs instanceof Latency)
builder.append("<Region>").append(Latency.class.cast(rrs).getRegion()).append("</Region>");
if (rrs.getAliasTarget().isPresent()) {
builder.append("<AliasTarget>");
builder.append("<HostedZoneId>").append(rrs.getAliasTarget().get().getZoneId()).append("</HostedZoneId>");
builder.append("<DNSName>").append(rrs.getAliasTarget().get().getDNSName()).append("</DNSName>");
builder.append("</AliasTarget>");
} else {
builder.append("<TTL>").append(rrs.getTTL().or(0)).append("</TTL>");
builder.append("<ResourceRecords>");
for (String record : rrs.getValues())
builder.append("<ResourceRecord>").append("<Value>").append(record).append("</Value>")
.append("</ResourceRecord>");
builder.append("</ResourceRecords>");
}
return builder.append("</ResourceRecordSet>").toString();
}
}

View File

@ -27,7 +27,6 @@ import org.jclouds.collect.internal.CallerArg0ToPagedIterable;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.features.ZoneApi;
import org.jclouds.route53.options.ListZonesOptions;
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
@ -52,7 +51,7 @@ public class ZonesToPagedIterable extends CallerArg0ToPagedIterable<Zone, ZonesT
@Override
public IterableWithMarker<Zone> apply(Object input) {
return zoneApi.list(ListZonesOptions.Builder.afterMarker(input.toString()));
return zoneApi.listAt(input.toString());
}
@Override

View File

@ -0,0 +1,102 @@
/**
* 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.route53.handlers;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import static org.jclouds.http.HttpUtils.releasePayload;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.xml.ErrorHandler;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.functions.ParseSax.Factory;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.InsufficientResourcesException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.route53.InvalidChangeBatchException;
import org.jclouds.route53.xml.InvalidChangeBatchHandler;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Singleton
public class Route53ErrorHandler implements HttpErrorHandler {
private final Factory factory;
private final Provider<ErrorHandler> handlers;
private final Provider<InvalidChangeBatchHandler> batchHandlers;
@Inject
Route53ErrorHandler(Factory factory, Provider<ErrorHandler> handlers,
Provider<InvalidChangeBatchHandler> batchHandlers) {
this.factory = factory;
this.handlers = handlers;
this.batchHandlers = batchHandlers;
}
public void handleError(HttpCommand command, HttpResponse response) {
Exception exception = new HttpResponseException(command, response);
try {
byte[] data = closeClientButKeepContentStream(response);
String message = data != null ? new String(data) : null;
if (message != null) {
exception = new HttpResponseException(command, response, message);
if (message.indexOf("ErrorResponse") != -1) {
AWSError error = factory.create(handlers.get()).parse(message);
exception = refineException(new AWSResponseException(command, response, error));
} else if (message.indexOf("InvalidChangeBatch") != -1) {
ImmutableList<String> errors = factory.create(batchHandlers.get()).parse(message);
exception = new InvalidChangeBatchException(errors, new HttpResponseException(command, response));
}
}
} finally {
releasePayload(response);
command.setException(exception);
}
}
private Exception refineException(AWSResponseException in) {
int statusCode = in.getResponse().getStatusCode();
String errorCode = in.getError().getCode();
String message = in.getError().getMessage();
if (statusCode == 403 || "RequestExpired".equals(errorCode))
return new AuthorizationException(message, in);
if (statusCode == 400) {
if (ImmutableSet.of("InvalidAction", "AccessDenied").contains(errorCode))
return new UnsupportedOperationException(message, in);
else if ("Throttling".equals(errorCode))
return new InsufficientResourcesException(message, in);
else if (message.indexOf("not found") != -1)
return new ResourceNotFoundException(message, in);
return new IllegalArgumentException(message, in);
}
return in;
}
}

View File

@ -1,121 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.options;
import org.jclouds.http.options.BaseHttpRequestOptions;
import com.google.common.base.Objects;
import com.google.common.collect.Multimap;
/**
* Options used to list available zones.
*
* @see <a href=
* "http://docs.aws.amazon.com/Route53/latest/APIReference/API_ListHostedZones.html"
* />
*
* @author Adrian Cole
*/
public class ListZonesOptions extends BaseHttpRequestOptions implements Cloneable {
private Integer maxItems;
private Object afterMarker;
/**
* corresponds to {@link org.jclouds.collect.IterableWithMarker#nextMarker}
*/
public ListZonesOptions afterMarker(Object afterMarker) {
this.afterMarker = afterMarker;
return this;
}
/**
* Use this parameter only when paginating results to indicate the maximum
* number of zone names you want in the response. If there are
* additional zone names beyond the maximum you specify, the
* IsTruncated response element is true.
*/
public ListZonesOptions maxItems(Integer maxItems) {
this.maxItems = maxItems;
return this;
}
public static class Builder {
/**
* @see ListZonesOptions#afterMarker
*/
public static ListZonesOptions afterMarker(Object afterMarker) {
return new ListZonesOptions().afterMarker(afterMarker);
}
/**
* @see ListZonesOptions#maxItems
*/
public static ListZonesOptions maxItems(Integer maxItems) {
return new ListZonesOptions().maxItems(maxItems);
}
}
@Override
public Multimap<String, String> buildQueryParameters() {
Multimap<String, String> params = super.buildQueryParameters();
if (afterMarker != null)
params.put("marker", afterMarker.toString());
if (maxItems != null)
params.put("maxitems", maxItems.toString());
return params;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Objects.hashCode(afterMarker, maxItems);
}
@Override
public ListZonesOptions clone() {
return new ListZonesOptions().afterMarker(afterMarker).maxItems(maxItems);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ListZonesOptions other = ListZonesOptions.class.cast(obj);
return Objects.equal(this.afterMarker, other.afterMarker) && Objects.equal(this.maxItems, other.maxItems);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return Objects.toStringHelper(this).omitNullValues().add("afterMarker", afterMarker).add("maxItems", maxItems).toString();
}
}

View File

@ -0,0 +1,53 @@
/**
* 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.route53.predicates;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.Type;
import com.google.common.base.Predicate;
/**
* Predicates handy when working with ResourceRecordSet Types
*
* @author Adrian Cole
*/
public class RecordSetPredicates {
/**
* matches zones of the given type
*/
public static Predicate<RecordSet> typeEquals(final Type type) {
checkNotNull(type, "type must be defined");
return new Predicate<RecordSet>() {
@Override
public boolean apply(RecordSet zone) {
return type.equals(zone.getType());
}
@Override
public String toString() {
return "typeEquals(" + type + ")";
}
};
}
}

View File

@ -0,0 +1,52 @@
/**
* 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.route53.predicates;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.route53.domain.Zone;
import com.google.common.base.Predicate;
/**
* Predicates handy when working with Zones
*
* @author Adrian Cole
*/
public class ZonePredicates {
/**
* matches zones of the given name
*/
public static Predicate<Zone> nameEquals(final String name) {
checkNotNull(name, "name must be defined");
return new Predicate<Zone>() {
@Override
public boolean apply(Zone zone) {
return name.equals(zone.getName());
}
@Override
public String toString() {
return "nameEquals(" + name + ")";
}
};
}
}

View File

@ -0,0 +1,53 @@
/**
* 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.route53.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import org.jclouds.http.functions.ParseSax;
import com.google.common.collect.ImmutableList;
/**
*
* @author Adrian Cole
*/
public class InvalidChangeBatchHandler extends ParseSax.HandlerForGeneratedRequestWithResult<ImmutableList<String>> {
private StringBuilder currentText = new StringBuilder();
private ImmutableList.Builder<String> builder = ImmutableList.builder();
@Override
public ImmutableList<String> getResult() {
return builder.build();
}
@Override
public void endElement(String uri, String name, String qName) {
if (qName.equals("Message")) {
builder.add(currentOrNull(currentText));
}
currentText = new StringBuilder();
}
@Override
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -22,9 +22,9 @@ import static org.jclouds.util.SaxUtils.currentOrNull;
import static org.jclouds.util.SaxUtils.equalsOrSuffix;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.ResourceRecordSetIterable.Builder;
import org.jclouds.route53.domain.RecordSet.Type;
import org.jclouds.route53.domain.RecordSetIterable;
import org.jclouds.route53.domain.RecordSetIterable.Builder;
import org.xml.sax.Attributes;
import com.google.inject.Inject;
@ -37,12 +37,12 @@ import com.google.inject.Inject;
* @author Adrian Cole
*/
public class ListResourceRecordSetsResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<ResourceRecordSetIterable> {
ParseSax.HandlerForGeneratedRequestWithResult<RecordSetIterable> {
private final ResourceRecordSetHandler resourceRecordSetHandler;
private StringBuilder currentText = new StringBuilder();
private Builder builder = ResourceRecordSetIterable.builder();
private Builder builder = RecordSetIterable.builder();
private boolean inResourceRecordSets;
@ -52,11 +52,11 @@ public class ListResourceRecordSetsResponseHandler extends
}
@Override
public ResourceRecordSetIterable getResult() {
public RecordSetIterable getResult() {
try {
return builder.build();
} finally {
builder = ResourceRecordSetIterable.builder();
builder = RecordSetIterable.builder();
}
}

View File

@ -21,25 +21,25 @@ package org.jclouds.route53.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.Type;
import org.xml.sax.Attributes;
/**
*
* @author Adrian Cole
*/
public class ResourceRecordSetHandler extends ParseSax.HandlerForGeneratedRequestWithResult<ResourceRecordSet> {
public class ResourceRecordSetHandler extends ParseSax.HandlerForGeneratedRequestWithResult<RecordSet> {
private StringBuilder currentText = new StringBuilder();
private ResourceRecordSet.Builder builder = ResourceRecordSet.builder();
private RecordSet.Builder builder = RecordSet.builder();
@Override
public ResourceRecordSet getResult() {
public RecordSet getResult() {
try {
return builder.build();
} finally {
builder = ResourceRecordSet.builder();
builder = RecordSet.builder();
}
}
@ -57,6 +57,16 @@ public class ResourceRecordSetHandler extends ParseSax.HandlerForGeneratedReques
builder.ttl(Integer.parseInt(currentOrNull(currentText)));
} else if (qName.equals("Value")) {
builder.add(currentOrNull(currentText));
} else if (qName.equals("HostedZoneId")) {
builder.zoneId(currentOrNull(currentText));
} else if (qName.equals("SetIdentifier")) {
builder.id(currentOrNull(currentText));
} else if (qName.equals("DNSName")) {
builder.dnsName(currentOrNull(currentText));
} else if (qName.equals("Weight")) {
builder.weight(Integer.parseInt(currentOrNull(currentText)));
} else if (qName.equals("Region")) {
builder.region(currentOrNull(currentText));
}
currentText = new StringBuilder();
}

View File

@ -38,7 +38,7 @@ public class Route53ApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.build();
HttpResponse getChangeResponse = HttpResponse.builder().statusCode(200)

View File

@ -0,0 +1,147 @@
/**
* 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.route53.features;
import static org.testng.Assert.assertEquals;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.route53.InvalidChangeBatchException;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.ChangeBatch;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.Type;
import org.jclouds.route53.domain.RecordSetIterable.NextRecord;
import org.jclouds.route53.internal.BaseRoute53ApiExpectTest;
import org.jclouds.route53.parse.GetChangeResponseTest;
import org.jclouds.route53.parse.ListResourceRecordSetsResponseTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ResourceRecordSetApiExpectTest")
public class RecordSetApiExpectTest extends BaseRoute53ApiExpectTest {
HttpRequest create = HttpRequest.builder().method("POST")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", authForDate)
.payload(payloadFromResourceWithContentType("/create_rrs_request.xml", "application/xml")).build();
HttpResponse jobResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/change.xml", "text/xml")).build();
public void testCreateWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(create, jobResponse);
assertEquals(success.getRecordSetApiForZone("Z1PA6795UKMFR9").create(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my texts").build()).toString(),
new GetChangeResponseTest().expected().toString());
}
HttpRequest apply = HttpRequest.builder().method("POST")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", authForDate)
.payload(payloadFromResourceWithContentType("/batch_rrs_request.xml", "application/xml")).build();
public void testApplyWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(apply, jobResponse);
assertEquals(success.getRecordSetApiForZone("Z1PA6795UKMFR9").apply(
ChangeBatch.builder()
.delete(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my texts").build())
.create(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my better texts").build())
.build()).toString(),
new GetChangeResponseTest().expected().toString());
}
@Test(expectedExceptions = InvalidChangeBatchException.class, expectedExceptionsMessageRegExp = "\\[Tried to create resource record set duplicate.example.com. type A, but it already exists, Tried to delete resource record set noexist.example.com. type A, but it was not found\\]")
public void testApplyWhenResponseIs4xx() {
HttpResponse batchErrorFound = HttpResponse.builder().statusCode(400)
.payload(payloadFromResourceWithContentType("/invalid_change_batch.xml", "application/xml")).build();
Route53Api fails = requestSendsResponse(apply, batchErrorFound);
fails.getRecordSetApiForZone("Z1PA6795UKMFR9").apply(
ChangeBatch.builder()
.delete(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my texts").build())
.create(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my better texts").build())
.build());
}
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", authForDate).build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/rrsets.xml", "text/xml")).build();
public void testListWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(list, listResponse);
assertEquals(success.getRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toString(), new ListResourceRecordSetsResponseTest().expected()
.toString());
}
// TODO: this should really be an empty set
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testListWhenResponseIs404() {
Route53Api fail = requestSendsResponse(list, notFound);
assertEquals(fail.getRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toSet(), ImmutableSet.of());
}
HttpRequest listAt = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset?name=testdoc2.example.com&type=NS")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", authForDate).build();
public void testListAtWhenResponseIs2xx() {
Route53Api apiWhenAtExist = requestSendsResponse(listAt, listResponse);
NextRecord next = NextRecord.nameAndType("testdoc2.example.com", Type.NS);
assertEquals(apiWhenAtExist.getRecordSetApiForZone("Z1PA6795UKMFR9").listAt(next).toString(),
new ListResourceRecordSetsResponseTest().expected().toString());
}
public void testList2PagesWhenResponseIs2xx() {
HttpResponse noMore = HttpResponse.builder().statusCode(200)
.payload(payloadFromStringWithContentType("<ListResourceRecordSetsResponse />", "text/xml")).build();
Route53Api success = requestsSendResponses(list, listResponse, listAt, noMore);
assertEquals(success.getRecordSetApiForZone("Z1PA6795UKMFR9").list().concat().toSet(), new ListResourceRecordSetsResponseTest().expected()
.toSet());
}
HttpRequest delete = HttpRequest.builder().method("POST")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", authForDate)
.payload(payloadFromResourceWithContentType("/delete_rrs_request.xml", "application/xml")).build();
public void testDeleteWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(delete, jobResponse);
assertEquals(success.getRecordSetApiForZone("Z1PA6795UKMFR9").create(RecordSet.builder().name("jclouds.org.").type(Type.TXT).add("my texts").build()).toString(),
new GetChangeResponseTest().expected().toString());
}
}

View File

@ -0,0 +1,172 @@
/**
* 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.route53.features;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Predicates.or;
import static java.util.logging.Logger.getAnonymousLogger;
import static org.jclouds.route53.domain.ChangeBatch.createAll;
import static org.jclouds.route53.domain.ChangeBatch.deleteAll;
import static org.jclouds.route53.domain.RecordSet.Type.NS;
import static org.jclouds.route53.domain.RecordSet.Type.SOA;
import static org.jclouds.route53.domain.RecordSet.Type.TXT;
import static org.jclouds.route53.predicates.RecordSetPredicates.typeEquals;
import static org.jclouds.route53.predicates.ZonePredicates.nameEquals;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.jclouds.JcloudsVersion;
import org.jclouds.collect.PagedIterable;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.domain.NewZone;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.RecordSubset;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Latency;
import org.jclouds.route53.domain.RecordSet.RecordSubset.Weighted;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.internal.BaseRoute53ApiLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "ResourceRecordSetApiLiveTest")
public class RecordSetApiLiveTest extends BaseRoute53ApiLiveTest {
private void checkRRS(RecordSet rrs) {
checkNotNull(rrs.getName(), "Name: ResourceRecordSet %s", rrs);
checkNotNull(rrs.getType(), "Type: ResourceRecordSet %s", rrs);
checkNotNull(rrs.getTTL(), "TTL: ResourceRecordSet %s", rrs);
checkNotNull(rrs.getAliasTarget(), "AliasTarget: ResourceRecordSet %s", rrs);
if (rrs.getAliasTarget().isPresent()) {
assertTrue(rrs.getValues().isEmpty(), "Values present on aliasTarget ResourceRecordSet: " + rrs);
} else {
assertTrue(!rrs.getValues().isEmpty(), "Values absent on ResourceRecordSet: " + rrs);
}
if (rrs instanceof RecordSubset) {
checkNotNull(RecordSubset.class.cast(rrs).getId(), "Id: ResourceRecordSubset %s", rrs);
}
if (rrs instanceof Weighted) {
assertTrue(Weighted.class.cast(rrs).getWeight() >= 0, "Weight negative: ResourceRecordSubset " + rrs);
}
if (rrs instanceof Latency) {
checkNotNull(Latency.class.cast(rrs).getRegion(), "Region: ResourceRecordSubset %s", rrs);
}
}
@Test
private void testListRRSs() {
for (Zone zone : zones().concat()) {
checkAllRRs(zone.getId());
}
}
private void checkAllRRs(String zoneId) {
Zone zone = context.getApi().getZoneApi().get(zoneId).getZone();
List<RecordSet> records = api(zone.getId()).list().concat().toList();
assertEquals(zone.getResourceRecordSetCount(), records.size());
for (RecordSet rrs : records) {
checkRRS(rrs);
}
}
@Test
public void testDeleteRRSNotFound() {
for (Zone zone : zones().concat()) {
assertNull(api(zone.getId()).delete(
RecordSet.builder().name("krank.foo.bar.").type(TXT).add("kranko").build()));
}
}
/**
* cannot delete a zone without at least one of each
*/
private static final Predicate<RecordSet> requiredRRTypes = or(typeEquals(SOA), typeEquals(NS));
@Test
public void testCreateAndDeleteBulkRecords() {
String name = System.getProperty("user.name").replace('.', '-') + ".bulk.route53test.jclouds.org.";
clearAndDeleteZonesNamed(name);
ImmutableList<RecordSet> records = ImmutableList.<RecordSet> builder()
.add(RecordSet.builder().name("dom1." + name).type(TXT).add("\"somehow\" \" somewhere\"").build())
.add(RecordSet.builder().name("dom2." + name).type(TXT).add("\"goodies\"").build()).build();
String nonce = name + " @ " + new Date();
String comment = name + " for " + JcloudsVersion.get();
NewZone newZone = context.getApi().getZoneApi().createWithReferenceAndComment(name, nonce, comment);
String zoneId = newZone.getZone().getId();
getAnonymousLogger().info("created zone: " + newZone);
try {
assertTrue(inSync.apply(newZone.getChange()), "zone didn't sync " + newZone);
sync(api(zoneId).apply(createAll(records)));
checkAllRRs(zoneId);
sync(api(zoneId).apply(deleteAll(records)));
PagedIterable<RecordSet> refreshed = refresh(zoneId);
assertTrue(refreshed.concat().filter(not(requiredRRTypes)).isEmpty(), "zone still has optional records: "
+ refreshed);
} finally {
clearAndDeleteZonesNamed(name);
}
}
private void clearAndDeleteZonesNamed(String name) {
for (Zone zone : context.getApi().getZoneApi().list().concat().filter(nameEquals(name))) {
getAnonymousLogger().info("clearing and deleting zone: " + zone);
Set<RecordSet> remaining = refresh(zone.getId()).concat().filter(not(requiredRRTypes)).toSet();
if (!remaining.isEmpty())
sync(api(zone.getId()).apply(deleteAll(remaining)));
sync(context.getApi().getZoneApi().delete(zone.getId()));
}
}
private void sync(Change job) {
assertTrue(inSync.apply(job), "job didn't sync " + job);
}
private PagedIterable<RecordSet> refresh(String zoneId) {
return api(zoneId).list();
}
private PagedIterable<Zone> zones() {
PagedIterable<Zone> zones = context.getApi().getZoneApi().list();
if (zones.get(0).isEmpty())
throw new SkipException("no zones in context: " + context);
return zones;
}
private RecordSetApi api(String zoneId) {
return context.getApi().getRecordSetApiForZone(zoneId);
}
}

View File

@ -1,88 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.route53.features;
import static org.testng.Assert.assertEquals;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable.NextRecord;
import org.jclouds.route53.internal.BaseRoute53ApiExpectTest;
import org.jclouds.route53.parse.ListResourceRecordSetsResponseTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ResourceRecordSetApiExpectTest")
public class ResourceRecordSetApiExpectTest extends BaseRoute53ApiExpectTest {
HttpRequest list = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
.build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/rrsets.xml", "text/xml")).build();
public void testListWhenResponseIs2xx() {
Route53Api success = requestSendsResponse(list, listResponse);
assertEquals(success.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toString(), new ListResourceRecordSetsResponseTest().expected()
.toString());
}
// TODO: this should really be an empty set
@Test(expectedExceptions = ResourceNotFoundException.class)
public void testListWhenResponseIs404() {
Route53Api fail = requestSendsResponse(list, notFound);
assertEquals(fail.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().get(0).toSet(), ImmutableSet.of());
}
HttpRequest listAt = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset?name=testdoc2.example.com&type=NS")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
.build();
public void testListAtWhenResponseIs2xx() {
Route53Api apiWhenAtExist = requestSendsResponse(listAt, listResponse);
NextRecord next = NextRecord.nameAndType("testdoc2.example.com", Type.NS);
assertEquals(apiWhenAtExist.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").listAt(next).toString(),
new ListResourceRecordSetsResponseTest().expected().toString());
}
public void testList2PagesWhenResponseIs2xx() {
HttpResponse noMore = HttpResponse.builder().statusCode(200)
.payload(payloadFromStringWithContentType("<ListResourceRecordSetsResponse />", "text/xml")).build();
Route53Api success = requestsSendResponses(list, listResponse, listAt, noMore);
assertEquals(success.getResourceRecordSetApiForZone("Z1PA6795UKMFR9").list().concat().toSet(), new ListResourceRecordSetsResponseTest().expected()
.toSet());
}
}

View File

@ -1,64 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.features;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals;
import java.util.List;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.internal.BaseRoute53ApiLiveTest;
import org.testng.SkipException;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "ResourceRecordSetApiLiveTest")
public class ResourceRecordSetApiLiveTest extends BaseRoute53ApiLiveTest {
private void checkResourceRecordSet(ResourceRecordSet resourceRecordSet) {
checkNotNull(resourceRecordSet.getName(), "Id cannot be null for a ResourceRecordSet %s", resourceRecordSet);
checkNotNull(resourceRecordSet.getType(), "Type cannot be null for a ResourceRecordSet %s", resourceRecordSet);
checkNotNull(resourceRecordSet.getTTL(),
"While TTL can be null for a ResourceRecordSet, its Optional wrapper cannot %s", resourceRecordSet);
}
@Test
protected void testListResourceRecordSets() {
IterableWithMarker<Zone> zones = context.getApi().getZoneApi().list().get(0);
if (zones.isEmpty())
throw new SkipException("no zones in context: " + context);
Zone zone = zones.first().get();
List<ResourceRecordSet> records = api(zone.getId()).list().concat().toList();
assertEquals(zone.getResourceRecordSetCount(), records.size());
for (ResourceRecordSet resourceRecordSet : records) {
checkResourceRecordSet(resourceRecordSet);
}
}
protected ResourceRecordSetApi api(String zoneId) {
return context.getApi().getResourceRecordSetApiForZone(zoneId);
}
}

View File

@ -18,7 +18,6 @@
*/
package org.jclouds.route53.features;
import static org.jclouds.route53.options.ListZonesOptions.Builder.afterMarker;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@ -45,7 +44,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.payload(
payloadFromStringWithContentType(
"<CreateHostedZoneRequest xmlns=\"https://route53.amazonaws.com/doc/2012-02-29/\"><Name>jclouds.org.</Name><CallerReference>expect</CallerReference></CreateHostedZoneRequest>",
@ -65,7 +64,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.payload(
payloadFromStringWithContentType(
"<CreateHostedZoneRequest xmlns=\"https://route53.amazonaws.com/doc/2012-02-29/\"><Name>jclouds.org.</Name><CallerReference>expect</CallerReference><HostedZoneConfig><Comment>comment</Comment></HostedZoneConfig></CreateHostedZoneRequest>",
@ -82,7 +81,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.build();
HttpResponse getResponse = HttpResponse.builder().statusCode(200)
@ -104,7 +103,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.build();
HttpResponse listResponse = HttpResponse.builder().statusCode(200)
@ -123,17 +122,17 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
assertEquals(fail.getZoneApi().list().get(0).toSet(), ImmutableSet.of());
}
HttpRequest listWithOptions = HttpRequest.builder().method("GET")
HttpRequest listAt = HttpRequest.builder().method("GET")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone?marker=Z333333YYYYYYY")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.build();
public void testListWithOptionsWhenResponseIs2xx() {
Route53Api apiWhenWithOptionsExist = requestSendsResponse(listWithOptions, listResponse);
assertEquals(apiWhenWithOptionsExist.getZoneApi().list(afterMarker("Z333333YYYYYYY")).toString(),
public void testListAtWhenResponseIs2xx() {
Route53Api apiWhenAtExist = requestSendsResponse(listAt, listResponse);
assertEquals(apiWhenAtExist.getZoneApi().listAt("Z333333YYYYYYY").toString(),
new ListHostedZonesResponseTest().expected().toString());
}
@ -141,7 +140,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
HttpResponse noMore = HttpResponse.builder().statusCode(200)
.payload(payloadFromStringWithContentType("<ListHostedZonesResponse />", "text/xml")).build();
Route53Api success = requestsSendResponses(list, listResponse, listWithOptions, noMore);
Route53Api success = requestsSendResponses(list, listResponse, listAt, noMore);
assertEquals(success.getZoneApi().list().concat().toString(), new ListHostedZonesResponseTest().expected()
.toString());
}
@ -151,7 +150,7 @@ public class ZoneApiExpectTest extends BaseRoute53ApiExpectTest {
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization",
"AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
authForDate)
.build();
HttpResponse deleteResponse = HttpResponse.builder().statusCode(200)

View File

@ -17,74 +17,49 @@
* under the License.
*/
package org.jclouds.route53.features;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.route53.domain.Change.Status.INSYNC;
import static java.lang.String.format;
import static java.util.logging.Logger.getAnonymousLogger;
import static org.jclouds.route53.domain.Change.Status.PENDING;
import static org.jclouds.util.Predicates2.retry;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Date;
import java.util.logging.Logger;
import org.jclouds.JcloudsVersion;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.route53.domain.Change;
import org.jclouds.route53.domain.NewZone;
import org.jclouds.route53.domain.Zone;
import org.jclouds.route53.internal.BaseRoute53ApiLiveTest;
import org.jclouds.route53.options.ListZonesOptions;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
/**
* @author Adrian Cole
*/
@Test(groups = "live", testName = "ZoneApiLiveTest")
public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest {
private Predicate<Change> inSync;
@BeforeClass(groups = "live")
@Override
public void setupContext() {
super.setupContext();
inSync = retry(new Predicate<Change>() {
public boolean apply(Change input) {
return context.getApi().getChange(input.getId()).getStatus() == INSYNC;
}
}, 600, 1, 5, SECONDS);
}
private void checkZone(Zone zone) {
checkNotNull(zone.getId(), "Id cannot be null for a Zone %s", zone);
checkNotNull(zone.getName(), "Name cannot be null for a Zone %s", zone);
checkNotNull(zone.getCallerReference(), "CallerReference cannot be null for a Zone %s", zone);
getAnonymousLogger().info(format("zone %s rrs: %s", zone.getName(), zone.getResourceRecordSetCount()));
checkNotNull(zone.getId(), "Id: Zone %s", zone);
checkNotNull(zone.getName(), "Name: Zone %s", zone);
checkNotNull(zone.getCallerReference(), "CallerReference: Zone %s", zone);
checkNotNull(zone.getComment(), "While Comment can be null for a Zone, its Optional wrapper cannot %s", zone);
}
@Test
protected void testListZones() {
IterableWithMarker<Zone> response = api().list().get(0);
ImmutableList<Zone> zones = api().list().concat().toList();
getAnonymousLogger().info("zones: " + zones.size());
for (Zone zone : response) {
for (Zone zone : zones) {
checkZone(zone);
}
if (response.size() > 0) {
Zone zone = response.iterator().next();
assertEquals(api().get(zone.getId()).getZone(), zone);
}
// Test with a Marker, even if it's null
response = api().list(ListZonesOptions.Builder.afterMarker(response.nextMarker().orNull()));
for (Zone zone : response) {
checkZone(zone);
}
}
@Test
@ -103,7 +78,7 @@ public class ZoneApiLiveTest extends BaseRoute53ApiLiveTest {
String nonce = name + " @ " + new Date();
String comment = name + " for " + JcloudsVersion.get();
NewZone newZone = api().createWithReferenceAndComment(name, nonce, comment);
Logger.getAnonymousLogger().info("created zone: " + newZone);
getAnonymousLogger().info("created zone: " + newZone);
try {
checkZone(newZone.getZone());
assertEquals(newZone.getChange().getStatus(), PENDING, "invalid status on zone " + newZone);

View File

@ -0,0 +1,99 @@
/**
* 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.route53.handlers;
import static com.google.common.base.Throwables.propagate;
import static org.jclouds.rest.internal.BaseRestApiExpectTest.payloadFromStringWithContentType;
import static org.jclouds.util.Strings2.toStringAndClose;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.config.SaxParserModule;
import org.jclouds.io.Payload;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.route53.InvalidChangeBatchException;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Guice;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit" )
public class Route53ErrorHandlerTest {
Route53ErrorHandler function = Guice.createInjector(new SaxParserModule()).getInstance(Route53ErrorHandler.class);
HttpRequest request = HttpRequest.builder().method("POST")
.endpoint("https://route53.amazonaws.com/2012-02-29/hostedzone/Z1PA6795UKMFR9/rrset")
.addHeader("Host", "route53.amazonaws.com")
.addHeader("Date", "Mon, 21 Jan 02013 19:29:03 -0800")
.addHeader("X-Amzn-Authorization", "AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=")
.payload(payloadFromResource("/batch_rrs_request.xml")).build();
HttpCommand command = new HttpCommand(request);
@Test
public void testInvalidChangeBatchException() throws IOException {
HttpResponse response = HttpResponse.builder().statusCode(400)
.payload(payloadFromResource("/invalid_change_batch.xml")).build();
function.handleError(command, response);
InvalidChangeBatchException exception = InvalidChangeBatchException.class.cast(command.getException());
assertEquals(exception.getMessages(), ImmutableSet.of(
"Tried to create resource record set duplicate.example.com. type A, but it already exists",
"Tried to delete resource record set noexist.example.com. type A, but it was not found"));
}
@Test
public void testDeleteNotFound() throws IOException {
HttpResponse response = HttpResponse.builder().statusCode(400)
.payload(
payloadFromStringWithContentType(
"<ErrorResponse><Error><Type>Sender</Type><Code>InvalidChangeBatch</Code>"
+ "<Message>Tried to delete resource record set krank.foo.bar., type TXT but it was not found</Message>"
+ "</Error></ErrorResponse>", "application/xml")).build();
function.handleError(command, response);
assertEquals(command.getException().getClass(), ResourceNotFoundException.class);
assertEquals(command.getException().getMessage(), "Tried to delete resource record set krank.foo.bar., type TXT but it was not found");
AWSResponseException exception = AWSResponseException.class.cast(command.getException().getCause());
assertEquals(exception.getError().getCode(), "InvalidChangeBatch");
}
private Payload payloadFromResource(String resource) {
try {
return payloadFromStringWithContentType(toStringAndClose(getClass().getResourceAsStream(resource)),
"application/xml");
} catch (IOException e) {
throw propagate(e);
}
}
}

View File

@ -18,13 +18,7 @@
*/
package org.jclouds.route53.internal;
import org.jclouds.http.HttpResponse;
import org.jclouds.route53.Route53Api;
/**
*
* @author Adrian Cole
*/
public class BaseRoute53ApiExpectTest extends BaseRoute53ExpectTest<Route53Api> {
protected HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
}

View File

@ -18,13 +18,20 @@
*/
package org.jclouds.route53.internal;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.jclouds.route53.domain.Change.Status.INSYNC;
import static org.jclouds.util.Predicates2.retry;
import org.jclouds.apis.BaseContextLiveTest;
import org.jclouds.route53.Route53ApiMetadata;
import org.jclouds.route53.Route53AsyncApi;
import org.jclouds.route53.Route53Api;
import org.jclouds.route53.domain.Change;
import org.jclouds.rest.RestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.reflect.TypeToken;
/**
@ -39,9 +46,21 @@ public class BaseRoute53ApiLiveTest extends
provider = "route53";
}
protected Predicate<Change> inSync;
@BeforeClass(groups = "live")
@Override
public void setupContext() {
super.setupContext();
inSync = retry(new Predicate<Change>() {
public boolean apply(Change input) {
return context.getApi().getChange(input.getId()).getStatus() == INSYNC;
}
}, 600, 1, 5, SECONDS);
}
@Override
protected TypeToken<RestContext<? extends Route53Api, ? extends Route53AsyncApi>> contextType() {
return Route53ApiMetadata.CONTEXT_TOKEN;
}
}

View File

@ -19,9 +19,10 @@
package org.jclouds.route53.internal;
import org.jclouds.date.DateService;
import org.jclouds.route53.config.Route53RestClientModule;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.internal.BaseRestApiExpectTest;
import org.jclouds.route53.config.Route53RestClientModule;
import com.google.inject.Module;
@ -44,6 +45,9 @@ public class BaseRoute53ExpectTest<T> extends BaseRestApiExpectTest<T> {
}
}
protected final HttpResponse notFound = HttpResponse.builder().statusCode(404).build();
protected final String authForDate = "AWS3-HTTPS AWSAccessKeyId=identity,Algorithm=HmacSHA256,Signature=pylxNiLcrsjNRZOsxyT161JCwytVPHyc2rFfmNCuZKI=";
@Override
protected Module createModule() {
return new TestRoute53RestClientModule();

View File

@ -1,56 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.route53.options;
import static org.jclouds.route53.options.ListZonesOptions.Builder.afterMarker;
import static org.jclouds.route53.options.ListZonesOptions.Builder.maxItems;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
/**
* Tests behavior of {@code ListZonesOptions}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ListZonesOptionsTest")
public class ListZonesOptionsTest {
public void testMarker() {
ListZonesOptions options = new ListZonesOptions().afterMarker("FFFFF");
assertEquals(ImmutableSet.of("FFFFF"), options.buildQueryParameters().get("marker"));
}
public void testMarkerStatic() {
ListZonesOptions options = afterMarker("FFFFF");
assertEquals(ImmutableSet.of("FFFFF"), options.buildQueryParameters().get("marker"));
}
public void testMaxItems() {
ListZonesOptions options = new ListZonesOptions().maxItems(1000);
assertEquals(ImmutableSet.of("1000"), options.buildQueryParameters().get("maxitems"));
}
public void testMaxItemsStatic() {
ListZonesOptions options = maxItems(1000);
assertEquals(ImmutableSet.of("1000"), options.buildQueryParameters().get("maxitems"));
}
}

View File

@ -0,0 +1,49 @@
/**
* 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.route53.parse;
import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.route53.xml.InvalidChangeBatchHandler;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
/**
* @author Adrian Cole
*/
// NOTE:without testName, this will not call @Before* and fail w/NPE during
// surefire
@Test(groups = "unit", testName = "InvalidChangeBatchResponseTest")
public class InvalidChangeBatchResponseTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/invalid_change_batch.xml");
InvalidChangeBatchHandler handler = injector.getInstance(InvalidChangeBatchHandler.class);
ImmutableList<String> result = factory.create(handler).parse(is);
assertEquals(result, ImmutableList.of(
"Tried to create resource record set duplicate.example.com. type A, but it already exists",
"Tried to delete resource record set noexist.example.com. type A, but it was not found"));
}
}

View File

@ -23,9 +23,9 @@ import static org.testng.Assert.assertEquals;
import java.io.InputStream;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.route53.domain.ResourceRecordSet;
import org.jclouds.route53.domain.ResourceRecordSet.Type;
import org.jclouds.route53.domain.ResourceRecordSetIterable;
import org.jclouds.route53.domain.RecordSet;
import org.jclouds.route53.domain.RecordSet.Type;
import org.jclouds.route53.domain.RecordSetIterable;
import org.jclouds.route53.xml.ListResourceRecordSetsResponseHandler;
import org.testng.annotations.Test;
@ -40,24 +40,24 @@ public class ListResourceRecordSetsResponseTest extends BaseHandlerTest {
public void test() {
InputStream is = getClass().getResourceAsStream("/rrsets.xml");
ResourceRecordSetIterable expected = expected();
RecordSetIterable expected = expected();
ListResourceRecordSetsResponseHandler handler = injector.getInstance(ListResourceRecordSetsResponseHandler.class);
ResourceRecordSetIterable result = factory.create(handler).parse(is);
RecordSetIterable result = factory.create(handler).parse(is);
assertEquals(result.toString(), expected.toString());
}
public ResourceRecordSetIterable expected() {
return ResourceRecordSetIterable.builder()
.add(ResourceRecordSet.builder()
public RecordSetIterable expected() {
return RecordSetIterable.builder()
.add(RecordSet.builder()
.name("example.com.")
.type(Type.SOA)
.ttl(900)
.add("ns-2048.awsdns-64.net. hostmaster.awsdns.com. 1 7200 900 1209600 86400")
.build())
.add(ResourceRecordSet.builder()
.add(RecordSet.builder()
.name("example.com.")
.type(Type.NS)
.ttl(172800)

View File

@ -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.route53.predicates;
import static org.jclouds.route53.domain.RecordSet.Type.AAAA;
import static org.jclouds.route53.domain.RecordSet.Type.NS;
import static org.jclouds.route53.predicates.RecordSetPredicates.typeEquals;
import org.jclouds.route53.domain.RecordSet;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ResourceRecordSetPredicatesTest")
public class RecordSetPredicatesTest {
RecordSet rrs = RecordSet.builder().name("jclouds.org.").type(NS).add("ns-119.awsdns-14.com.")
.build();
@Test
public void testTypeEqualsWhenEqual() {
assert typeEquals(NS).apply(rrs);
}
@Test
public void testTypeEqualsWhenNotEqual() {
assert !typeEquals(AAAA).apply(rrs);
}
}

View File

@ -0,0 +1,43 @@
/**
* 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.route53.predicates;
import static org.jclouds.route53.predicates.ZonePredicates.nameEquals;
import org.jclouds.route53.domain.Zone;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "ZonePredicatesTest")
public class ZonePredicatesTest {
Zone zone = Zone.builder().id("EEEFFFEEE").callerReference("goog").name("jclouds.org.").build();
@Test
public void testNameEqualsWhenEqual() {
assert nameEquals("jclouds.org.").apply(zone);
}
@Test
public void testNameEqualsWhenNotEqual() {
assert !nameEquals("kclouds.org.").apply(zone);
}
}

View File

@ -0,0 +1 @@
<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2012-02-29/"><ChangeBatch><Changes><Change><Action>DELETE</Action><ResourceRecordSet><Name>jclouds.org.</Name><Type>TXT</Type><TTL>0</TTL><ResourceRecords><ResourceRecord><Value>my texts</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change><Change><Action>CREATE</Action><ResourceRecordSet><Name>jclouds.org.</Name><Type>TXT</Type><TTL>0</TTL><ResourceRecords><ResourceRecord><Value>my better texts</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>

View File

@ -0,0 +1 @@
<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2012-02-29/"><ChangeBatch><Changes><Change><Action>CREATE</Action><ResourceRecordSet><Name>jclouds.org.</Name><Type>TXT</Type><TTL>0</TTL><ResourceRecords><ResourceRecord><Value>my texts</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>

View File

@ -0,0 +1 @@
<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2012-02-29/"><ChangeBatch><Changes><Change><Action>CREATE</Action><ResourceRecordSet><Name>jclouds.org.</Name><Type>TXT</Type><TTL>0</TTL><ResourceRecords><ResourceRecord><Value>my texts</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change></Changes></ChangeBatch></ChangeResourceRecordSetsRequest>

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<InvalidChangeBatch xmlns="https://route53.amazonaws.com/doc/2012-02-29/">
<Messages>
<Message>Tried to create resource record set duplicate.example.com. type A, but it already exists</Message>
<Message>Tried to delete resource record set noexist.example.com. type A, but it was not found</Message>
</Messages>
</InvalidChangeBatch>