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 2014/05/21 09:56:42 UTC
git commit: JCLOUDS-543/JCLOUDS-572: Use slugs as IDs and assume a
node can have a null image if it used an imageId that has been removed
Repository: jclouds-labs
Updated Branches:
refs/heads/master c6ec94490 -> c5afe3cc4
JCLOUDS-543/JCLOUDS-572: Use slugs as IDs and assume a node can have a null image if it used an imageId that has been removed
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/c5afe3cc
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/c5afe3cc
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/c5afe3cc
Branch: refs/heads/master
Commit: c5afe3cc4af9819f71a12aae74444090955f9d9a
Parents: c6ec944
Author: Ignasi Barrera <na...@apache.org>
Authored: Mon May 19 12:56:56 2014 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Wed May 21 09:54:09 2014 +0200
----------------------------------------------------------------------
digitalocean/pom.xml | 2 +-
.../extensions/DigitalOceanImageExtension.java | 12 +-
.../functions/DropletToNodeMetadata.java | 75 ++++++++++---
.../compute/functions/ImageToImage.java | 4 +-
.../compute/functions/RegionToLocation.java | 3 +-
.../compute/functions/SizeToHardware.java | 3 +-
.../DigitalOceanComputeServiceAdapter.java | 18 ++-
.../compute/util/LocationNamingUtils.java | 70 ++++++++++++
.../org/jclouds/digitalocean/domain/Size.java | 7 +-
.../digitalocean/features/DropletApi.java | 46 +++++++-
.../jclouds/digitalocean/features/EventApi.java | 2 +
.../jclouds/digitalocean/features/ImageApi.java | 55 ++++++++-
.../digitalocean/features/KeyPairApi.java | 5 +-
.../functions/DropletToNodeMetadataTest.java | 29 ++++-
.../compute/functions/ImageToImageTest.java | 5 +-
.../compute/functions/SizeToHardwareTest.java | 5 +-
.../compute/util/LocationNamingUtilsTest.java | 111 +++++++++++++++++++
.../features/DropletApiLiveTest.java | 20 +++-
.../features/DropletApiMockTest.java | 52 +++++++++
.../digitalocean/features/ImageApiLiveTest.java | 36 ++++++
.../digitalocean/features/ImageApiMockTest.java | 54 ++++++++-
digitalocean/src/test/resources/image1.json | 2 +-
digitalocean/src/test/resources/image2.json | 2 +-
digitalocean/src/test/resources/images.json | 4 +-
digitalocean/src/test/resources/sizes.json | 8 +-
25 files changed, 572 insertions(+), 58 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/pom.xml
----------------------------------------------------------------------
diff --git a/digitalocean/pom.xml b/digitalocean/pom.xml
index 92f77b3..ae8d972 100644
--- a/digitalocean/pom.xml
+++ b/digitalocean/pom.xml
@@ -39,7 +39,7 @@
<test.digitalocean.identity>FIXME</test.digitalocean.identity>
<test.digitalocean.credential>FIXME</test.digitalocean.credential>
<!-- CentOS 6.5 x64 -->
- <test.digitalocean.template>imageId=3448641</test.digitalocean.template>
+ <test.digitalocean.template>imageId=centos-6-5-x64</test.digitalocean.template>
<jclouds.osgi.export>org.jclouds.digitalocean*;version="${jclouds.version}"</jclouds.osgi.export>
<jclouds.osgi.import>
org.jclouds.compute.internal;version="${jclouds.version}",
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/extensions/DigitalOceanImageExtension.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/extensions/DigitalOceanImageExtension.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/extensions/DigitalOceanImageExtension.java
index 2248b1e..fd1e761 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/extensions/DigitalOceanImageExtension.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/extensions/DigitalOceanImageExtension.java
@@ -43,6 +43,7 @@ import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
+import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.ListenableFuture;
/**
@@ -116,7 +117,16 @@ public class DigitalOceanImageExtension implements ImageExtension {
@Override
public boolean deleteImage(String id) {
try {
- api.getImageApi().delete(Integer.parseInt(id));
+ // The id of the image can be an id or a slug. Use the corresponding method of the API depending on what is
+ // provided. If it can be parsed as a number, use the method to destroy by ID. Otherwise, destroy by slug.
+ Integer imageId = Ints.tryParse(id);
+ if (imageId != null) {
+ logger.debug(">> image does not have a slug. Using the id to delete the image...");
+ api.getImageApi().delete(imageId);
+ } else {
+ logger.debug(">> image has a slug. Using it to delete the image...");
+ api.getImageApi().delete(id);
+ }
return true;
} catch (Exception ex) {
return false;
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadata.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadata.java
index db0f5c9..fdd6b71 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadata.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadata.java
@@ -18,11 +18,15 @@ package org.jclouds.digitalocean.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.tryFind;
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.extractRegionId;
import java.util.Map;
import java.util.Set;
+import javax.annotation.Resource;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.collect.Memoized;
@@ -32,15 +36,19 @@ import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.digitalocean.domain.Droplet;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
+import org.jclouds.logging.Logger;
import com.google.common.base.Function;
+import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
/**
* Transforms an {@link Droplet} to the jclouds portable model.
@@ -51,6 +59,10 @@ import com.google.common.collect.ImmutableSet;
@Singleton
public class DropletToNodeMetadata implements Function<Droplet, NodeMetadata> {
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
private final Supplier<Map<String, ? extends Image>> images;
private final Supplier<Map<String, ? extends Hardware>> hardwares;
private final Supplier<Set<? extends Location>> locations;
@@ -80,19 +92,18 @@ public class DropletToNodeMetadata implements Function<Droplet, NodeMetadata> {
builder.hostname(input.getName());
builder.group(groupNamingConvention.extractGroup(input.getName()));
- builder.hardware(hardwares.get().get(String.valueOf(input.getSizeId())));
-
- final String regionIdPattern = input.getRegionId() + "/";
- builder.location(find(locations.get(), new Predicate<Location>() {
- @Override
- public boolean apply(Location location) {
- return location.getDescription().startsWith(regionIdPattern);
- }
- }));
-
- Image image = images.get().get(String.valueOf(input.getImageId()));
- builder.imageId(image.getId());
- builder.operatingSystem(image.getOperatingSystem());
+ builder.hardware(getHardware(input.getSizeId()));
+ builder.location(getLocation(input.getRegionId()));
+
+ Optional<? extends Image> image = findImage(input.getImageId());
+ if (image.isPresent()) {
+ builder.imageId(image.get().getId());
+ builder.operatingSystem(image.get().getOperatingSystem());
+ } else {
+ logger.info(">> image with id %s for droplet %s was not found. "
+ + "This might be because the image that was used to create the droplet has a new id.",
+ input.getImageId(), input.getId());
+ }
builder.status(toPortableStatus.apply(input.getStatus()));
builder.backendStatus(input.getStatus().name());
@@ -113,4 +124,42 @@ public class DropletToNodeMetadata implements Function<Droplet, NodeMetadata> {
return builder.build();
}
+
+ protected Optional<? extends Image> findImage(Integer id) {
+ // Try to find the image by ID in the cache. The cache is indexed by slug (for public images) and by id (for
+ // private ones).
+ final String imageId = String.valueOf(id);
+ Optional<? extends Image> image = Optional.fromNullable(images.get().get(imageId));
+ if (!image.isPresent()) {
+ // If it is a public image (indexed by slug) but the "int" form of the id was provided, try to find it in the
+ // whole list of cached images
+ image = tryFind(images.get().values(), new Predicate<Image>() {
+ @Override
+ public boolean apply(Image input) {
+ return input.getProviderId().equals(imageId);
+ }
+ });
+ }
+ return image;
+ }
+
+ protected Hardware getHardware(Integer id) {
+ // Hardwares are indexed by slug, but the droplet only provides its ID.
+ final String hardwareId = String.valueOf(id);
+ return Iterables.find(hardwares.get().values(), new Predicate<Hardware>() {
+ @Override
+ public boolean apply(Hardware input) {
+ return input.getProviderId().equals(hardwareId);
+ }
+ });
+ }
+
+ protected Location getLocation(final Integer id) {
+ return find(locations.get(), new Predicate<Location>() {
+ @Override
+ public boolean apply(Location location) {
+ return id.equals(extractRegionId(location));
+ }
+ });
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/ImageToImage.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/ImageToImage.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/ImageToImage.java
index d29b864..3e33ed9 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/ImageToImage.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/ImageToImage.java
@@ -40,7 +40,9 @@ public class ImageToImage implements Function<Image, org.jclouds.compute.domain.
@Override
public org.jclouds.compute.domain.Image apply(final Image input) {
ImageBuilder builder = new ImageBuilder();
- builder.ids(String.valueOf(input.getId()));
+ // Private images don't have a slug
+ builder.id(input.getSlug() != null ? input.getSlug() : String.valueOf(input.getId()));
+ builder.providerId(String.valueOf(input.getId()));
builder.name(input.getName());
builder.description(input.getName());
builder.status(Status.AVAILABLE);
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/RegionToLocation.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/RegionToLocation.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/RegionToLocation.java
index f1c5ed1..0406be0 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/RegionToLocation.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/RegionToLocation.java
@@ -18,6 +18,7 @@ package org.jclouds.digitalocean.compute.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.encodeRegionIdAndName;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -51,7 +52,7 @@ public class RegionToLocation implements Function<Region, Location> {
public Location apply(Region input) {
LocationBuilder builder = new LocationBuilder();
builder.id(input.getSlug());
- builder.description(input.getId() + "/" + input.getName());
+ builder.description(encodeRegionIdAndName(input));
builder.scope(LocationScope.REGION);
builder.parent(getOnlyElement(justProvider.get()));
builder.iso3166Codes(ImmutableSet.<String> of());
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/SizeToHardware.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/SizeToHardware.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/SizeToHardware.java
index 6dccb64..dc7577b 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/SizeToHardware.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/functions/SizeToHardware.java
@@ -40,7 +40,8 @@ public class SizeToHardware implements Function<Size, Hardware> {
@Override
public Hardware apply(Size input) {
HardwareBuilder builder = new HardwareBuilder();
- builder.ids(String.valueOf(input.getId()));
+ builder.id(input.getSlug());
+ builder.providerId(String.valueOf(input.getId()));
builder.name(input.getName());
builder.ram(input.getMemory());
// DigitalOcean does not provide the processor speed. We configure it to
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/strategy/DigitalOceanComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/strategy/DigitalOceanComputeServiceAdapter.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/strategy/DigitalOceanComputeServiceAdapter.java
index bef9e06..ff7150e 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/strategy/DigitalOceanComputeServiceAdapter.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/strategy/DigitalOceanComputeServiceAdapter.java
@@ -19,10 +19,10 @@ package org.jclouds.digitalocean.compute.strategy;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.find;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.extractRegionId;
import java.util.Map;
@@ -48,6 +48,7 @@ import org.jclouds.ssh.SshKeyPairGenerator;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
+import com.google.common.primitives.Ints;
/**
* Implementation of the Compute Service for the DigitalOcean API.
@@ -114,17 +115,11 @@ public class DigitalOceanComputeServiceAdapter implements ComputeServiceAdapter<
}
// Find the location where the Droplet has to be created
- final String locationId = template.getLocation().getId();
- Region region = find(api.getRegionApi().list(), new Predicate<Region>() {
- @Override
- public boolean apply(Region input) {
- return input.getSlug().equals(locationId);
- }
- });
+ int regionId = extractRegionId(template.getLocation());
DropletCreation dropletCreation = api.getDropletApi().create(name,
Integer.parseInt(template.getImage().getProviderId()),
- Integer.parseInt(template.getHardware().getProviderId()), region.getId(), options.build());
+ Integer.parseInt(template.getHardware().getProviderId()), regionId, options.build());
// We have to actively wait until the droplet has been provisioned until
// we can build the entire Droplet object we want to return
@@ -169,7 +164,10 @@ public class DigitalOceanComputeServiceAdapter implements ComputeServiceAdapter<
@Override
public Image getImage(String id) {
- return api.getImageApi().get(Integer.parseInt(id));
+ // The id of the image can be an id or a slug. Use the corresponding method of the API depending on what is
+ // provided. If it can be parsed as a number, use the method to get by ID. Otherwise, get by slug.
+ Integer imageId = Ints.tryParse(id);
+ return imageId != null ? api.getImageApi().get(imageId) : api.getImageApi().get(id);
}
@Override
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/compute/util/LocationNamingUtils.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/compute/util/LocationNamingUtils.java b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/util/LocationNamingUtils.java
new file mode 100644
index 0000000..abbf4dc
--- /dev/null
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/compute/util/LocationNamingUtils.java
@@ -0,0 +1,70 @@
+/*
+ * 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.digitalocean.compute.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.digitalocean.domain.Region;
+import org.jclouds.domain.Location;
+
+/**
+ * Utility class to encode and decode the region id and name in a {@link Location}.
+ *
+ * @author Ignasi Barrera
+ */
+public class LocationNamingUtils {
+
+ /**
+ * Extracts the region id for the given location.
+ *
+ * @param location The location to extract the region id from.
+ * @return The id of the region.
+ */
+ public static int extractRegionId(Location location) {
+ checkNotNull(location, "location cannot be null");
+ String regionIdAndName = location.getDescription();
+ int index = regionIdAndName.indexOf('/');
+ checkArgument(index >= 0, "location description should be in the form 'regionId/regionName'");
+ return Integer.parseInt(regionIdAndName.substring(0, index));
+ }
+
+ /**
+ * Extracts the region name for the given location.
+ *
+ * @param location The location to extract the region name from.
+ * @return The name of the region.
+ */
+ public static String extractRegionName(Location location) {
+ checkNotNull(location, "location cannot be null");
+ String regionIdAndName = location.getDescription();
+ int index = regionIdAndName.indexOf('/');
+ checkArgument(index >= 0, "location description should be in the form 'regionId/regionName'");
+ return regionIdAndName.substring(index + 1);
+ }
+
+ /**
+ * Encodes the id and name of the given region into a String so it can be populated in a {@link Location} object.
+ *
+ * @param region The region to encode.
+ * @return The encoded id and name for the given region.
+ */
+ public static String encodeRegionIdAndName(Region region) {
+ checkNotNull(region, "region cannot be null");
+ return region.getId() + "/" + region.getName();
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/domain/Size.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/domain/Size.java b/digitalocean/src/main/java/org/jclouds/digitalocean/domain/Size.java
index b1c7c63..6687564 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/domain/Size.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/domain/Size.java
@@ -22,8 +22,6 @@ import java.beans.ConstructorProperties;
import javax.inject.Named;
-import org.jclouds.javax.annotation.Nullable;
-
/**
* A Size.
*
@@ -44,11 +42,10 @@ public class Size {
private final String costPerMonth;
@ConstructorProperties({ "id", "name", "slug", "memory", "cpu", "disk", "cost_per_hour", "cost_per_month" })
- public Size(int id, String name, @Nullable String slug, int memory, int cpu, int disk, String costPerHour,
- String costPerMonth) {
+ public Size(int id, String name, String slug, int memory, int cpu, int disk, String costPerHour, String costPerMonth) {
this.id = id;
this.name = checkNotNull(name, "name cannot be null");
- this.slug = slug;
+ this.slug = checkNotNull(slug, "slug");
this.memory = memory;
this.cpu = cpu;
this.disk = disk;
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/features/DropletApi.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/features/DropletApi.java b/digitalocean/src/main/java/org/jclouds/digitalocean/features/DropletApi.java
index 4c57db2..0cf838b 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/features/DropletApi.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/features/DropletApi.java
@@ -31,6 +31,7 @@ import org.jclouds.digitalocean.domain.Droplet;
import org.jclouds.digitalocean.domain.DropletCreation;
import org.jclouds.digitalocean.domain.options.CreateDropletOptions;
import org.jclouds.digitalocean.http.filters.AuthenticationFilter;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
@@ -69,6 +70,7 @@ public interface DropletApi extends Closeable {
@Path("/{id}")
@SelectJson("droplet")
@Fallback(NullOnNotFoundOr404.class)
+ @Nullable
Droplet get(@PathParam("id") int id);
/**
@@ -79,6 +81,8 @@ public interface DropletApi extends Closeable {
* @param sizeId The size to use to create the droplet.
* @param regionId The region where the droplet must be created.
* @return The created droplet.
+ *
+ * @see #create(String, String, String, String)
*/
@Named("droplet:create")
@GET
@@ -96,6 +100,8 @@ public interface DropletApi extends Closeable {
* @param regionId The region where the droplet must be created.
* @param options Custom options to create the droplet.
* @return The created droplet.
+ *
+ * @see #create(String, String, String, String, CreateDropletOptions)
*/
@Named("droplet:create")
@GET
@@ -105,6 +111,44 @@ public interface DropletApi extends Closeable {
@QueryParam("size_id") int sizeId, @QueryParam("region_id") int regionId, CreateDropletOptions options);
/**
+ * Creates a new droplet.
+ *
+ * @param name The name for the new droplet.
+ * @param imageSlug The slug of the image to use to create the droplet.
+ * @param sizeSlug The slug of the size to use to create the droplet.
+ * @param regionSlug The slug region where the droplet must be created.
+ * @return The created droplet.
+ *
+ * @see #create(String, int, int, int)
+ */
+ @Named("droplet:create")
+ @GET
+ @Path("/new")
+ @SelectJson("droplet")
+ DropletCreation create(@QueryParam("name") String name, @QueryParam("image_slug") String imageSlug,
+ @QueryParam("size_slug") String sizeSlug, @QueryParam("region_slug") String regionSlug);
+
+ /**
+ * Creates a new droplet.
+ *
+ * @param name The name for the new droplet.
+ * @param imageSlug The slug of the image to use to create the droplet.
+ * @param sizeSlug The slug of the size to use to create the droplet.
+ * @param regionSlug The slug region where the droplet must be created.
+ * @param options Custom options to create the droplet.
+ * @return The created droplet.
+ *
+ * @see #create(String, int, int, int, CreateDropletOptions)
+ */
+ @Named("droplet:create")
+ @GET
+ @Path("/new")
+ @SelectJson("droplet")
+ DropletCreation create(@QueryParam("name") String name, @QueryParam("image_slug") String imageSlug,
+ @QueryParam("size_slug") String sizeSlug, @QueryParam("region_slug") String regionSlug,
+ CreateDropletOptions options);
+
+ /**
* Reboots the given droplet.
*
* @param id The id of the droplet to reboot.
@@ -275,7 +319,7 @@ public interface DropletApi extends Closeable {
*
* @param id The id of the droplet to destroy.
* @param scrubData If true this will strictly write 0s to your prior partition to ensure that all data is completely
- * erased.
+ * erased.
* @return The id of the event to track the destroy process.
*/
@Named("droplet:destroy")
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/features/EventApi.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/features/EventApi.java b/digitalocean/src/main/java/org/jclouds/digitalocean/features/EventApi.java
index 5da2685..aa7ebee 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/features/EventApi.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/features/EventApi.java
@@ -27,6 +27,7 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.digitalocean.domain.Event;
import org.jclouds.digitalocean.http.filters.AuthenticationFilter;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
@@ -55,5 +56,6 @@ public interface EventApi extends Closeable {
@Path("/{id}")
@Fallback(NullOnNotFoundOr404.class)
@SelectJson("event")
+ @Nullable
Event get(@PathParam("id") int id);
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/features/ImageApi.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/features/ImageApi.java b/digitalocean/src/main/java/org/jclouds/digitalocean/features/ImageApi.java
index 53d99c5..ecfba50 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/features/ImageApi.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/features/ImageApi.java
@@ -29,6 +29,7 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.digitalocean.domain.Image;
import org.jclouds.digitalocean.http.filters.AuthenticationFilter;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
@@ -58,19 +59,39 @@ public interface ImageApi extends Closeable {
/**
* Gets the details of the given image.
+ * <p>
+ * Note that Image IDs can change. The recommended way to get an image is using the {@link #get(String)} method.
*
* @param id The id of the image to get.
- * @return The details of the image or <code>null</code> if no image exists
- * with the given id.
+ * @return The details of the image or <code>null</code> if no image exists with the given id.
+ *
+ * @see #get(String)
*/
@Named("image:get")
@GET
@Path("/{id}")
@SelectJson("image")
@Fallback(NullOnNotFoundOr404.class)
+ @Nullable
Image get(@PathParam("id") int id);
/**
+ * Gets the details of the given image.
+ *
+ * @param slug The slug of the image to get.
+ * @return The details of the image or <code>null</code> if no image exists with the given slug.
+ *
+ * @see #get(int)
+ */
+ @Named("image:get")
+ @GET
+ @Path("/{slug}")
+ @SelectJson("image")
+ @Fallback(NullOnNotFoundOr404.class)
+ @Nullable
+ Image get(@PathParam("slug") String slug);
+
+ /**
* Deletes an existing image.
*
* @param id The id of the key pair.
@@ -81,16 +102,42 @@ public interface ImageApi extends Closeable {
void delete(@PathParam("id") int id);
/**
+ * Deletes an existing image.
+ *
+ * @param slug The slug of the key pair.
+ */
+ @Named("image:delete")
+ @GET
+ @Path("/{slug}/destroy")
+ void delete(@PathParam("slug") String slug);
+
+ /**
* Transfers the image to the given region.
*
* @param id The id of the image to transfer.
- * @param regionId The id of the region to which the image will be
- * transferred.
+ * @param regionId The id of the region to which the image will be transferred.
* @return The id of the event to track the transfer process.
+ *
+ * @see #transfer(String, int)
*/
@Named("image:transfer")
@GET
@Path("/{id}/transfer")
@SelectJson("event_id")
int transfer(@PathParam("id") int id, @QueryParam("region_id") int regionId);
+
+ /**
+ * Transfers the image to the given region.
+ *
+ * @param slug The slug of the image to transfer.
+ * @param regionId The id of the region to which the image will be transferred.
+ * @return The id of the event to track the transfer process.
+ *
+ * @see #transfer(int, int)
+ */
+ @Named("image:transfer")
+ @GET
+ @Path("/{slug}/transfer")
+ @SelectJson("event_id")
+ int transfer(@PathParam("slug") String slug, @QueryParam("region_id") int regionId);
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/main/java/org/jclouds/digitalocean/features/KeyPairApi.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/main/java/org/jclouds/digitalocean/features/KeyPairApi.java b/digitalocean/src/main/java/org/jclouds/digitalocean/features/KeyPairApi.java
index b8b2dfb..8924a0b 100644
--- a/digitalocean/src/main/java/org/jclouds/digitalocean/features/KeyPairApi.java
+++ b/digitalocean/src/main/java/org/jclouds/digitalocean/features/KeyPairApi.java
@@ -29,6 +29,7 @@ import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.digitalocean.domain.SshKey;
import org.jclouds.digitalocean.http.filters.AuthenticationFilter;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.SelectJson;
@@ -60,14 +61,14 @@ public interface KeyPairApi extends Closeable {
* Gets the details of an existing SSH key pair.
*
* @param id The id of the SSH key pair.
- * @return The details of the SSH key pair or <code>null</code> if no key
- * exists with the given id.
+ * @return The details of the SSH key pair or <code>null</code> if no key exists with the given id.
*/
@Named("key:get")
@GET
@Path("/{id}")
@SelectJson("ssh_key")
@Fallback(NullOnNotFoundOr404.class)
+ @Nullable
SshKey get(@PathParam("id") int id);
/**
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadataTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadataTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadataTest.java
index 9a49df7..e0f1ae4 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadataTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/DropletToNodeMetadataTest.java
@@ -80,14 +80,15 @@ public class DropletToNodeMetadataTest {
@BeforeMethod
public void setup() {
images = ImmutableSet.of(new ImageBuilder()
- .ids("1")
+ .id("ubuntu-1404-x86")
+ .providerId("1")
.name("mock image")
.status(AVAILABLE)
.operatingSystem(
OperatingSystem.builder().name("Ubuntu 14.04 x86_64").description("Ubuntu").family(OsFamily.UBUNTU)
.version("10.04").arch("x86_64").is64Bit(true).build()).build());
- hardwares = ImmutableSet.of(new HardwareBuilder().ids("1").name("mock hardware")
+ hardwares = ImmutableSet.of(new HardwareBuilder().id("2gb").providerId("1").name("mock hardware")
.processor(new Processor(1.0, 1.0)).ram(2048)
.volume(new VolumeBuilder().size(20f).type(Type.LOCAL).build()).build());
@@ -109,14 +110,34 @@ public class DropletToNodeMetadataTest {
Droplet droplet = new Droplet(1, "mock-droplet", 1, 1, 1, false, ImmutableList.of(), ImmutableList.of(),
"84.45.69.3", "192.168.2.5", false, ACTIVE, new Date());
- NodeMetadata expected = new NodeMetadataBuilder().ids("1").hardware(getOnlyElement(hardwares)).imageId("1")
+ NodeMetadata expected = new NodeMetadataBuilder().ids("1").hardware(getOnlyElement(hardwares))
+ .imageId("ubuntu-1404-x86").status(RUNNING).location(getOnlyElement(locations)).name("mock-droplet")
+ .hostname("mock-droplet").group("mock").credentials(credentials)
+ .publicAddresses(ImmutableSet.of("84.45.69.3")).privateAddresses(ImmutableSet.of("192.168.2.5"))
+ .providerId("1").backendStatus(ACTIVE.name()).operatingSystem(getOnlyElement(images).getOperatingSystem())
+ .build();
+
+ NodeMetadata actual = function.apply(droplet);
+ assertNodeEquals(actual, expected);
+ }
+
+ @Test
+ public void testConvertDropletOldImage() throws ParseException {
+ // Use an image id that is not in the list of images
+ Droplet droplet = new Droplet(1, "mock-droplet", 9999, 1, 1, false, ImmutableList.of(), ImmutableList.of(),
+ "84.45.69.3", "192.168.2.5", false, ACTIVE, new Date());
+
+ NodeMetadata expected = new NodeMetadataBuilder().ids("1").hardware(getOnlyElement(hardwares)).imageId(null)
.status(RUNNING).location(getOnlyElement(locations)).name("mock-droplet").hostname("mock-droplet")
.group("mock").credentials(credentials).publicAddresses(ImmutableSet.of("84.45.69.3"))
.privateAddresses(ImmutableSet.of("192.168.2.5")).providerId("1").backendStatus(ACTIVE.name())
- .operatingSystem(getOnlyElement(images).getOperatingSystem()).build();
+ .operatingSystem(null).build();
NodeMetadata actual = function.apply(droplet);
+ assertNodeEquals(actual, expected);
+ }
+ private static void assertNodeEquals(NodeMetadata actual, NodeMetadata expected) {
assertEquals(actual, expected);
// NodeMetadata equals method does not use all fields in equals. It assumes that same ids in same locations
// determine the equivalence
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/ImageToImageTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/ImageToImageTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/ImageToImageTest.java
index a591286..f05c78d 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/ImageToImageTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/ImageToImageTest.java
@@ -37,9 +37,10 @@ public class ImageToImageTest {
@Test
public void testConvertImage() {
- Image image = new Image(1, "Ubuntu 14.04 x64", "Ubuntu 14.04 x64", true, "ubuntu-1404");
+ Image image = new Image(1, "Ubuntu 14.04 x64", "Ubuntu 14.04 x64", true, "ubuntu-1404-x86");
org.jclouds.compute.domain.Image expected = new ImageBuilder()
- .ids("1")
+ .id("ubuntu-1404-x86")
+ .providerId("1")
.name("Ubuntu 14.04 x64")
.status(AVAILABLE)
.operatingSystem(
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/SizeToHardwareTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/SizeToHardwareTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/SizeToHardwareTest.java
index 507c626..9691c25 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/SizeToHardwareTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/functions/SizeToHardwareTest.java
@@ -38,8 +38,9 @@ public class SizeToHardwareTest {
@Test
public void testConvertSize() {
- Size size = new Size(1, "Medium", "2GB", 2048, 1, 20, "0.05", "10");
- Hardware expected = new HardwareBuilder().ids("1").name("Medium").processor(new Processor(1.0, 1.0)).ram(2048)
+ Size size = new Size(1, "Medium", "2gb", 2048, 1, 20, "0.05", "10");
+ Hardware expected = new HardwareBuilder().id("2gb").providerId("1").name("Medium")
+ .processor(new Processor(1.0, 1.0)).ram(2048)
.volume(new VolumeBuilder().size(20f).type(Type.LOCAL).build())
.userMetadata(ImmutableMap.of("costPerHour", "0.05", "costPerMonth", "10")).build();
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/compute/util/LocationNamingUtilsTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/compute/util/LocationNamingUtilsTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/util/LocationNamingUtilsTest.java
new file mode 100644
index 0000000..4f5ec06
--- /dev/null
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/compute/util/LocationNamingUtilsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.digitalocean.compute.util;
+
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.encodeRegionIdAndName;
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.extractRegionId;
+import static org.jclouds.digitalocean.compute.util.LocationNamingUtils.extractRegionName;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+import org.jclouds.digitalocean.domain.Region;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link LocationNamingUtils} class.
+ *
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "LocationNamingUtilsTest")
+public class LocationNamingUtilsTest {
+
+ @Test
+ public void testExtractRegionId() {
+ assertEquals(1, extractRegionId(location("1/foo")));
+ assertEquals(1, extractRegionId(location("1///foo")));
+ assertEquals(1, extractRegionId(location("1/2/3/foo")));
+ }
+
+ @Test
+ public void testExtractRegionIdInvalidEncodedForms() {
+ assertInvalidRegionIdFormat("/");
+ assertInvalidRegionIdFormat("/foo");
+ assertInvalidRegionIdFormat("/1/2/foo");
+ }
+
+ @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "location cannot be null")
+ public void testExtractRegionIdNullLocation() {
+ extractRegionId(null);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "location description should be in the form 'regionId/regionName'")
+ public void testExtractRegionIdWithoutEncodedForm() {
+ extractRegionId(location("foobar"));
+ }
+
+ @Test
+ public void testExtractRegionName() {
+ assertEquals("foo", extractRegionName(location("1/foo")));
+ assertEquals("//foo", extractRegionName(location("1///foo")));
+ assertEquals("2/3/foo", extractRegionName(location("1/2/3/foo")));
+ }
+
+ @Test
+ public void testExtractRegionNameInvalidEncodedForms() {
+ assertEquals("", extractRegionName(location("/")));
+ assertEquals("foo", extractRegionName(location("/foo")));
+ assertEquals("1/2/foo", extractRegionName(location("/1/2/foo")));
+ }
+
+ @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "location cannot be null")
+ public void testExtractRegionNameNullLocation() {
+ extractRegionId(null);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "location description should be in the form 'regionId/regionName'")
+ public void testExtractRegionNameWithoutEncodedForm() {
+ extractRegionId(location("foobar"));
+ }
+
+ @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "region cannot be null")
+ public void testEncodeRegionAndNameNullRegion() {
+ encodeRegionIdAndName(null);
+ }
+
+ @Test
+ public void testEncodeRegionAndName() {
+ assertEquals("1/foo", encodeRegionIdAndName(new Region(1, "foo", "bar")));
+ assertEquals("1/1", encodeRegionIdAndName(new Region(1, "1", "1")));
+ assertEquals("1///", encodeRegionIdAndName(new Region(1, "//", "1")));
+ }
+
+ private static void assertInvalidRegionIdFormat(String encoded) {
+ try {
+ extractRegionId(location(encoded));
+ fail("Encoded form [" + encoded + "] shouldn't produce a valid region id");
+ } catch (NumberFormatException ex) {
+ // Success
+ }
+ }
+
+ private static Location location(String description) {
+ return new LocationBuilder().id("location").description(description).scope(LocationScope.REGION).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
index 9816729..732207d 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiLiveTest.java
@@ -45,7 +45,9 @@ import com.google.common.base.Predicate;
public class DropletApiLiveTest extends BaseDigitalOceanLiveTest {
private DropletCreation dropletCreation;
+ private DropletCreation dropletCreationUsingSlugs;
private Droplet droplet;
+ private Droplet dropletUsingSlugs;
private Image snapshot;
@Override
@@ -60,6 +62,10 @@ public class DropletApiLiveTest extends BaseDigitalOceanLiveTest {
int event = api.getDropletApi().destroy(droplet.getId(), true);
assertTrue(event > 0, "The event id should not be null");
}
+ if (dropletUsingSlugs != null) {
+ int event = api.getDropletApi().destroy(dropletUsingSlugs.getId(), true);
+ assertTrue(event > 0, "The event id should not be null");
+ }
if (snapshot != null) {
api.getImageApi().delete(snapshot.getId());
}
@@ -73,12 +79,24 @@ public class DropletApiLiveTest extends BaseDigitalOceanLiveTest {
assertTrue(dropletCreation.getEventId() > 0, "Droplet creation event id should be > 0");
}
- @Test(dependsOnMethods = "testCreateDroplet")
+ public void testCreateDropletUsingSlugs() {
+ dropletCreationUsingSlugs = api.getDropletApi().create("droplettestwithslugs", defaultImage.getSlug(),
+ defaultSize.getSlug(), defaultRegion.getSlug());
+
+ assertTrue(dropletCreationUsingSlugs.getId() > 0, "Created droplet id should be > 0");
+ assertTrue(dropletCreationUsingSlugs.getEventId() > 0, "Droplet creation event id should be > 0");
+ }
+
+ @Test(dependsOnMethods = { "testCreateDroplet", "testCreateDropletUsingSlugs" })
public void testGetDroplet() {
waitForEvent(dropletCreation.getEventId());
+ waitForEvent(dropletCreationUsingSlugs.getEventId());
+
droplet = api.getDropletApi().get(dropletCreation.getId());
+ dropletUsingSlugs = api.getDropletApi().get(dropletCreationUsingSlugs.getId());
assertNotNull(droplet, "Created droplet should not be null");
+ assertNotNull(dropletUsingSlugs, "Created droplet using slugs should not be null");
}
@Test(dependsOnMethods = "testGetDroplet")
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
index 32c247d..3bf9088 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/DropletApiMockTest.java
@@ -104,6 +104,58 @@ public class DropletApiMockTest extends BaseDigitalOceanMockTest {
}
}
+ public void testCreateDropletUsingSlugs() throws Exception {
+ MockWebServer server = mockWebServer();
+ server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet-creation.json")));
+
+ DigitalOceanApi api = api(server.getUrl("/"));
+ DropletApi dropletApi = api.getDropletApi();
+
+ try {
+ DropletCreation droplet = dropletApi.create("test", "img-1", "size-1", "region-1");
+
+ assertRequestHasParameters(server.takeRequest(), "/droplets/new", ImmutableMultimap.of("name", "test",
+ "image_slug", "img-1", "size_slug", "size-1", "region_slug", "region-1"));
+
+ assertNotNull(droplet);
+ assertEquals(droplet.getName(), "test");
+ } finally {
+ api.close();
+ server.shutdown();
+ }
+ }
+
+ public void testCreateDropletUsingSlugsWithOptions() throws Exception {
+ MockWebServer server = mockWebServer();
+ server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet-creation.json")));
+
+ DigitalOceanApi api = api(server.getUrl("/"));
+ DropletApi dropletApi = api.getDropletApi();
+
+ try {
+ CreateDropletOptions options = CreateDropletOptions.builder().addSshKeyId(5).addSshKeyId(4)
+ .privateNetworking(true).backupsEnabled(false).build();
+ DropletCreation droplet = dropletApi.create("test", "img-1", "size-1", "region-1", options);
+
+ ImmutableMultimap.Builder<String, String> params = ImmutableMultimap.builder();
+ params.put("name", "test");
+ params.put("image_slug", "img-1");
+ params.put("size_slug", "size-1");
+ params.put("region_slug", "region-1");
+ params.put("ssh_key_ids", "5,4");
+ params.put("private_networking", "true");
+ params.put("backups_enabled", "false");
+
+ assertRequestHasParameters(server.takeRequest(), "/droplets/new", params.build());
+
+ assertNotNull(droplet);
+ assertEquals(droplet.getName(), "test");
+ } finally {
+ api.close();
+ server.shutdown();
+ }
+ }
+
public void testCreateDroplet() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setBody(payloadFromResource("/droplet-creation.json")));
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
index 2625424..75ba1e7 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiLiveTest.java
@@ -42,7 +42,9 @@ import com.google.common.base.Predicate;
public class ImageApiLiveTest extends BaseDigitalOceanLiveTest {
private Image snapshot;
+ private Image snapshotUsingSlug;
private DropletCreation droplet;
+ private DropletCreation dropletUsingSlug;
@Override
protected void initialize() {
@@ -56,12 +58,20 @@ public class ImageApiLiveTest extends BaseDigitalOceanLiveTest {
if (droplet != null) {
api.getDropletApi().destroy(droplet.getId(), true);
}
+ if (dropletUsingSlug != null) {
+ api.getDropletApi().destroy(dropletUsingSlug.getId(), true);
+ }
} finally {
if (snapshot != null) {
api.getImageApi().delete(snapshot.getId());
assertFalse(tryFind(api.getImageApi().list(), byName(snapshot.getName())).isPresent(),
"Snapshot should not exist after delete");
}
+ if (snapshotUsingSlug != null) {
+ api.getImageApi().delete(snapshotUsingSlug.getId());
+ assertFalse(tryFind(api.getImageApi().list(), byName(snapshotUsingSlug.getName())).isPresent(),
+ "Snapshot should not exist after delete");
+ }
}
}
@@ -69,6 +79,10 @@ public class ImageApiLiveTest extends BaseDigitalOceanLiveTest {
assertNotNull(api.getImageApi().get(defaultImage.getId()), "The image should not be null");
}
+ public void testGetImageBySlug() {
+ assertNotNull(api.getImageApi().get(defaultImage.getSlug()), "The image should not be null");
+ }
+
public void testGetImageNotFound() {
assertNull(api.getImageApi().get(-1));
}
@@ -95,6 +109,28 @@ public class ImageApiLiveTest extends BaseDigitalOceanLiveTest {
waitForEvent(transferEvent);
}
+ public void testTransferImageUsingSlug() {
+ dropletUsingSlug = api.getDropletApi().create("imagetransferdropletusingslug", defaultImage.getSlug(),
+ defaultSize.getSlug(), defaultRegion.getSlug());
+
+ assertTrue(dropletUsingSlug.getId() > 0, "Created droplet id should be > 0");
+ assertTrue(dropletUsingSlug.getEventId() > 0, "Droplet creation event id should be > 0");
+
+ waitForEvent(dropletUsingSlug.getEventId());
+ int powerOffEvent = api.getDropletApi().powerOff(dropletUsingSlug.getId());
+ waitForEvent(powerOffEvent);
+
+ int snapshotEvent = api.getDropletApi().snapshot(dropletUsingSlug.getId(), "imagetransfersnapshotusingslug");
+ waitForEvent(snapshotEvent);
+
+ snapshotUsingSlug = find(api.getImageApi().list(), byName("imagetransfersnapshotusingslug"));
+
+ Region newRegion = regions.get(1);
+ int transferEvent = api.getImageApi().transfer(snapshotUsingSlug.getId(), newRegion.getId());
+ assertTrue(transferEvent > 0, "Transfer event id should be > 0");
+ waitForEvent(transferEvent);
+ }
+
private static Predicate<Image> byName(final String name) {
return new Predicate<Image>() {
@Override
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
index 52bbaa1..1f61ede 100644
--- a/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
+++ b/digitalocean/src/test/java/org/jclouds/digitalocean/features/ImageApiMockTest.java
@@ -64,7 +64,7 @@ public class ImageApiMockTest extends BaseDigitalOceanMockTest {
public void testGetImage() throws Exception {
MockWebServer server = mockWebServer();
- String[] imageJsons = new String[] { "/image1.json", "/image2.json", "/image3.json" };
+ String[] imageJsons = new String[] { "/image1.json", "/image2.json", "/image3.json", "/image2.json" };
for (String imageJson : imageJsons) {
server.enqueue(new MockResponse().setBody(payloadFromResource(imageJson)));
@@ -84,6 +84,7 @@ public class ImageApiMockTest extends BaseDigitalOceanMockTest {
assertEquals(image.getOs().getArch(), "x32");
assertEquals(image.getName(), "Arch Linux 2013.05 x32");
assertTrue(image.isPublicImage());
+ assertEquals(image.getSlug(), "arch-linux-x32");
image = imageApi.get(2);
@@ -95,17 +96,32 @@ public class ImageApiMockTest extends BaseDigitalOceanMockTest {
assertEquals(image.getOs().getArch(), "x64");
assertEquals(image.getName(), "Fedora 17 x64 Desktop");
assertTrue(image.isPublicImage());
+ assertEquals(image.getSlug(), "fedora-17-x64");
image = imageApi.get(3);
assertRequestHasCommonFields(server.takeRequest(), "/images/3");
assertNotNull(image);
+ assertNull(image.getSlug());
assertEquals(image.getId(), 3);
assertEquals(image.getOs().getDistribution(), Distribution.UBUNTU);
assertEquals(image.getOs().getVersion(), "13.04");
assertEquals(image.getOs().getArch(), "");
assertEquals(image.getName(), "Dokku on Ubuntu 13.04 0.2.0rc3");
assertTrue(image.isPublicImage());
+ assertNull(image.getSlug());
+
+ image = imageApi.get("fedora-17-x64");
+
+ assertRequestHasCommonFields(server.takeRequest(), "/images/fedora-17-x64");
+ assertNotNull(image);
+ assertEquals(image.getId(), 2);
+ assertEquals(image.getOs().getDistribution(), Distribution.FEDORA);
+ assertEquals(image.getOs().getVersion(), "17");
+ assertEquals(image.getOs().getArch(), "x64");
+ assertEquals(image.getName(), "Fedora 17 x64 Desktop");
+ assertTrue(image.isPublicImage());
+ assertEquals(image.getSlug(), "fedora-17-x64");
} finally {
api.close();
server.shutdown();
@@ -147,6 +163,23 @@ public class ImageApiMockTest extends BaseDigitalOceanMockTest {
}
}
+ public void testDeleteImageUsingSlug() throws Exception {
+ MockWebServer server = mockWebServer();
+ server.enqueue(new MockResponse());
+
+ DigitalOceanApi api = api(server.getUrl("/"));
+ ImageApi imageApi = api.getImageApi();
+
+ try {
+ imageApi.delete("img-15");
+
+ assertRequestHasCommonFields(server.takeRequest(), "/images/img-15/destroy");
+ } finally {
+ api.close();
+ server.shutdown();
+ }
+ }
+
public void testDeleteUnexistingImage() throws Exception {
MockWebServer server = mockWebServer();
server.enqueue(new MockResponse().setResponseCode(404));
@@ -210,4 +243,23 @@ public class ImageApiMockTest extends BaseDigitalOceanMockTest {
server.shutdown();
}
}
+
+ public void testTransferImageUsingSlug() throws Exception {
+ MockWebServer server = mockWebServer();
+ server.enqueue(new MockResponse().setBody(payloadFromResource("/eventid.json")));
+
+ DigitalOceanApi api = api(server.getUrl("/"));
+ ImageApi imageApi = api.getImageApi();
+
+ try {
+ int eventId = imageApi.transfer("img-47", 23);
+
+ assertRequestHasParameters(server.takeRequest(), "/images/img-47/transfer",
+ ImmutableMultimap.of("region_id", "23"));
+ assertEquals(eventId, 7499);
+ } finally {
+ api.close();
+ server.shutdown();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/resources/image1.json
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/resources/image1.json b/digitalocean/src/test/resources/image1.json
index 879e375..97737d5 100644
--- a/digitalocean/src/test/resources/image1.json
+++ b/digitalocean/src/test/resources/image1.json
@@ -5,6 +5,6 @@
"name" : "Arch Linux 2013.05 x32",
"distribution" : "Arch Linux",
"public" : true,
- "slug" : null
+ "slug" : "arch-linux-x32"
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/resources/image2.json
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/resources/image2.json b/digitalocean/src/test/resources/image2.json
index ae680fe..2c5e16a 100644
--- a/digitalocean/src/test/resources/image2.json
+++ b/digitalocean/src/test/resources/image2.json
@@ -5,6 +5,6 @@
"name" : "Fedora 17 x64 Desktop",
"distribution" : "Fedora",
"public" : true,
- "slug" : null
+ "slug" : "fedora-17-x64"
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/resources/images.json
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/resources/images.json b/digitalocean/src/test/resources/images.json
index 44f081f..0eaef18 100644
--- a/digitalocean/src/test/resources/images.json
+++ b/digitalocean/src/test/resources/images.json
@@ -4,14 +4,14 @@
{
"id":1601,
"name":"CentOS 5.8 x64",
- "slug":null,
+ "slug":"centos-58-x64",
"distribution":"CentOS",
"public":true
},
{
"id":1602,
"name":"CentOS 5.8 x32",
- "slug":null,
+ "slug":"centos-58-x32",
"distribution":"CentOS",
"public":true
},
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/c5afe3cc/digitalocean/src/test/resources/sizes.json
----------------------------------------------------------------------
diff --git a/digitalocean/src/test/resources/sizes.json b/digitalocean/src/test/resources/sizes.json
index ef1dd94..5bb2a1f 100644
--- a/digitalocean/src/test/resources/sizes.json
+++ b/digitalocean/src/test/resources/sizes.json
@@ -4,7 +4,7 @@
{
"id":66,
"name":"512MB",
- "slug":null,
+ "slug":"512mb",
"memory":512,
"cpu":1,
"disk":20,
@@ -14,7 +14,7 @@
{
"id":63,
"name":"1GB",
- "slug":null,
+ "slug":"1gb",
"memory":1024,
"cpu":1,
"disk":30,
@@ -24,7 +24,7 @@
{
"id":62,
"name":"2GB",
- "slug":null,
+ "slug":"2gb",
"memory":2048,
"cpu":2,
"disk":40,
@@ -34,7 +34,7 @@
{
"id":64,
"name":"4GB",
- "slug":null,
+ "slug":"4gb",
"memory":4096,
"cpu":2,
"disk":60,