finished zone support for dynect

This commit is contained in:
adriancole 2013-02-04 16:30:34 -08:00
parent 756e7724ef
commit b7fca7bff6
16 changed files with 798 additions and 40 deletions

View File

@ -18,8 +18,10 @@
*/ */
package org.jclouds.dynect.v3; package org.jclouds.dynect.v3;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.features.SessionApi; import org.jclouds.dynect.v3.features.SessionApi;
import org.jclouds.dynect.v3.features.ZoneApi; import org.jclouds.dynect.v3.features.ZoneApi;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
/** /**
@ -32,6 +34,15 @@ import org.jclouds.rest.annotations.Delegate;
* @author Adrian Cole * @author Adrian Cole
*/ */
public interface DynECTApi { public interface DynECTApi {
/**
* returns the current status of a job.
*
* @param jobId
* The ID of the job
* @return null, if not found
*/
@Nullable
Job getJob(long jobId);
/** /**
* Provides synchronous access to Session features. * Provides synchronous access to Session features.

View File

@ -20,12 +20,24 @@ package org.jclouds.dynect.v3;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.features.SessionAsyncApi; import org.jclouds.dynect.v3.features.SessionAsyncApi;
import org.jclouds.dynect.v3.features.ZoneAsyncApi; import org.jclouds.dynect.v3.features.ZoneAsyncApi;
import org.jclouds.dynect.v3.filters.SessionManager;
import org.jclouds.rest.annotations.Delegate; import org.jclouds.rest.annotations.Delegate;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.RequestFilters;
import com.google.common.util.concurrent.ListenableFuture;
/** /**
* Provides access to DynECT Managed DNS through the API2 api * Provides access to DynECT Managed DNS through the API2 api
@ -35,10 +47,19 @@ import org.jclouds.rest.annotations.Headers;
* @see <a href="https://manage.dynect.net/help/docs/api2/rest/" /> * @see <a href="https://manage.dynect.net/help/docs/api2/rest/" />
* @author Adrian Cole * @author Adrian Cole
*/ */
// required for all calls
@Produces(APPLICATION_JSON)
@Headers(keys = "API-Version", values = "{jclouds.api-version}")
public interface DynECTAsyncApi { public interface DynECTAsyncApi {
/**
* @see DynECTApi#getJob
*/
@Named("GetJob")
@GET
@Path("/Job/{jobId}")
@Produces(APPLICATION_JSON)
@RequestFilters(SessionManager.class)
@Headers(keys = "API-Version", values = "{jclouds.api-version}")
@Fallback(NullOnNotFoundOr404.class)
@Consumes(APPLICATION_JSON)
ListenableFuture<Job> getJob(@PathParam("jobId") long jobId);
/** /**
* Provides asynchronous access to Session features. * Provides asynchronous access to Session features.

View File

@ -29,8 +29,10 @@ import org.jclouds.dynect.v3.features.SessionAsyncApi;
import org.jclouds.dynect.v3.features.ZoneApi; import org.jclouds.dynect.v3.features.ZoneApi;
import org.jclouds.dynect.v3.features.ZoneAsyncApi; import org.jclouds.dynect.v3.features.ZoneAsyncApi;
import org.jclouds.dynect.v3.filters.SessionManager; import org.jclouds.dynect.v3.filters.SessionManager;
import org.jclouds.dynect.v3.handlers.GetJobRedirectionRetryHandler;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.handlers.RedirectionRetryHandler;
import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.config.RestClientModule;
@ -61,6 +63,7 @@ public class DynECTRestClientModule extends RestClientModule<DynECTApi, DynECTAs
protected void configure() { protected void configure() {
// binding explicitly ensures singleton despite multiple linked bindings // binding explicitly ensures singleton despite multiple linked bindings
bind(SessionManager.class); bind(SessionManager.class);
bind(RedirectionRetryHandler.class).to(GetJobRedirectionRetryHandler.class);
super.configure(); super.configure();
// Bind apis that are used directly vs via DynECTApi // Bind apis that are used directly vs via DynECTApi
bindHttpApi(binder(), SessionApi.class, SessionAsyncApi.class); bindHttpApi(binder(), SessionApi.class, SessionAsyncApi.class);

View File

@ -0,0 +1,161 @@
/**
* 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, String 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.dynect.v3.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 javax.inject.Named;
import org.jclouds.dynect.v3.domain.Zone.SerialStyle;
import com.google.common.base.Function;
import com.google.common.base.Objects;
/**
* @author Adrian Cole
*/
public final class CreatePrimaryZone {
// persisted via path param
private final transient String fqdn;
@Named("rname")
private final String contact;
@Named("serial_style")
private final SerialStyle serialStyle;
@Named("ttl")
private final int defaultTTL;
private CreatePrimaryZone(String fqdn, String contact, SerialStyle serialStyle, int defaultTTL) {
this.fqdn = checkNotNull(fqdn, "fqdn");
this.contact = checkNotNull(contact, "contact for %s", fqdn);
this.defaultTTL = checkNotNull(defaultTTL, "defaultTTL for %s", fqdn);
this.serialStyle = checkNotNull(serialStyle, "serialStyle for %s", serialStyle);
}
/**
* The fqdn of the zone to create
*/
public String getFQDN() {
return fqdn;
}
/**
* The administrative contact or {@code rname} for the {@code SOA} record.
*/
public String getContact() {
return contact;
}
/**
* Default TTL (in seconds) for records in the zone. Defaults to 3600.
*/
public int getDefaultTTL() {
return defaultTTL;
}
/**
* The style of the zone's serial. Defaults to {@link SerialStyle#INCREMENT}.
*/
public SerialStyle getSerialStyle() {
return serialStyle;
}
@Override
public int hashCode() {
return Objects.hashCode(fqdn, contact);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
CreatePrimaryZone that = CreatePrimaryZone.class.cast(obj);
return equal(this.fqdn, that.fqdn) && equal(this.contact, that.contact);
}
@Override
public String toString() {
return toStringHelper(this).omitNullValues().add("fqdn", fqdn).add("contact", contact)
.add("defaultTTL", defaultTTL).add("serialStyle", serialStyle).toString();
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return builder().from(this);
}
public final static class Builder {
private String fqdn;
private String contact;
private int defaultTTL = 3600;
private SerialStyle serialStyle = SerialStyle.INCREMENT;
/**
* @see CreatePrimaryZone#getFQDN()
*/
public Builder fqdn(String fqdn) {
this.fqdn = fqdn;
return this;
}
/**
* @see CreatePrimaryZone#getContact()
*/
public Builder contact(String contact) {
this.contact = contact;
return this;
}
/**
* @see CreatePrimaryZone#getDefaultTTL()
*/
public Builder defaultTTL(int defaultTTL) {
this.defaultTTL = defaultTTL;
return this;
}
/**
* @see CreatePrimaryZone#getSerialStyle()
*/
public Builder serialStyle(SerialStyle serialStyle) {
this.serialStyle = serialStyle;
return this;
}
public CreatePrimaryZone build() {
return new CreatePrimaryZone(fqdn, contact, serialStyle, defaultTTL);
}
public Builder from(CreatePrimaryZone in) {
return this.fqdn(in.fqdn).contact(in.contact).serialStyle(in.serialStyle).defaultTTL(in.defaultTTL);
}
}
public static final class ToFQDN implements Function<Object, String> {
public String apply(Object in) {
return CreatePrimaryZone.class.cast(in).getFQDN();
}
}
}

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.dynect.v3.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.beans.ConstructorProperties;
import javax.inject.Named;
import com.google.common.base.Objects;
/**
* @author Adrian Cole
*/
public final class Job {
@Named("job_id")
private final long id;
private final Status status;
@ConstructorProperties({ "job_id", "status" })
private Job(long id, Status status) {
this.id = checkNotNull(id, "id");
this.status = checkNotNull(status, "status for %s", id);
}
/**
* The ID of the job.
*/
public long getId() {
return id;
}
/**
* The current status of the job.
*/
public Status getStatus() {
return status;
}
public enum Status {
SUCCESS, FAILURE, UNRECOGNIZED;
public static Status fromValue(String status) {
try {
return valueOf(checkNotNull(status, "status").toUpperCase());
} catch (IllegalArgumentException e) {
return UNRECOGNIZED;
}
}
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Job that = Job.class.cast(obj);
return equal(this.id, that.id);
}
@Override
public String toString() {
return toStringHelper(this).add("id", id).add("status", status).toString();
}
public static Job success(long id) {
return new Job(id, Status.SUCCESS);
}
public static Job failure(long id) {
return new Job(id, Status.FAILURE);
}
}

View File

@ -34,7 +34,7 @@ import com.google.common.base.Objects;
*/ */
public final class Zone { public final class Zone {
private final String name; private final String fqdn;
@Named("zone_type") @Named("zone_type")
private final Type type; private final Type type;
private final int serial; private final int serial;
@ -92,18 +92,18 @@ public final class Zone {
} }
@ConstructorProperties({ "zone", "zone_type", "serial", "serial_style" }) @ConstructorProperties({ "zone", "zone_type", "serial", "serial_style" })
private Zone(String name, Type type, int serial, SerialStyle serialStyle) { private Zone(String fqdn, Type type, int serial, SerialStyle serialStyle) {
this.name = checkNotNull(name, "name"); this.fqdn = checkNotNull(fqdn, "fqdn");
this.type = checkNotNull(type, "type for %s", name); this.type = checkNotNull(type, "type for %s", fqdn);
this.serial = checkNotNull(serial, "serial for %s", name); this.serial = checkNotNull(serial, "serial for %s", fqdn);
this.serialStyle = checkNotNull(serialStyle, "serialStyle for %s", serialStyle); this.serialStyle = checkNotNull(serialStyle, "serialStyle for %s", serialStyle);
} }
/** /**
* The name of the requested zone * The fqdn of the requested zone
*/ */
public String getName() { public String getFQDN() {
return name; return fqdn;
} }
/** /**
@ -129,7 +129,7 @@ public final class Zone {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(name, type); return Objects.hashCode(fqdn, type);
} }
@Override @Override
@ -139,12 +139,12 @@ public final class Zone {
if (obj == null || getClass() != obj.getClass()) if (obj == null || getClass() != obj.getClass())
return false; return false;
Zone that = Zone.class.cast(obj); Zone that = Zone.class.cast(obj);
return equal(this.name, that.name) && equal(this.type, that.type); return equal(this.fqdn, that.fqdn) && equal(this.type, that.type);
} }
@Override @Override
public String toString() { public String toString() {
return toStringHelper(this).omitNullValues().add("name", name).add("type", type).add("serial", serial) return toStringHelper(this).omitNullValues().add("fqdn", fqdn).add("type", type).add("serial", serial)
.add("serialStyle", serialStyle).toString(); .add("serialStyle", serialStyle).toString();
} }
@ -157,16 +157,16 @@ public final class Zone {
} }
public final static class Builder { public final static class Builder {
private String name; private String fqdn;
private Type type; private Type type;
private int serial; private int serial;
private SerialStyle serialStyle; private SerialStyle serialStyle;
/** /**
* @see Zone#getName() * @see Zone#getFQDN()
*/ */
public Builder name(String name) { public Builder fqdn(String fqdn) {
this.name = name; this.fqdn = fqdn;
return this; return this;
} }
@ -195,11 +195,11 @@ public final class Zone {
} }
public Zone build() { public Zone build() {
return new Zone(name, type, serial, serialStyle); return new Zone(fqdn, type, serial, serialStyle);
} }
public Builder from(Zone in) { public Builder from(Zone in) {
return this.name(in.name).type(in.type).serial(in.serial).serialStyle(in.serialStyle); return this.fqdn(in.fqdn).type(in.type).serial(in.serial).serialStyle(in.serialStyle);
} }
} }
} }

