You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2016/01/21 01:03:26 UTC

[02/19] jclouds git commit: JCLOUDS-613: Implement the DigitalOcean v2 API

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiLiveTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiLiveTest.java
new file mode 100644
index 0000000..73c7451
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiLiveTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.features;
+
+import static org.testng.Assert.assertTrue;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.Builder.page;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jclouds.digitalocean2.domain.Region;
+import org.jclouds.digitalocean2.internal.BaseDigitalOcean2ApiLiveTest;
+import org.testng.annotations.Test;
+import org.testng.util.Strings;
+
+import com.google.common.base.Predicate;
+
+@Test(groups = "live", testName = "RegionApiLiveTest")
+public class RegionApiLiveTest extends BaseDigitalOcean2ApiLiveTest {
+   
+   public void testListRegions() {
+      final AtomicInteger found = new AtomicInteger(0);
+      // DigitalOcean return 25 records per page by default. Inspect at most 2 pages
+      assertTrue(api().list().concat().limit(50).allMatch(new Predicate<Region>() {
+         @Override
+         public boolean apply(Region input) {
+            found.incrementAndGet();
+            return !Strings.isNullOrEmpty(input.slug());
+         }
+      }), "All regions must have the 'slug' field populated");
+      assertTrue(found.get() > 0, "Expected some regions to be returned");
+   }
+   
+   public void testListRegionsOnePage() {
+      final AtomicInteger found = new AtomicInteger(0);
+      assertTrue(api().list(page(1)).allMatch(new Predicate<Region>() {
+         @Override
+         public boolean apply(Region input) {
+            found.incrementAndGet();
+            return !Strings.isNullOrEmpty(input.slug());
+         }
+      }), "All regions must have the 'slug' field populated");
+      assertTrue(found.get() > 0, "Expected some regions to be returned");
+   }
+   
+   private RegionApi api() {
+      return api.regionApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiMockTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiMockTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiMockTest.java
new file mode 100644
index 0000000..8c8c326
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/RegionApiMockTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.features;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.size;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.Builder.page;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.digitalocean2.domain.Region;
+import org.jclouds.digitalocean2.internal.BaseDigitalOcean2ApiMockTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "RegionApiMockTest", singleThreaded = true)
+public class RegionApiMockTest extends BaseDigitalOcean2ApiMockTest {
+
+   public void testListRegions() throws InterruptedException {
+      server.enqueue(jsonResponse("/regions-first.json"));
+      server.enqueue(jsonResponse("/regions-last.json"));
+
+      Iterable<Region> regions = api.regionApi().list().concat();
+
+      assertEquals(size(regions), 10); // Force the PagedIterable to advance
+      assertEquals(server.getRequestCount(), 2);
+
+      assertSent(server, "GET", "/regions");
+      assertSent(server, "GET", "/regions?page=2&per_page=5");
+   }
+
+   public void testListRegionsReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      Iterable<Region> regions = api.regionApi().list().concat();
+
+      assertTrue(isEmpty(regions));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/regions");
+   }
+
+   public void testListRegionsWithOptions() throws InterruptedException {
+      server.enqueue(jsonResponse("/regions-first.json"));
+
+      Iterable<Region> regions = api.regionApi().list(page(1).perPage(5));
+
+      assertEquals(size(regions), 5);
+      assertEquals(server.getRequestCount(), 1);
+
+      assertSent(server, "GET", "/regions?page=1&per_page=5");
+   }
+
+   public void testListRegionsWithOptionsReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      Iterable<Region> regions = api.regionApi().list(page(1).perPage(5));
+
+      assertTrue(isEmpty(regions));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/regions?page=1&per_page=5");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiLiveTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiLiveTest.java
new file mode 100644
index 0000000..a9a8566
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiLiveTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.features;
+
+import static org.jclouds.digitalocean2.domain.options.ListOptions.Builder.page;
+import static org.testng.Assert.assertTrue;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jclouds.digitalocean2.domain.Size;
+import org.jclouds.digitalocean2.internal.BaseDigitalOcean2ApiLiveTest;
+import org.testng.annotations.Test;
+import org.testng.util.Strings;
+
+import com.google.common.base.Predicate;
+
+@Test(groups = "live", testName = "SizeApiLiveTest")
+public class SizeApiLiveTest extends BaseDigitalOcean2ApiLiveTest {
+   
+   public void testListSizes() {
+      final AtomicInteger found = new AtomicInteger(0);
+      // DigitalOcean return 25 records per page by default. Inspect at most 2 pages
+      assertTrue(api().list().concat().limit(50).allMatch(new Predicate<Size>() {
+         @Override
+         public boolean apply(Size input) {
+            found.incrementAndGet();
+            return !Strings.isNullOrEmpty(input.slug());
+         }
+      }), "All sizes must have the 'slug' field populated");
+      assertTrue(found.get() > 0, "Expected some sizes to be returned");
+   }
+   
+   public void testListSizesOnePage() {
+      final AtomicInteger found = new AtomicInteger(0);
+      assertTrue(api().list(page(1)).allMatch(new Predicate<Size>() {
+         @Override
+         public boolean apply(Size input) {
+            found.incrementAndGet();
+            return !Strings.isNullOrEmpty(input.slug());
+         }
+      }), "All sizes must have the 'slug' field populated");
+      assertTrue(found.get() > 0, "Expected some sizes to be returned");
+   }
+   
+   private SizeApi api() {
+      return api.sizeApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiMockTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiMockTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiMockTest.java
new file mode 100644
index 0000000..7403518
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/features/SizeApiMockTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.features;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.size;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.Builder.page;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.digitalocean2.domain.Size;
+import org.jclouds.digitalocean2.internal.BaseDigitalOcean2ApiMockTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "SizeApiMockTest", singleThreaded = true)
+public class SizeApiMockTest extends BaseDigitalOcean2ApiMockTest {
+
+   public void testListSizes() throws InterruptedException {
+      server.enqueue(jsonResponse("/sizes-first.json"));
+      server.enqueue(jsonResponse("/sizes-last.json"));
+
+      Iterable<Size> sizes = api.sizeApi().list().concat();
+
+      assertEquals(size(sizes), 9); // Force the PagedIterable to advance
+      assertEquals(server.getRequestCount(), 2);
+
+      assertSent(server, "GET", "/sizes");
+      assertSent(server, "GET", "/sizes?page=2&per_page=5");
+   }
+
+   public void testListSizesReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      Iterable<Size> sizes = api.sizeApi().list().concat();
+
+      assertTrue(isEmpty(sizes));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/sizes");
+   }
+
+   public void testListSizesWithOptions() throws InterruptedException {
+      server.enqueue(jsonResponse("/sizes-first.json"));
+
+      Iterable<Size> sizes = api.sizeApi().list(page(1).perPage(5));
+
+      assertEquals(size(sizes), 5);
+      assertEquals(server.getRequestCount(), 1);
+
+      assertSent(server, "GET", "/sizes?page=1&per_page=5");
+   }
+
+   public void testListSizesWithOptionsReturns404() throws InterruptedException {
+      server.enqueue(response404());
+
+      Iterable<Size> sizes = api.sizeApi().list(page(1).perPage(5));
+
+      assertTrue(isEmpty(sizes));
+
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "/sizes?page=1&per_page=5");
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToImageListOptionsTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToImageListOptionsTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToImageListOptionsTest.java
new file mode 100644
index 0000000..22985fa
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToImageListOptionsTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.functions;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.jclouds.digitalocean2.domain.options.ImageListOptions.PRIVATE_PARAM;
+import static org.jclouds.digitalocean2.domain.options.ImageListOptions.TYPE_PARAM;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.PAGE_PARAM;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.PER_PAGE_PARAM;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URI;
+
+import org.jclouds.digitalocean2.domain.options.ImageListOptions;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Multimap;
+
+@Test(groups = "unit", testName = "LinkToImageListOptionsTest")
+public class LinkToImageListOptionsTest {
+
+   public void testNoOptions() {
+      LinkToImageListOptions function = new LinkToImageListOptions();
+
+      ImageListOptions options = function.apply(URI.create("https://api.digitalocean.com/v2/images"));
+      assertNotNull(options);
+
+      Multimap<String, String> params = options.buildQueryParameters();
+      assertFalse(params.containsKey(PAGE_PARAM));
+      assertFalse(params.containsKey(PER_PAGE_PARAM));
+      assertFalse(params.containsKey(TYPE_PARAM));
+      assertFalse(params.containsKey(PRIVATE_PARAM));
+   }
+
+   public void testWithOptions() {
+      LinkToImageListOptions function = new LinkToImageListOptions();
+
+      ImageListOptions options = function.apply(URI
+            .create("https://api.digitalocean.com/v2/images?page=1&per_page=5&type=distribution&private=true"));
+      assertNotNull(options);
+
+      Multimap<String, String> params = options.buildQueryParameters();
+      assertEquals(getOnlyElement(params.get(PAGE_PARAM)), "1");
+      assertEquals(getOnlyElement(params.get(PER_PAGE_PARAM)), "5");
+      assertEquals(getOnlyElement(params.get(TYPE_PARAM)), "distribution");
+      assertEquals(getOnlyElement(params.get(PRIVATE_PARAM)), "true");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToListOptionsTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToListOptionsTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToListOptionsTest.java
new file mode 100644
index 0000000..2bb3544
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/functions/LinkToListOptionsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.functions;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.PAGE_PARAM;
+import static org.jclouds.digitalocean2.domain.options.ListOptions.PER_PAGE_PARAM;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+
+import java.net.URI;
+
+import org.jclouds.digitalocean2.domain.options.ListOptions;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Multimap;
+
+@Test(groups = "unit", testName = "LinkToListOptionsTest")
+public class LinkToListOptionsTest {
+
+   public void testNoOptions() {
+      LinkToListOptions function = new LinkToListOptions();
+
+      ListOptions options = function.apply(URI.create("https://api.digitalocean.com/v2/actions"));
+      assertNotNull(options);
+
+      Multimap<String, String> params = options.buildQueryParameters();
+      assertFalse(params.containsKey(PAGE_PARAM));
+      assertFalse(params.containsKey(PER_PAGE_PARAM));
+   }
+
+   public void testWithOptions() {
+      LinkToListOptions function = new LinkToListOptions();
+
+      ListOptions options = function.apply(URI.create("https://api.digitalocean.com/v2/actions?page=2&per_page=5"));
+      assertNotNull(options);
+
+      Multimap<String, String> params = options.buildQueryParameters();
+      assertEquals(getOnlyElement(params.get(PAGE_PARAM)), "2");
+      assertEquals(getOnlyElement(params.get(PER_PAGE_PARAM)), "5");
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiLiveTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiLiveTest.java
new file mode 100644
index 0000000..18f97c6
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiLiveTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.internal;
+
+import static com.google.common.base.Preconditions.checkState;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.testng.Assert.assertEquals;
+import static org.testng.util.Strings.isNullOrEmpty;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.compute.config.ComputeServiceProperties;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.digitalocean2.DigitalOcean2Api;
+import org.jclouds.digitalocean2.domain.Action;
+import org.jclouds.digitalocean2.domain.Image;
+import org.jclouds.digitalocean2.domain.Region;
+import org.jclouds.digitalocean2.domain.Size;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.Ordering;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+public class BaseDigitalOcean2ApiLiveTest extends BaseApiLiveTest<DigitalOcean2Api> {
+
+   protected Predicate<Integer> actionCompleted;
+   protected Predicate<AtomicReference<NodeMetadata>> nodeRunning;
+   protected Predicate<Integer> nodeTerminated;
+   protected Predicate<Integer> dropletOff;
+
+   public BaseDigitalOcean2ApiLiveTest() {
+      provider = "digitalocean2";
+   }
+
+   @Override protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      props.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
+      props.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
+      return props;
+   }
+
+   @Override protected DigitalOcean2Api create(Properties props, Iterable<Module> modules) {
+      Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
+      actionCompleted = injector.getInstance(Key.get(new TypeLiteral<Predicate<Integer>>(){}));
+      nodeTerminated = injector.getInstance(Key.get(new TypeLiteral<Predicate<Integer>>(){},
+            Names.named(TIMEOUT_NODE_TERMINATED)));
+      return injector.getInstance(DigitalOcean2Api.class);
+   }
+
+   protected void assertActionCompleted(int actionId) {
+      checkState(actionCompleted.apply(actionId), "Timeout waiting for action: %s", actionId);
+      Action action = api.actionApi().get(actionId);
+      assertEquals(action.status(), Action.Status.COMPLETED);
+   }
+
+   protected void assertNodeTerminated(int dropletId) {
+      assertEquals(nodeTerminated.apply(dropletId), true, String.format("Timeout waiting for dropletId: %s", dropletId));
+   }
+   
+   protected Region firstAvailableRegion() {
+      return api.regionApi().list().concat().firstMatch(new Predicate<Region>() {
+         @Override
+         public boolean apply(Region input) {
+            return input.available();
+         }
+      }).get();
+   }
+   
+   protected Size cheapestSizeInRegion(final Region region) {
+      return sizesByPrice().min(api.sizeApi().list().concat().filter(new Predicate<Size>() {
+         @Override
+         public boolean apply(Size input) {
+            return input.available() && input.regions().contains(region.slug());
+         }
+      }));
+   }
+   
+   protected Image ubuntuImageInRegion(final Region region) {
+      return api.imageApi().list().concat().firstMatch(new Predicate<Image>() {
+         @Override
+         public boolean apply(Image input) {
+            return "Ubuntu".equalsIgnoreCase(input.distribution()) && !isNullOrEmpty(input.slug())
+                  && input.regions().contains(region.slug());
+         }
+      }).get();
+   }
+   
+   protected static Ordering<Size> sizesByPrice() {
+      return new Ordering<Size>() {
+         @Override
+         public int compare(Size left, Size right) {
+            return ComparisonChain.start()
+                  .compare(left.priceHourly(), right.priceHourly())
+                  .compare(left.priceMonthly(), right.priceMonthly())
+                  .result();
+         }
+      };
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiMockTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiMockTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiMockTest.java
new file mode 100644
index 0000000..78550a5
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/internal/BaseDigitalOcean2ApiMockTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.digitalocean2.DigitalOcean2Api;
+import org.jclouds.digitalocean2.DigitalOcean2ProviderMetadata;
+import org.jclouds.json.Json;
+import org.jclouds.rest.ApiContext;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Resources;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.JsonParser;
+import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+public class BaseDigitalOcean2ApiMockTest {
+   
+   private static final String MOCK_BEARER_TOKEN = "c5401990f0c24135e8d6b5d260603fc71696d4738da9aa04a720229a01a2521d";
+   private static final String DEFAULT_ENDPOINT = new DigitalOcean2ProviderMetadata().getEndpoint();
+   
+   private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
+   
+   protected MockWebServer server;
+   protected DigitalOcean2Api api;
+   private Json json;
+   
+   // So that we can ignore formatting.
+   private final JsonParser parser = new JsonParser();
+   
+   
+   @BeforeMethod
+   public void start() throws IOException {
+      server = new MockWebServer();
+      server.play();
+      ApiContext<DigitalOcean2Api> ctx = ContextBuilder.newBuilder("digitalocean2")
+            .credentials("", MOCK_BEARER_TOKEN)
+            .endpoint(url(""))
+            .modules(modules)
+            .build();
+      json = ctx.utils().injector().getInstance(Json.class);
+      api = ctx.getApi();
+   }
+
+   @AfterMethod(alwaysRun = true)
+   public void stop() throws IOException {
+      server.shutdown();
+      api.close();
+   }
+   
+   protected String url(String path) {
+      return server.getUrl(path).toString();
+   }
+
+   protected MockResponse jsonResponse(String resource) {
+      return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+   }
+
+   protected MockResponse response404() {
+      return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
+   }
+   
+   protected MockResponse response204() {
+      return new MockResponse().setStatus("HTTP/1.1 204 No Content");
+   }
+
+   protected String stringFromResource(String resourceName) {
+      try {
+         return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8)
+               .replace(DEFAULT_ENDPOINT, url(""));
+      } catch (IOException e) {
+         throw Throwables.propagate(e);
+      }
+   }
+   
+   protected <T> T onlyObjectFromResource(String resourceName, TypeToken<Map<String, T>> type) {
+      // Assume JSON objects passed here will be in the form: { "entity": { ... } }
+      String text = stringFromResource(resourceName);
+      Map<String, T> object = json.fromJson(text, type.getType());
+      checkArgument(!object.isEmpty(), "The given json does not contain any object: %s", text);
+      checkArgument(object.keySet().size() == 1, "The given json does not contain more than one object: %s", text);
+      return object.get(getOnlyElement(object.keySet()));
+   }
+   
+   protected <T> T objectFromResource(String resourceName, Class<T> type) {
+      String text = stringFromResource(resourceName);
+      return json.fromJson(text, type);
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
+      RecordedRequest request = server.takeRequest();
+      assertEquals(request.getMethod(), method);
+      assertEquals(request.getPath(), path);
+      assertEquals(request.getHeader("Accept"), "application/json");
+      assertEquals(request.getHeader("Authorization"), "Bearer " + MOCK_BEARER_TOKEN);
+      return request;
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
+         throws InterruptedException {
+      RecordedRequest request = assertSent(server, method, path);
+      assertEquals(request.getHeader("Content-Type"), "application/json");
+      assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
+      return request;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/DSAKeysTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/DSAKeysTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/DSAKeysTest.java
new file mode 100644
index 0000000..91f3c96
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/DSAKeysTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.ssh;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.DSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link DSAKeys} class.
+ */
+@Test(groups = "unit", testName = "DSAKeysTest")
+public class DSAKeysTest {
+
+   private static final String expectedFingerPrint = "2a:54:bb:8e:ba:44:96:c8:6c:9c:40:34:3c:4d:38:e4";
+
+   @Test
+   public void testCanReadRsaAndCompareFingerprintOnPublicRSAKey() throws IOException {
+      String dsa = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-dsa.pub"));
+      String fingerPrint = DSAKeys.fingerprintPublicKey(dsa);
+      assertEquals(fingerPrint, expectedFingerPrint);
+   }
+
+   @Test
+   public void testEncodeAsOpenSSH() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
+      String dsa = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-dsa.pub"));
+      DSAPublicKeySpec spec = DSAKeys.publicKeySpecFromOpenSSH(dsa);
+      DSAPublicKey key = (DSAPublicKey) KeyFactory.getInstance("DSA").generatePublic(spec);
+
+      assertEquals(DSAKeys.encodeAsOpenSSH(key), dsa);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/ECDSAKeysTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/ECDSAKeysTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/ECDSAKeysTest.java
new file mode 100644
index 0000000..2053ac7
--- /dev/null
+++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/ssh/ECDSAKeysTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.digitalocean2.ssh;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import org.jclouds.util.Strings2;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link ECDSAKeysTest} class.
+ */
+@Test(groups = "unit", testName = "ECDSAKeysTest")
+public class ECDSAKeysTest {
+
+   private static final String expectedFingerPrint = "0e:9f:aa:cc:3e:79:5d:1e:f9:19:58:08:dc:c4:5e:1c";
+
+   @Test
+   public void testCanReadRsaAndCompareFingerprintOnPublicECDSAKey() throws IOException {
+      String ecdsa = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-ecdsa.pub"));
+      String fingerPrint = ECDSAKeys.fingerprintPublicKey(ecdsa);
+      assertEquals(fingerPrint, expectedFingerPrint);
+   }
+
+   @Test
+   public void testEncodeAsOpenSSH() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
+      String ecdsa = Strings2.toStringAndClose(getClass().getResourceAsStream("/ssh-ecdsa.pub"));
+      ECPublicKeySpec spec = ECDSAKeys.publicKeySpecFromOpenSSH(ecdsa);
+      ECPublicKey key = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(spec);
+
+      assertTrue(ecdsa.startsWith(ECDSAKeys.encodeAsOpenSSH(key)));
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/action.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/action.json b/providers/digitalocean2/src/test/resources/action.json
new file mode 100644
index 0000000..0202ca0
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/action.json
@@ -0,0 +1,33 @@
+{
+    "action": {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "completed",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "destroy"
+      }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/actions-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/actions-first.json b/providers/digitalocean2/src/test/resources/actions-first.json
new file mode 100644
index 0000000..c19fde3
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/actions-first.json
@@ -0,0 +1,168 @@
+{
+   "actions" : [
+      {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "completed",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "destroy"
+      },
+      {
+         "started_at" : "2015-05-19T15:07:55Z",
+         "region" : {
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true,
+            "slug" : "nyc1",
+            "name" : "New York 1"
+         },
+         "status" : "completed",
+         "resource_type" : "droplet",
+         "region_slug" : "nyc1",
+         "id" : 50899364,
+         "resource_id" : 5346565,
+         "completed_at" : "2015-05-19T15:08:04Z",
+         "type" : "destroy"
+      },
+      {
+         "completed_at" : "2015-05-19T13:39:59Z",
+         "type" : "create",
+         "region" : {
+            "name" : "New York 1",
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true,
+            "slug" : "nyc1"
+         },
+         "started_at" : "2015-05-19T13:39:12Z",
+         "resource_type" : "droplet",
+         "status" : "completed",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50892713
+      },
+      {
+         "resource_id" : 5346565,
+         "region_slug" : "nyc1",
+         "id" : 50888077,
+         "status" : "completed",
+         "resource_type" : "droplet",
+         "region" : {
+            "name" : "New York 1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true,
+            "slug" : "nyc1",
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T12:37:23Z",
+         "type" : "create",
+         "completed_at" : "2015-05-19T12:38:13Z"
+      },
+      {
+         "completed_at" : "2015-05-19T11:33:00Z",
+         "type" : "destroy",
+         "status" : "completed",
+         "resource_type" : "droplet",
+         "started_at" : "2015-05-19T11:32:55Z",
+         "region" : {
+            "available" : true,
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "slug" : "nyc1",
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "name" : "New York 1"
+         },
+         "region_slug" : "nyc1",
+         "id" : 50884032,
+         "resource_id" : 5344505
+      }
+   ],
+   "links" : {
+      "pages" : {
+         "last" : "https://api.digitalocean.com/v2/actions?page=2&per_page=5",
+         "next" : "https://api.digitalocean.com/v2/actions?page=2&per_page=5"
+      }
+   },
+   "meta" : {
+      "total" : 8
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/actions-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/actions-last.json b/providers/digitalocean2/src/test/resources/actions-last.json
new file mode 100644
index 0000000..5d86e7a
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/actions-last.json
@@ -0,0 +1,106 @@
+{
+   "meta" : {
+      "total" : 8
+   },
+   "links" : {
+      "pages" : {
+         "first" : "https://api.digitalocean.com/v2/actions?page=1&per_page=5",
+         "prev" : "https://api.digitalocean.com/v2/actions?page=1&per_page=5"
+      }
+   },
+   "actions" : [
+      {
+         "region" : {
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "available" : true,
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "slug" : "nyc1",
+            "name" : "New York 1"
+         },
+         "started_at" : "2014-01-18T22:39:08Z",
+         "type" : "create",
+         "resource_type" : "droplet",
+         "id" : 14115951,
+         "completed_at" : "2014-01-18T22:41:14Z",
+         "region_slug" : "nyc1",
+         "resource_id" : 1010699,
+         "status" : "completed"
+      },
+      {
+         "started_at" : "2014-01-18T22:39:06Z",
+         "type" : "create",
+         "resource_type" : "droplet",
+         "region" : {
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "slug" : "nyc1",
+            "name" : "New York 1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true
+         },
+         "resource_id" : 1010697,
+         "region_slug" : "nyc1",
+         "status" : "completed",
+         "id" : 14115948,
+         "completed_at" : "2014-01-18T22:40:43Z"
+      },
+      {
+         "region_slug" : "nyc1",
+         "resource_id" : 1010698,
+         "status" : "completed",
+         "id" : 14115949,
+         "completed_at" : "2014-01-18T22:44:08Z",
+         "type" : "create",
+         "started_at" : "2014-01-18T22:39:06Z",
+         "resource_type" : "droplet",
+         "region" : {
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ],
+            "slug" : "nyc1",
+            "name" : "New York 1",
+            "available" : true,
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ]
+         }
+      }
+   ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/backups-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/backups-first.json b/providers/digitalocean2/src/test/resources/backups-first.json
new file mode 100644
index 0000000..f1083f1
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/backups-first.json
@@ -0,0 +1,26 @@
+{
+  "backups": [
+    {
+      "id": 7622989,
+      "name": "example.com 2014-11-14",
+      "distribution": "Ubuntu",
+      "slug": null,
+      "public": false,
+      "regions": [
+        "nyc3"
+      ],
+      "created_at": "2014-11-14T16:07:38Z",
+      "type": "snapshot",
+      "min_disk_size": 20
+    }
+  ],
+  "links" : {
+      "pages" : {
+         "last" : "https://api.digitalocean.com/v2/droplets/3067509/backups?page=2",
+         "next" : "https://api.digitalocean.com/v2/droplets/3067509/backups?page=2"
+      }
+  },
+  "meta": {
+    "total": 2
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/backups-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/backups-last.json b/providers/digitalocean2/src/test/resources/backups-last.json
new file mode 100644
index 0000000..c927c19
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/backups-last.json
@@ -0,0 +1,26 @@
+{
+  "backups": [
+    {
+      "id": 76229890,
+      "name": "example.com 2014-11-14",
+      "distribution": "Ubuntu",
+      "slug": null,
+      "public": false,
+      "regions": [
+        "nyc3"
+      ],
+      "created_at": "2014-11-14T16:07:38Z",
+      "type": "snapshot",
+      "min_disk_size": 20
+    }
+  ],
+  "links" : {
+      "pages" : {
+         "first" : "https://api.digitalocean.com/v2/droplets/3067509/backups?page=1",
+         "prev" : "https://api.digitalocean.com/v2/droplets/3067509/backups?page=1"
+      }
+  },
+  "meta": {
+    "total": 2
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/droplet-create-req.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/droplet-create-req.json b/providers/digitalocean2/src/test/resources/droplet-create-req.json
new file mode 100644
index 0000000..3ed5273
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/droplet-create-req.json
@@ -0,0 +1,12 @@
+{
+    "name": "digitalocean2-s-d5e",
+    "region": "sfo1",
+    "size": "512mb",
+    "image": "6374124",
+    "ssh_keys": [
+        421192
+    ],
+    "backups": false,
+    "ipv6": false,
+    "private_networking": false
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/droplet-create-res.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/droplet-create-res.json b/providers/digitalocean2/src/test/resources/droplet-create-res.json
new file mode 100644
index 0000000..a7d37a5
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/droplet-create-res.json
@@ -0,0 +1,35 @@
+{
+    "droplet": {
+        "id": 2987224,
+        "name": "digitalocean2-s-d5e",
+        "memory": 512,
+        "vcpus": 1,
+        "disk": 20,
+        "locked": true,
+        "status": "new",
+        "kernel": {
+            "id": 70,
+            "name": "Ubuntu 10.04 x64 vmlinuz-2.6.32-41-server",
+            "version": "2.6.32-41-server"
+        },
+        "created_at": "2014-10-27T19:33:34Z",
+        "features": [
+            "virtio"
+        ],
+        "backup_ids": [],
+        "snapshot_ids": [],
+        "image": {},
+        "size_slug": "512mb",
+        "networks": {},
+        "region": {}
+    },
+    "links": {
+        "actions": [
+            {
+                "id": 35383956,
+                "rel": "create",
+                "href": "https://api.digitalocean.com/v2/actions/35383956"
+            }
+        ]
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/droplet.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/droplet.json b/providers/digitalocean2/src/test/resources/droplet.json
new file mode 100644
index 0000000..fb995c5
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/droplet.json
@@ -0,0 +1,105 @@
+{
+   "droplet" :
+      {
+         "created_at" : "2015-05-25T15:50:48Z",
+         "region" : {
+            "name" : "New York 1",
+            "sizes" : [
+               "32gb",
+               "16gb",
+               "2gb",
+               "1gb",
+               "4gb",
+               "8gb",
+               "512mb",
+               "64gb",
+               "48gb"
+            ],
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true
+         },
+         "id" : 5425561,
+         "disk" : 20,
+         "networks" : {
+            "v6" : [],
+            "v4" : [
+               {
+                  "type" : "public",
+                  "ip_address" : "162.243.167.46",
+                  "netmask" : "255.255.255.0",
+                  "gateway" : "162.243.167.1"
+               }
+            ]
+         },
+         "backup_ids" : [],
+         "image" : {
+            "slug" : "ubuntu-14-10-x32",
+            "public" : true,
+            "created_at" : "2015-01-08T18:41:22Z",
+            "distribution" : "Ubuntu",
+            "id" : 9801951,
+            "type" : "snapshot",
+            "regions" : [
+               "nyc1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "ams2",
+               "sgp1",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "fra1"
+            ],
+            "name" : "14.10 x32",
+            "min_disk_size" : 20
+         },
+         "vcpus" : 1,
+         "next_backup_window" : {
+            "end" : "2015-06-01T23:00:00Z",
+            "start" : "2015-06-01T00:00:00Z"
+         },
+         "locked" : false,
+         "snapshot_ids" : [],
+         "kernel" : {
+            "name" : "Ubuntu 14.10 x32 vmlinuz-3.16.0-28-generic",
+            "id" : 2926,
+            "version" : "3.16.0-28-generic"
+         },
+         "status" : "active",
+         "features" : [
+            "backups",
+            "virtio"
+         ],
+         "size" : {
+            "price_hourly" : 0.00744,
+            "slug" : "512mb",
+            "disk" : 20,
+            "available" : true,
+            "transfer" : 1,
+            "price_monthly" : 5,
+            "regions" : [
+               "nyc1",
+               "sgp1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "ams2",
+               "fra1"
+            ],
+            "memory" : 512,
+            "vcpus" : 1
+         },
+         "name" : "test1",
+         "size_slug" : "512mb",
+         "memory" : 512
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/droplets-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/droplets-first.json b/providers/digitalocean2/src/test/resources/droplets-first.json
new file mode 100644
index 0000000..5493f7f
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/droplets-first.json
@@ -0,0 +1,115 @@
+{
+   "links" : {
+      "pages" : {
+         "next" : "https://api.digitalocean.com/v2/droplets/5425561?page=2&per_page=1",
+         "last" : "https://api.digitalocean.com/v2/droplets/5425561?page=2&per_page=1"
+      }
+   },
+   "meta" : {
+      "total" : 2
+   },
+   "droplets" : [
+      {
+         "created_at" : "2015-05-25T15:50:48Z",
+         "region" : {
+            "name" : "New York 1",
+            "sizes" : [
+               "32gb",
+               "16gb",
+               "2gb",
+               "1gb",
+               "4gb",
+               "8gb",
+               "512mb",
+               "64gb",
+               "48gb"
+            ],
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true
+         },
+         "id" : 5425561,
+         "disk" : 20,
+         "networks" : {
+            "v6" : [],
+            "v4" : [
+               {
+                  "type" : "public",
+                  "ip_address" : "162.243.167.46",
+                  "netmask" : "255.255.255.0",
+                  "gateway" : "162.243.167.1"
+               }
+            ]
+         },
+         "backup_ids" : [],
+         "image" : {
+            "slug" : "ubuntu-14-10-x32",
+            "public" : true,
+            "created_at" : "2015-01-08T18:41:22Z",
+            "distribution" : "Ubuntu",
+            "id" : 9801951,
+            "type" : "snapshot",
+            "regions" : [
+               "nyc1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "ams2",
+               "sgp1",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "fra1"
+            ],
+            "name" : "14.10 x32",
+            "min_disk_size" : 20
+         },
+         "vcpus" : 1,
+         "next_backup_window" : {
+            "end" : "2015-06-01T23:00:00Z",
+            "start" : "2015-06-01T00:00:00Z"
+         },
+         "locked" : false,
+         "snapshot_ids" : [],
+         "kernel" : {
+            "name" : "Ubuntu 14.10 x32 vmlinuz-3.16.0-28-generic",
+            "id" : 2926,
+            "version" : "3.16.0-28-generic"
+         },
+         "status" : "active",
+         "features" : [
+            "backups",
+            "virtio"
+         ],
+         "size" : {
+            "price_hourly" : 0.00744,
+            "slug" : "512mb",
+            "disk" : 20,
+            "available" : true,
+            "transfer" : 1,
+            "price_monthly" : 5,
+            "regions" : [
+               "nyc1",
+               "sgp1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "ams2",
+               "fra1"
+            ],
+            "memory" : 512,
+            "vcpus" : 1
+         },
+         "name" : "test1",
+         "size_slug" : "512mb",
+         "memory" : 512
+      }
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/droplets-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/droplets-last.json b/providers/digitalocean2/src/test/resources/droplets-last.json
new file mode 100644
index 0000000..beeb654
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/droplets-last.json
@@ -0,0 +1,115 @@
+{
+   "links" : {
+      "pages" : {
+         "first" : "https://api.digitalocean.com/v2/droplets/5425561?page=2&per_page=1",
+         "prev" : "https://api.digitalocean.com/v2/droplets/5425561?page=2&per_page=1"
+      }
+   },
+   "meta" : {
+      "total" : 2
+   },
+   "droplets" : [
+      {
+         "created_at" : "2015-05-25T15:50:48Z",
+         "region" : {
+            "name" : "New York 1",
+            "sizes" : [
+               "32gb",
+               "16gb",
+               "2gb",
+               "1gb",
+               "4gb",
+               "8gb",
+               "512mb",
+               "64gb",
+               "48gb"
+            ],
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "available" : true
+         },
+         "id" : 5425561,
+         "disk" : 20,
+         "networks" : {
+            "v6" : [],
+            "v4" : [
+               {
+                  "type" : "public",
+                  "ip_address" : "162.243.167.46",
+                  "netmask" : "255.255.255.0",
+                  "gateway" : "162.243.167.1"
+               }
+            ]
+         },
+         "backup_ids" : [],
+         "image" : {
+            "slug" : "ubuntu-14-10-x32",
+            "public" : true,
+            "created_at" : "2015-01-08T18:41:22Z",
+            "distribution" : "Ubuntu",
+            "id" : 9801951,
+            "type" : "snapshot",
+            "regions" : [
+               "nyc1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "ams2",
+               "sgp1",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "fra1"
+            ],
+            "name" : "14.10 x32",
+            "min_disk_size" : 20
+         },
+         "vcpus" : 1,
+         "next_backup_window" : {
+            "end" : "2015-06-01T23:00:00Z",
+            "start" : "2015-06-01T00:00:00Z"
+         },
+         "locked" : false,
+         "snapshot_ids" : [],
+         "kernel" : {
+            "name" : "Ubuntu 14.10 x32 vmlinuz-3.16.0-28-generic",
+            "id" : 2926,
+            "version" : "3.16.0-28-generic"
+         },
+         "status" : "active",
+         "features" : [
+            "backups",
+            "virtio"
+         ],
+         "size" : {
+            "price_hourly" : 0.00744,
+            "slug" : "512mb",
+            "disk" : 20,
+            "available" : true,
+            "transfer" : 1,
+            "price_monthly" : 5,
+            "regions" : [
+               "nyc1",
+               "sgp1",
+               "ams1",
+               "sfo1",
+               "nyc2",
+               "lon1",
+               "nyc3",
+               "ams3",
+               "ams2",
+               "fra1"
+            ],
+            "memory" : 512,
+            "vcpus" : 1
+         },
+         "name" : "test1",
+         "size_slug" : "512mb",
+         "memory" : 512
+      }
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/image.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/image.json b/providers/digitalocean2/src/test/resources/image.json
new file mode 100644
index 0000000..e66fda9
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/image.json
@@ -0,0 +1,24 @@
+{
+   "image" : {
+         "type" : "snapshot",
+         "id" : 11732785,
+         "name" : "Maintenance Mode",
+         "min_disk_size" : 20,
+         "distribution" : "Debian",
+         "created_at" : "2015-05-05T21:21:25Z",
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "slug" : null,
+         "public" : true
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/images-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/images-first.json b/providers/digitalocean2/src/test/resources/images-first.json
new file mode 100644
index 0000000..7d98cf9
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/images-first.json
@@ -0,0 +1,108 @@
+{
+   "meta" : {
+      "total" : 54
+   },
+   "links" : {
+      "pages" : {
+         "next" : "https://api.digitalocean.com/v2/images?page=2&per_page=5&type=distribution",
+         "last" : "https://api.digitalocean.com/v2/images?page=11&per_page=5&type=distribution"
+      }
+   },
+   "images" : [
+      {
+         "type" : "snapshot",
+         "id" : 11732785,
+         "name" : "Maintenance Mode",
+         "min_disk_size" : 20,
+         "distribution" : "Debian",
+         "created_at" : "2015-05-05T21:21:25Z",
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "slug" : null,
+         "public" : true
+      },
+      {
+         "type" : "snapshot",
+         "id" : 11833262,
+         "distribution" : "CoreOS",
+         "created_at" : "2015-05-12T17:41:36Z",
+         "regions" : [
+            "nyc1",
+            "sfo1",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "public" : true,
+         "slug" : "coreos-stable",
+         "name" : "647.0.0 (stable)",
+         "min_disk_size" : 20
+      },
+      {
+         "min_disk_size" : 20,
+         "name" : "668.3.0 (beta)",
+         "created_at" : "2015-05-18T18:14:12Z",
+         "public" : true,
+         "regions" : [
+            "nyc1",
+            "sfo1",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "slug" : "coreos-beta",
+         "distribution" : "CoreOS",
+         "id" : 11919888,
+         "type" : "snapshot"
+      },
+      {
+         "type" : "snapshot",
+         "id" : 11919908,
+         "distribution" : "CoreOS",
+         "created_at" : "2015-05-18T18:20:08Z",
+         "public" : true,
+         "regions" : [
+            "nyc1",
+            "sfo1",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "slug" : "coreos-alpha",
+         "name" : "681.0.0 (alpha)",
+         "min_disk_size" : 20
+      },
+      {
+         "min_disk_size" : 30,
+         "name" : "vum-easter-move",
+         "slug" : null,
+         "regions" : [
+            "ams1"
+         ],
+         "public" : true,
+         "created_at" : "2015-04-10T07:31:20Z",
+         "distribution" : "Debian",
+         "id" : 11385199,
+         "type" : "snapshot"
+      }
+   ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/images-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/images-last.json b/providers/digitalocean2/src/test/resources/images-last.json
new file mode 100644
index 0000000..89642fe
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/images-last.json
@@ -0,0 +1,123 @@
+{
+   "meta" : {
+      "total" : 54
+   },
+   "links" : {
+      "pages" : {
+         "prev" : "https://api.digitalocean.com/v2/images?page=1&per_page=5&type=distribution",
+         "first" : "https://api.digitalocean.com/v2/images?page=1&per_page=5&type=distribution"
+      }
+   },
+   "images" : [
+      {
+         "distribution" : "Fedora",
+         "min_disk_size" : 20,
+         "id" : 6370882,
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "name" : "20 x64",
+         "type" : "snapshot",
+         "slug" : "fedora-20-x64",
+         "public" : true,
+         "created_at" : "2014-09-26T15:29:01Z"
+      },
+      {
+         "name" : "20 x32",
+         "type" : "snapshot",
+         "distribution" : "Fedora",
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "id" : 6370885,
+         "min_disk_size" : 20,
+         "created_at" : "2014-09-26T15:29:18Z",
+         "public" : true,
+         "slug" : "fedora-20-x32"
+      },
+      {
+         "created_at" : "2014-09-26T16:40:18Z",
+         "slug" : "centos-5-8-x64",
+         "public" : true,
+         "type" : "snapshot",
+         "name" : "5.10 x64",
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "min_disk_size" : 20,
+         "id" : 6372321,
+         "distribution" : "CentOS"
+      },
+      {
+         "public" : true,
+         "slug" : "centos-5-8-x32",
+         "created_at" : "2014-09-26T16:45:29Z",
+         "id" : 6372425,
+         "min_disk_size" : 20,
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "distribution" : "CentOS",
+         "type" : "snapshot",
+         "name" : "5.10 x32"
+      },
+      {
+         "created_at" : "2014-09-26T16:56:00Z",
+         "public" : true,
+         "slug" : "debian-6-0-x64",
+         "type" : "snapshot",
+         "name" : "6.0 x64",
+         "regions" : [
+            "nyc1",
+            "ams1",
+            "sfo1",
+            "nyc2",
+            "ams2",
+            "sgp1",
+            "lon1",
+            "nyc3",
+            "ams3",
+            "fra1"
+         ],
+         "min_disk_size" : 20,
+         "id" : 6372581,
+         "distribution" : "Debian"
+      }
+   ]
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/kernels-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/kernels-first.json b/providers/digitalocean2/src/test/resources/kernels-first.json
new file mode 100644
index 0000000..3ec05e0
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/kernels-first.json
@@ -0,0 +1,38 @@
+{
+   "links" : {
+      "pages" : {
+         "next" : "https://api.digitalocean.com/v2/droplets/5425561/kernels?page=2",
+         "last" : "https://api.digitalocean.com/v2/droplets/5425561/kernels?page=2"
+      }
+   },
+   "meta" : {
+      "total" : 10
+   },
+   "kernels" : [
+      {
+         "id" : 231,
+         "version" : "3.8.0-25-generic",
+         "name" : "DO-recovery-static-fsck"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-41-generic-pae",
+         "version" : "2.6.32-41-generic-pae",
+         "id" : 61
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-56-generic-pae",
+         "id" : 946,
+         "version" : "2.6.32-56-generic-pae"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-57-generic-pae",
+         "id" : 987,
+         "version" : "2.6.32-57-generic-pae"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-58-generic-pae",
+         "id" : 1269,
+         "version" : "2.6.32-58-generic-pae"
+      }
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/kernels-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/kernels-last.json b/providers/digitalocean2/src/test/resources/kernels-last.json
new file mode 100644
index 0000000..ca6c4f6
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/kernels-last.json
@@ -0,0 +1,38 @@
+{
+   "links" : {
+      "pages" : {
+         "first" : "https://api.digitalocean.com/v2/droplets/5425561/kernels?page=2",
+         "prev" : "https://api.digitalocean.com/v2/droplets/5425561/kernels?page=2"
+      }
+   },
+   "meta" : {
+      "total" : 10
+   },
+   "kernels" : [
+      {
+         "id" : 2311,
+         "version" : "3.8.0-25-generic",
+         "name" : "DO-recovery-static-fsck"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-41-generic-pae",
+         "version" : "2.6.32-41-generic-pae",
+         "id" : 6111
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-56-generic-pae",
+         "id" : 94611,
+         "version" : "2.6.32-56-generic-pae"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-57-generic-pae",
+         "id" : 98711,
+         "version" : "2.6.32-57-generic-pae"
+      },
+      {
+         "name" : "Ubuntu 10.04 x32 vmlinuz-2.6.32-58-generic-pae",
+         "id" : 1269,
+         "version" : "2.6.32-58-generic-pae"
+      }
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/key.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/key.json b/providers/digitalocean2/src/test/resources/key.json
new file mode 100644
index 0000000..db77937
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/key.json
@@ -0,0 +1,8 @@
+{
+   "ssh_key" : {
+     "id" : 767051,
+     "name" : "ubuntu-1204-64bit-338",
+     "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCIshoBsRRK73Kwq9zlKauwNWClpaR99TpAxjtP5DcL3EMCTCDepaNPMHhPYO1rogRyjZrc2nsJ1ImCbZ3eEGFjspyhIyPk1NbYkAFsoSV7eBtqy1V7ddZ8t1ZsDexigAA5GnXZSWL4O0oVyZpBTrkzhZ49lbWUq8ch3Hhulvml1BR4nTx92ZcYIFFr1S7NEtua9xhKvRvUcSmgL/0A8deOBgkc85y5ADcEIt+nrlrOtIOW/agX0VPXNFjRxYW7MCkRGoDObOYEaT/mj7PyKk0kimmemxAH5wd6aOI4C82TmHjQmXTwXZgpVlrsdbxomOGDDGNSy7HoQLI/xMRvEf+9",
+     "fingerprint" : "1a:cc:9b:88:c8:4f:b8:77:96:15:d2:0c:95:86:ff:90"
+    }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/keys-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/keys-first.json b/providers/digitalocean2/src/test/resources/keys-first.json
new file mode 100644
index 0000000..93cbdd7
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/keys-first.json
@@ -0,0 +1,43 @@
+{
+   "meta" : {
+      "total" : 17
+   },
+   "ssh_keys" : [
+      {
+         "id" : 767051,
+         "name" : "ubuntu-1204-64bit-338",
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCIshoBsRRK73Kwq9zlKauwNWClpaR99TpAxjtP5DcL3EMCTCDepaNPMHhPYO1rogRyjZrc2nsJ1ImCbZ3eEGFjspyhIyPk1NbYkAFsoSV7eBtqy1V7ddZ8t1ZsDexigAA5GnXZSWL4O0oVyZpBTrkzhZ49lbWUq8ch3Hhulvml1BR4nTx92ZcYIFFr1S7NEtua9xhKvRvUcSmgL/0A8deOBgkc85y5ADcEIt+nrlrOtIOW/agX0VPXNFjRxYW7MCkRGoDObOYEaT/mj7PyKk0kimmemxAH5wd6aOI4C82TmHjQmXTwXZgpVlrsdbxomOGDDGNSy7HoQLI/xMRvEf+9",
+         "fingerprint" : "1a:cc:9b:88:c8:4f:b8:77:96:15:d2:0c:95:86:ff:90"
+      },
+      {
+         "id" : 767067,
+         "name" : "ubuntu-1204-64bit-ea4",
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdkjezVub0t+lSNSQYbO+Kcd/SoOJBCYgETqroI+ysHpPjRRNTYp3XSqSB0O3snnXM1GRfjPFatYFBUO9ajsb8cq0pzDPEIh3+cn+ZEoQ5WPg8OpaU6zVt2fPZNwqE2FCWEr2naH2yj2qV7Wzd1T9xYRfFdQ19YU1HuUhs16zvF6cO4BJH0YX2HLtZkxtOHonSbBx78/hBoV1NgSKlzlHVj9rhqmJO0fcEBeia4y/6G/65n8Z3hrM3zwyoN/eBKV7ngeJ70VU8tcP/QK9anB6hx4lOOSnWro2j3GKDwjSADL2vbOGl4OS6kM2y+6I04A6UAUkMu7KWCSr1Zf0zC45N",
+         "fingerprint" : "8f:81:51:d2:91:09:25:8f:c2:9c:b2:c3:6f:67:55:77"
+      },
+      {
+         "name" : "ubuntu-1204-64bit-jdk8-850",
+         "id" : 767363,
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD0zjvxYEyNYsVd7lfYXJxLON98OTgdMizD14XYCHwhVdJkS5ht9h+6dpiWbgatVgb/xyB63N2EeW+yZvQGlohWFytd1HvX7C8LFVtS/h4nrFJxtEYc2QXRl+WRRys6ekxYy6cwf/WV8nQPitUIPQru7vKR8bRpMPDBh+2Va/k7ApLd83AM9UYwXgCxDC7wlL/ttdJCV40buwPVvbOot97+qFPyg600/s6MGMX5448VifeBEEZG/qjzHj4SSGpQn+12O0dFLpYxOruzLLBpu2QxwvIqj1Nsm2ij6seMh0J8a5rQPl3ssPYrDCi8QGhL2NyzDSmT/9tYMLOlhBj7x3zN",
+         "fingerprint" : "1c:17:a2:c2:00:42:89:63:23:79:01:d7:b8:e6:a6:b4"
+      },
+      {
+         "name" : "ubuntu-1204-64bit-jdk8-ead",
+         "id" : 767375,
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCTMLrMCDSPF3BBeDjzSIYtVi8ACkxrvG4VT58sI+XjpeE9c3KGekqXqeTKZFwLxRllgbox8j610ABvcdAstBBSooSYmuDjT2nFotiTcw+5iHUe4A8BHG3HSlqXfuPlBCZ9bE4MdDBTPXqaDjiuvUqWdjpUihZeX2W2OtznDXDMFtn846Df0hD2V7ixHqVVQRXgflSfIoajEY5MlVG/Iuh3cjPpoJZ/IN/FcQnHjNe1FdJM8fC7kF+yY8sEE+u10KlfR1Y9WTdJSIiz5QDdlR4RTPXYyb9wyctD/sxCJ3xAgfZOG7Ey8eZF0+0MSbxkUGQll/xLuaP2aA5owIfMkEoj",
+         "fingerprint" : "a1:4d:20:c8:25:ce:2f:fa:43:8d:21:6f:65:77:c9:c4"
+      },
+      {
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCj9vCuDlTrQd+9WqlsQxWLg7VO2oVwq960CyW9C7rEro07wPGDdiOQornhyy7KiSL0mqYzFT5j0vTICSYeiGXIO/4SFPekz7vRN1yjO2GKaPokl/eYnbt1ABqB41jL0YX4ySwTm11O8A5yqWMouyxiGlSr13p61S2LTim5r5Z2C+4dKWHduloxnyCJJsh/56ZquS/ERgcrZusis9ybIP3aP+5DbXmyokGU83qGuNqSjZXaWc7MGFRTZzzzR9dQsbMvTgPwpyFXtSruIC+u7ca8oJ0b7+9WCAj2g/GXzbv/dsiJyJkEOtx6k1NN8n02PmL3o50vM1GGAjBFUN65JghD",
+         "name" : "ubuntu-1204-64bit-jdk8-ce2",
+         "id" : 767390,
+         "fingerprint" : "7f:bc:1c:af:e5:51:b6:1b:f0:bd:93:de:60:1a:e9:25"
+      }
+   ],
+   "links" : {
+      "pages" : {
+         "next" : "https://api.digitalocean.com/v2/account/keys?page=2&per_page=5",
+         "last" : "https://api.digitalocean.com/v2/account/keys?page=2&per_page=5"
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/keys-last.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/keys-last.json b/providers/digitalocean2/src/test/resources/keys-last.json
new file mode 100644
index 0000000..becbed1
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/keys-last.json
@@ -0,0 +1,25 @@
+{
+   "meta" : {
+      "total" : 7
+   },
+   "ssh_keys" : [
+      {
+         "fingerprint" : "a1:df:a0:64:e4:36:10:6b:de:8d:e6:65:55:17:8f:31",
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCHcVV1Rb5+/sCDF0eZ9Hw8vrHF+HYxptZRaNmnWsjzjeupeIiFgq6W1gRrDKB6pjA1t8R0b1xlcfb0361B5fRuwkeQTr2ALior6cipTgUUHT4UKcZMah7n09/YMPj2I7vzxjdX07PyB1z4OkL7FT3zBrsDTaR+ZngeFAVG59WmCj6vD2EqoZ2PeiEKETZOvQnZ3MEGiU5sYX9odIh5mjO5Jg1Q7oLjFzBbmMQ0oo/gkte8nBybHlD20iq1kzvmoSV4ECbTIa48yr8cgsX0d4M29YV9v2WCBfqaSRTRGVhfsalu9sgeSTHZ7F68g1wEkPXOzE3ZCUlZsKpskiYmCcTP",
+         "name" : "ubuntu-1204-64bit-jdk8-77a",
+         "id" : 799690
+      },
+      {
+         "name" : "ubuntu-1204-64bit-jdk8-a14",
+         "public_key" : "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCeU4rK0wlk8YOOAWd9GURgJzOlgNkLEarq/y9RMXKq1zUW9Jf5J4UuztZm6HsSpAbiBfwL/QYBsFEhfDUZiW0B5p6CL0cR93vgUhGVgoi+v5Nl3KDEgMDryja48CdmzkCB3KoX4aRGRkahx6chrFByM5laEyGJETnDl0s4VyxZH07FGgQYqdWj8jg99stIvZ8Ktzlq5RMXPYePEA03vzzc5JrTEtdhuiy/wjxY8piq1j40uch6JEfcEzfPdkWY7LZt5KlOtrYoVFVmu+Mp3QSknhZPyMMZW5GOEtcr1IGtOEBCL3gJWz0E/H4e4Itt+3L2bNpNfHJmISJRXsNanuyn",
+         "fingerprint" : "f3:34:8d:dc:26:31:90:2b:55:f0:d4:77:d4:17:1e:f2",
+         "id" : 802469
+      }
+   ],
+   "links" : {
+      "pages" : {
+         "first" : "https://api.digitalocean.com/v2/account/keys?page=1&per_page=5",
+         "prev" : "https://api.digitalocean.com/v2/account/keys?page=1&per_page=5"
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/logback-test.xml
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/logback-test.xml b/providers/digitalocean2/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..4cac342
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/logback-test.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<configuration scan="false">
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>target/test-data/jclouds.log</file>
+        <encoder>
+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
+        </encoder>
+    </appender>
+    <appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
+        <file>target/test-data/jclouds-wire.log</file>
+        <encoder>
+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
+        </encoder>
+    </appender>
+    <appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
+        <file>target/jclouds-compute.log</file>
+        <encoder>
+            <Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
+        </encoder>
+    </appender>
+
+    <logger name="org.jclouds">
+        <level value="DEBUG" />
+        <appender-ref ref="FILE" />
+    </logger>
+    <logger name="jclouds.compute">
+        <level value="DEBUG" />
+        <appender-ref ref="COMPUTEFILE" />
+    </logger>
+    <logger name="jclouds.wire">
+        <level value="DEBUG" />
+        <appender-ref ref="WIREFILE" />
+    </logger>
+    <logger name="jclouds.headers">
+        <level value="DEBUG" />
+        <appender-ref ref="WIREFILE" />
+    </logger>
+    
+    <root>
+        <level value="INFO" />
+    </root>
+</configuration>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/power-cycle.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/power-cycle.json b/providers/digitalocean2/src/test/resources/power-cycle.json
new file mode 100644
index 0000000..7af5963
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/power-cycle.json
@@ -0,0 +1,33 @@
+{
+    "action": {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "in-progress",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "power_cycle"
+      }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/power-off.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/power-off.json b/providers/digitalocean2/src/test/resources/power-off.json
new file mode 100644
index 0000000..7affe0a
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/power-off.json
@@ -0,0 +1,33 @@
+{
+    "action": {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "in-progress",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "power_off"
+      }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/power-on.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/power-on.json b/providers/digitalocean2/src/test/resources/power-on.json
new file mode 100644
index 0000000..32e6e83
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/power-on.json
@@ -0,0 +1,33 @@
+{
+    "action": {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "in-progress",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "power_on"
+      }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/reboot.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/reboot.json b/providers/digitalocean2/src/test/resources/reboot.json
new file mode 100644
index 0000000..799500a
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/reboot.json
@@ -0,0 +1,33 @@
+{
+    "action": {
+         "region" : {
+            "name" : "New York 1",
+            "available" : true,
+            "slug" : "nyc1",
+            "features" : [
+               "virtio",
+               "backups",
+               "metadata"
+            ],
+            "sizes" : [
+               "512mb",
+               "8gb",
+               "16gb",
+               "32gb",
+               "48gb",
+               "64gb",
+               "1gb",
+               "2gb",
+               "4gb"
+            ]
+         },
+         "started_at" : "2015-05-19T15:17:55Z",
+         "status" : "in-progress",
+         "resource_type" : "droplet",
+         "resource_id" : 5347489,
+         "region_slug" : "nyc1",
+         "id" : 50900149,
+         "completed_at" : "2015-05-19T15:18:01Z",
+         "type" : "reboot"
+      }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/test/resources/regions-first.json
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/resources/regions-first.json b/providers/digitalocean2/src/test/resources/regions-first.json
new file mode 100644
index 0000000..5be4b3f
--- /dev/null
+++ b/providers/digitalocean2/src/test/resources/regions-first.json
@@ -0,0 +1,111 @@
+{
+   "links" : {
+      "pages" : {
+         "next" : "https://api.digitalocean.com/v2/regions?page=2&per_page=5",
+         "last" : "https://api.digitalocean.com/v2/regions?page=2&per_page=5"
+      }
+   },
+   "meta" : {
+      "total" : 10
+   },
+   "regions" : [
+      {
+         "sizes" : [
+            "32gb",
+            "16gb",
+            "2gb",
+            "1gb",
+            "4gb",
+            "8gb",
+            "512mb",
+            "64gb",
+            "48gb"
+         ],
+         "name" : "New York 1",
+         "slug" : "nyc1",
+         "features" : [
+            "virtio",
+            "backups",
+            "metadata"
+         ],
+         "available" : true
+      },
+      {
+         "slug" : "ams1",
+         "name" : "Amsterdam 1",
+         "available" : false,
+         "features" : [
+            "virtio",
+            "backups"
+         ],
+         "sizes" : []
+      },
+      {
+         "sizes" : [
+            "32gb",
+            "16gb",
+            "2gb",
+            "1gb",
+            "4gb",
+            "8gb",
+            "512mb",
+            "64gb",
+            "48gb"
+         ],
+         "name" : "San Francisco 1",
+         "slug" : "sfo1",
+         "features" : [
+            "virtio",
+            "private_networking",
+            "backups",
+            "ipv6",
+            "metadata"
+         ],
+         "available" : true
+      },
+      {
+         "sizes" : [
+            "32gb",
+            "16gb",
+            "2gb",
+            "1gb",
+            "4gb",
+            "8gb",
+            "512mb",
+            "64gb",
+            "48gb"
+         ],
+         "available" : true,
+         "features" : [
+            "virtio",
+            "private_networking",
+            "backups"
+         ],
+         "name" : "New York 2",
+         "slug" : "nyc2"
+      },
+      {
+         "features" : [
+            "virtio",
+            "private_networking",
+            "backups",
+            "ipv6",
+            "metadata"
+         ],
+         "available" : true,
+         "slug" : "ams2",
+         "name" : "Amsterdam 2",
+         "sizes" : [
+            "32gb",
+            "16gb",
+            "2gb",
+            "1gb",
+            "4gb",
+            "8gb",
+            "512mb",
+            "64gb",
+            "48gb"
+         ]
+      }
+   ]
+}
\ No newline at end of file