View File

@ -18,7 +18,10 @@
*/ */
package org.jclouds.dynect.v3.features; package org.jclouds.dynect.v3.features;
import org.jclouds.dynect.v3.domain.CreatePrimaryZone;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.domain.Zone; import org.jclouds.dynect.v3.domain.Zone;
import org.jclouds.dynect.v3.domain.Zone.SerialStyle;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import com.google.common.collect.FluentIterable; import com.google.common.collect.FluentIterable;
@ -34,14 +37,69 @@ public interface ZoneApi {
FluentIterable<String> list(); FluentIterable<String> list();
/** /**
* Retrieves information about the specified zone, including its nameserver * Creates a new primary zone.
* configuration
* *
* @param name * @param zone
* name of the zone to get information about. ex * required parameters to create the zone.
* {@code Z1PA6795UKMFR9} * @return unpublished zone
*/
Zone create(CreatePrimaryZone zone);
/**
* Creates a new primary zone with one hour default TTL and
* {@link SerialStyle#INCREMENT}
*
* @param fqdn
* fqdn of the zone to create {@ex. jclouds.org}
* @param contact
* email address of the contact
* @return unpublished zone
*/
Zone createWithContact(String fqdn, String contact);
/**
* Retrieves information about the specified zone.
*
* @param fqdn
* fqdn of the zone to get information about. ex
* {@code jclouds.org}
* @return null if not found * @return null if not found
*/ */
@Nullable @Nullable
Zone get(String name); Zone get(String fqdn);
/**
* deletes the specified zone.
*
* @param fqdn
* fqdn of the zone to delete ex {@code jclouds.org}
* @return null if not found
*/
@Nullable
Job delete(String fqdn);
/**
* Publishes the current zone
*
* @param fqdn
* fqdn of the zone to publish. ex
* {@code jclouds.org}
*/
Zone publish(String fqdn);
/**
* freezes the specified zone.
*
* @param fqdn
* fqdn of the zone to freeze ex {@code jclouds.org}
*/
Job freeze(String fqdn);
/**
* thaws the specified zone.
*
* @param fqdn
* fqdn of the zone to thaw ex {@code jclouds.org}
*/
Job thaw(String fqdn);
} }

View File

@ -21,20 +21,32 @@ package org.jclouds.dynect.v3.features;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import javax.inject.Named; import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.dynect.v3.domain.CreatePrimaryZone;
import org.jclouds.dynect.v3.domain.CreatePrimaryZone.ToFQDN;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.domain.Zone; import org.jclouds.dynect.v3.domain.Zone;
import org.jclouds.dynect.v3.filters.SessionManager; import org.jclouds.dynect.v3.filters.SessionManager;
import org.jclouds.dynect.v3.functions.ExtractNames; import org.jclouds.dynect.v3.functions.ExtractNames;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.Headers;
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.RequestFilters;
import org.jclouds.rest.annotations.SelectJson; import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.Transform; import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.collect.FluentIterable; import com.google.common.collect.FluentIterable;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -56,19 +68,79 @@ public interface ZoneAsyncApi {
/** /**
* @see ZoneApi#list * @see ZoneApi#list
*/ */
@Named("GET:ZoneList") @Named("ListZones")
@GET @GET
@SelectJson("data") @SelectJson("data")
@Transform(ExtractNames.class) @Transform(ExtractNames.class)
ListenableFuture<FluentIterable<String>> list(); ListenableFuture<FluentIterable<String>> list();
/** /**
* @see ZoneApi#isValid * @see ZoneApi#get
*/ */
@Named("GET:Zone") @Named("GetZone")
@GET @GET
@Path("/{name}") @Path("/{fqdn}")
@SelectJson("data") @SelectJson("data")
@Fallback(NullOnNotFoundOr404.class) @Fallback(NullOnNotFoundOr404.class)
ListenableFuture<Zone> get(@PathParam("name") String name); ListenableFuture<Zone> get(@PathParam("fqdn") String fqdn);
/**
* @see ZoneApi#create
*/
@Named("CreatePrimaryZone")
@POST
@Path("/{fqdn}")
@SelectJson("data")
ListenableFuture<Zone> create(
@PathParam("fqdn") @ParamParser(ToFQDN.class) @BinderParam(BindToJsonPayload.class) CreatePrimaryZone createZone);
/**
* @see ZoneApi#createWithContact
*/
@Named("CreatePrimaryZone")
@POST
@Payload("%7B\"rname\":\"{contact}\",\"serial_style\":\"increment\",\"ttl\":3600%7D")
@Path("/{fqdn}")
@SelectJson("data")
ListenableFuture<Zone> createWithContact(@PathParam("fqdn") String fqdn, @PayloadParam("contact") String contact);
/**
* @see ZoneApi#delete
*/
@Named("DeleteZone")
@DELETE
@Path("/{fqdn}")
@Fallback(NullOnNotFoundOr404.class)
@Consumes(APPLICATION_JSON)
ListenableFuture<Job> delete(@PathParam("fqdn") String fqdn);
/**
* @see ZoneApi#publish
*/
@Named("PublishZone")
@PUT
@Payload("{\"publish\":true}")
@Path("/{fqdn}")
@SelectJson("data")
ListenableFuture<Zone> publish(@PathParam("fqdn") String fqdn);
/**
* @see ZoneApi#freeze
*/
@Named("FreezeZone")
@PUT
@Path("/{fqdn}")
@Payload("{\"freeze\":true}")
@Consumes(APPLICATION_JSON)
ListenableFuture<Job> freeze(@PathParam("fqdn") String fqdn);
/**
* @see ZoneApi#thaw
*/
@Named("ThawZone")
@PUT
@Path("/{fqdn}")
@Payload("{\"thaw\":true}")
@Consumes(APPLICATION_JSON)
ListenableFuture<Job> thaw(@PathParam("fqdn") String fqdn);
} }

View File

@ -0,0 +1,64 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.dynect.v3.handlers;
import static com.google.common.net.HttpHeaders.LOCATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.http.handlers.RedirectionRetryHandler;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
/**
* if the redirection URL is a Job, do not replay the original request; just get
* the job.
*
* @author Adrian Cole
*/
@Singleton
public class GetJobRedirectionRetryHandler extends RedirectionRetryHandler {
private final Payload emptyPayload;
@Inject
protected GetJobRedirectionRetryHandler(BackoffLimitedRetryHandler backoffHandler) {
super(backoffHandler);
this.emptyPayload = Payloads.newPayload(new byte[]{});
this.emptyPayload.getContentMetadata().setContentType(APPLICATION_JSON);
}
@Override
public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
String location = response.getFirstHeaderOrNull(LOCATION);
if (location != null && location.indexOf("Job") != -1) {
HttpRequest getRequest = command.getCurrentRequest().toBuilder()
.method("GET")
.payload(emptyPayload).build();
command.setCurrentRequest(getRequest);
}
return super.shouldRetryRequest(command, response);
}
}

View File

@ -18,12 +18,15 @@
*/ */
package org.jclouds.dynect.v3.features; package org.jclouds.dynect.v3.features;
import static com.google.common.net.HttpHeaders.ACCEPT;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull; import static org.testng.Assert.assertNull;
import org.jclouds.dynect.v3.DynECTApi; import org.jclouds.dynect.v3.DynECTApi;
import org.jclouds.dynect.v3.domain.CreatePrimaryZone;
import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest; import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest;
import org.jclouds.dynect.v3.parse.DeleteZoneResponseTest;
import org.jclouds.dynect.v3.parse.GetZoneResponseTest; import org.jclouds.dynect.v3.parse.GetZoneResponseTest;
import org.jclouds.dynect.v3.parse.ListZonesResponseTest; import org.jclouds.dynect.v3.parse.ListZonesResponseTest;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
@ -35,7 +38,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = "unit", testName = "ZoneApiExpectTest") @Test(groups = "unit", testName = "ZoneApiExpectTest")
public class ZoneApiExpectTest extends BaseDynECTApiExpectTest { public class ZoneApiExpectTest extends BaseDynECTApiExpectTest {
HttpRequest get = HttpRequest.builder().method("GET") HttpRequest get = HttpRequest.builder().method("GET")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org") .endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7") .addHeader("API-Version", "3.3.7")
@ -52,7 +54,29 @@ public class ZoneApiExpectTest extends BaseDynECTApiExpectTest {
new GetZoneResponseTest().expected().toString()); new GetZoneResponseTest().expected().toString());
} }
public void testGetWhenResponseError2401() { HttpRequest create = HttpRequest.builder().method("POST")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader("Auth-Token", authToken)
.payload(stringPayload("{\"rname\":\"jimmy@jclouds.org\",\"serial_style\":\"increment\",\"ttl\":3600}"))
.build();
public void testCreateWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, create, getResponse);
assertEquals(success.getZoneApi().create(CreatePrimaryZone.builder()
.fqdn("jclouds.org")
.contact("jimmy@jclouds.org")
.build()).toString(),
new GetZoneResponseTest().expected().toString());
}
public void testCreateWithContactWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, create, getResponse);
assertEquals(success.getZoneApi().createWithContact("jclouds.org", "jimmy@jclouds.org").toString(),
new GetZoneResponseTest().expected().toString());
}
public void testGetWhenResponseIs404() {
DynECTApi fail = requestsSendResponses(createSession, createSessionResponse, get, notFound); DynECTApi fail = requestsSendResponses(createSession, createSessionResponse, get, notFound);
assertNull(fail.getZoneApi().get("jclouds.org")); assertNull(fail.getZoneApi().get("jclouds.org"));
} }
@ -72,4 +96,67 @@ public class ZoneApiExpectTest extends BaseDynECTApiExpectTest {
assertEquals(success.getZoneApi().list().toString(), assertEquals(success.getZoneApi().list().toString(),
new ListZonesResponseTest().expected().toString()); new ListZonesResponseTest().expected().toString());
} }
HttpRequest delete = HttpRequest.builder().method("DELETE")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader(ACCEPT, APPLICATION_JSON)
.addHeader("Auth-Token", authToken)
.payload(emptyJsonPayload())
.build();
HttpResponse deleteResponse = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/delete_zone.json", APPLICATION_JSON)).build();
public void testDeleteWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, delete, deleteResponse);
assertEquals(success.getZoneApi().delete("jclouds.org").toString(),
new DeleteZoneResponseTest().expected().toString());
}
public void testDeleteWhenResponseIs404() {
DynECTApi fail = requestsSendResponses(createSession, createSessionResponse, delete, notFound);
assertNull(fail.getZoneApi().delete("jclouds.org"));
}
HttpRequest publish = HttpRequest.builder().method("PUT")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader("Auth-Token", authToken)
.payload(stringPayload("{\"publish\":true}"))
.build();
public void testPublishWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, publish, getResponse);
assertEquals(success.getZoneApi().publish("jclouds.org").toString(),
new GetZoneResponseTest().expected().toString());
}
HttpRequest freeze = HttpRequest.builder().method("PUT")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader(ACCEPT, APPLICATION_JSON)
.addHeader("Auth-Token", authToken)
.payload(stringPayload("{\"freeze\":true}"))
.build();
public void testFreezeWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, freeze, deleteResponse);
assertEquals(success.getZoneApi().freeze("jclouds.org").toString(),
new DeleteZoneResponseTest().expected().toString());
}
HttpRequest thaw = HttpRequest.builder().method("PUT")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader(ACCEPT, APPLICATION_JSON)
.addHeader("Auth-Token", authToken)
.payload(stringPayload("{\"thaw\":true}"))
.build();
public void testThawWhenResponseIs2xx() {
DynECTApi success = requestsSendResponses(createSession, createSessionResponse, thaw, deleteResponse);
assertEquals(success.getZoneApi().thaw("jclouds.org").toString(),
new DeleteZoneResponseTest().expected().toString());
}
} }

View File

@ -20,10 +20,15 @@ package org.jclouds.dynect.v3.features;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.logging.Logger.getAnonymousLogger; import static java.util.logging.Logger.getAnonymousLogger;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull; import static org.testng.Assert.assertNull;
import org.jclouds.JcloudsVersion;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.domain.Job.Status;
import org.jclouds.dynect.v3.domain.Zone; import org.jclouds.dynect.v3.domain.Zone;
import org.jclouds.dynect.v3.internal.BaseDynECTApiLiveTest; import org.jclouds.dynect.v3.internal.BaseDynECTApiLiveTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -35,7 +40,7 @@ import com.google.common.collect.ImmutableList;
public class ZoneApiLiveTest extends BaseDynECTApiLiveTest { public class ZoneApiLiveTest extends BaseDynECTApiLiveTest {
private void checkZone(Zone zone) { private void checkZone(Zone zone) {
checkNotNull(zone.getName(), "Name cannot be null for a Zone: %s", zone); checkNotNull(zone.getFQDN(), "FQDN cannot be null for a Zone: %s", zone);
checkNotNull(zone.getSerial(), "Serial cannot be null for a Zone: %s", zone); checkNotNull(zone.getSerial(), "Serial cannot be null for a Zone: %s", zone);
} }
@ -44,19 +49,74 @@ public class ZoneApiLiveTest extends BaseDynECTApiLiveTest {
ImmutableList<String> zones = api().list().toList(); ImmutableList<String> zones = api().list().toList();
getAnonymousLogger().info("zones: " + zones.size()); getAnonymousLogger().info("zones: " + zones.size());
for (String zoneName : zones) { for (String fqdn : zones) {
Zone zone = api().get(zoneName); Zone zone = api().get(fqdn);
checkNotNull(zone, "zone was null for Zone: %s", zoneName); checkNotNull(zone, "zone was null for Zone: %s", fqdn);
checkZone(zone); checkZone(zone);
} }
} }
@Test @Test
public void testGetZoneWhenNotFound() { public void testGetZoneWhenNotFound() {
assertNull(api().get("AAAAAAAAAAAAAAAA")); assertNull(api().get("AAAAAAAAAAAAAAAA.foo.com"));
}
@Test
public void testDeleteZoneWhenNotFound() {
assertNull(api().delete("AAAAAAAAAAAAAAAA.foo.com"));
}
String fqdn = System.getProperty("user.name").replace('.', '-') + ".zone.dynecttest.jclouds.org";
String contact = JcloudsVersion.get() + "@jclouds.org";
@Test
public void testCreateZone() {
Zone zone = api().createWithContact(fqdn, contact);
checkNotNull(zone, "unable to create zone %s", fqdn);
getAnonymousLogger().info("created zone: " + zone);
checkZone(zone);
}
@Test(dependsOnMethods = "testCreateZone")
public void testPublishZone() {
Zone zone = api().publish(fqdn);
checkNotNull(zone, "unable to publish zone %s", fqdn);
getAnonymousLogger().info("published zone: " + zone);
checkZone(zone);
}
@Test(dependsOnMethods = "testPublishZone")
public void testFreezeZone() {
Job job = api().freeze(fqdn);
assertEquals(job.getStatus(), Status.SUCCESS);
assertEquals(context.getApi().getJob(job.getId()), job);
// TODO: determine how to prove it is frozen
}
@Test(dependsOnMethods = "testFreezeZone")
public void testThawZone() {
Job job = api().thaw(fqdn);
assertEquals(job.getStatus(), Status.SUCCESS);
assertEquals(context.getApi().getJob(job.getId()), job);
// TODO: determine how to prove it is thawed
}
@Test(dependsOnMethods = "testThawZone")
public void testDeleteZone() {
Job job = api().delete(fqdn);
assertEquals(job.getStatus(), Status.SUCCESS);
assertEquals(context.getApi().getJob(job.getId()), job);
assertNull(api().get(fqdn), "job " + job + " didn't delete zone" + fqdn);
} }
protected ZoneApi api() { protected ZoneApi api() {
return context.getApi().getZoneApi(); return context.getApi().getZoneApi();
} }
@Override
@AfterClass(groups = "live")
protected void tearDownContext() {
api().delete(fqdn);
super.tearDownContext();
}
} }

View File

@ -0,0 +1,70 @@
/**
* 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.dynect.v3.handlers;
import static com.google.common.net.HttpHeaders.ACCEPT;
import static com.google.common.net.HttpHeaders.LOCATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import org.jclouds.dynect.v3.DynECTApi;
import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.testng.annotations.Test;
/**
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "GetJobRedirectionRetryHandlerExpectTest")
public class GetJobRedirectionRetryHandlerExpectTest extends BaseDynECTApiExpectTest {
public void testRedirectOnJobLocationSwitchesToGETAndNoPayload() {
HttpRequest thaw = HttpRequest.builder().method("PUT")
.endpoint("https://api2.dynect.net/REST/Zone/jclouds.org")
.addHeader("API-Version", "3.3.7")
.addHeader(ACCEPT, APPLICATION_JSON)
.addHeader("Auth-Token", authToken)
.payload(stringPayload("{\"thaw\":true}"))
.build();
HttpResponse redirectResponse = HttpResponse.builder()
.statusCode(317)
.addHeader(LOCATION, "https://api2.dynect.net/REST/Job/1234")
.build();
HttpRequest job = HttpRequest.builder().method("GET")
.endpoint("https://api2.dynect.net/REST/Job/1234")
.addHeader("API-Version", "3.3.7")
.addHeader(ACCEPT, APPLICATION_JSON)
.addHeader("Auth-Token", authToken)
.payload(emptyJsonPayload())
.build();
HttpResponse success = HttpResponse.builder().statusCode(200)
.payload(payloadFromResourceWithContentType("/delete_zone.json", APPLICATION_JSON)).build();
DynECTApi apiThatRedirects = requestsSendResponses(createSession, createSessionResponse, thaw, redirectResponse,
job, success);
apiThatRedirects.getZoneApi().thaw("jclouds.org");
}
}

View File

@ -44,6 +44,12 @@ public class BaseDynECTExpectTest<T> extends BaseRestApiExpectTest<T> {
return p; return p;
} }
public static Payload stringPayload(String json) {
Payload p = Payloads.newPayload(json);
p.getContentMetadata().setContentType(APPLICATION_JSON);
return p;
}
@Override @Override
protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) { protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
return HttpRequestComparisonType.JSON; return HttpRequestComparisonType.JSON;

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.dynect.v3.parse;
import javax.ws.rs.Consumes;
import javax.ws.rs.core.MediaType;
import org.jclouds.dynect.v3.domain.Job;
import org.jclouds.dynect.v3.internal.BaseDynECTParseTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = "unit")
public class DeleteZoneResponseTest extends BaseDynECTParseTest<Job> {
@Override
public String resource() {
return "/delete_zone.json";
}
@Override
@Consumes(MediaType.APPLICATION_JSON)
public Job expected() {
return Job.success(262989027l);
}
}

View File

@ -44,6 +44,6 @@ public class GetZoneResponseTest extends BaseDynECTParseTest<Zone> {
@SelectJson("data") @SelectJson("data")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Zone expected() { public Zone expected() {
return Zone.builder().type(PRIMARY).serialStyle(INCREMENT).serial(5).name("jclouds.org").build(); return Zone.builder().type(PRIMARY).serialStyle(INCREMENT).serial(5).fqdn("jclouds.org").build();
} }
} }

View File

@ -0,0 +1 @@
{"status": "success", "data": {}, "job_id": 262989027, "msgs": [{"INFO": "remove: Zone removed", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]}