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:29 UTC
[05/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/main/java/org/jclouds/digitalocean2/compute/strategy/CreateKeyPairsThenCreateNodes.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/strategy/CreateKeyPairsThenCreateNodes.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/strategy/CreateKeyPairsThenCreateNodes.java
new file mode 100644
index 0000000..3e4aae3
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/strategy/CreateKeyPairsThenCreateNodes.java
@@ -0,0 +1,217 @@
+/*
+ * 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.compute.strategy;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.List;
+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.Constants;
+import org.jclouds.compute.config.CustomizationResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.digitalocean2.DigitalOcean2Api;
+import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions;
+import org.jclouds.digitalocean2.domain.Key;
+import org.jclouds.digitalocean2.ssh.DSAKeys;
+import org.jclouds.digitalocean2.ssh.ECDSAKeys;
+import org.jclouds.logging.Logger;
+import org.jclouds.ssh.SshKeyPairGenerator;
+import org.jclouds.ssh.SshKeys;
+
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+@Singleton
+public class CreateKeyPairsThenCreateNodes extends CreateNodesWithGroupEncodedIntoNameThenAddToSet {
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ private final DigitalOcean2Api api;
+ private final SshKeyPairGenerator keyGenerator;
+ private final Function<String, PublicKey> sshKeyToPublicKey;
+
+ @Inject
+ protected CreateKeyPairsThenCreateNodes(
+ CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
+ ListNodesStrategy listNodesStrategy,
+ GroupNamingConvention.Factory namingConvention,
+ @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+ CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+ DigitalOcean2Api api, SshKeyPairGenerator keyGenerator, Function<String, PublicKey> sshKeyToPublicKey) {
+ super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
+ customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+ this.api = checkNotNull(api, "api cannot be null");
+ this.keyGenerator = checkNotNull(keyGenerator, "keyGenerator cannot be null");
+ checkNotNull(userExecutor, "userExecutor cannot be null");
+ this.sshKeyToPublicKey = checkNotNull(sshKeyToPublicKey, "sshKeyToPublicKey cannot be null");
+ }
+
+ @Override
+ public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
+ Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
+ Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
+
+ DigitalOcean2TemplateOptions options = template.getOptions().as(DigitalOcean2TemplateOptions.class);
+ Set<Integer> generatedSshKeyIds = Sets.newHashSet();
+
+ // If no key has been configured and the auto-create option is set, then generate a key pair
+ if (options.getSshKeyIds().isEmpty() && options.getAutoCreateKeyPair()
+ && Strings.isNullOrEmpty(options.getPublicKey())) {
+ generateKeyPairAndAddKeyToSet(options, generatedSshKeyIds, group);
+ }
+
+ // If there is a script to run in the node, make sure a private key has been configured so jclouds will be able to
+ // access the node
+ if (options.getRunScript() != null) {
+ checkArgument(!Strings.isNullOrEmpty(options.getLoginPrivateKey()),
+ "no private key configured for: %s; please use options.overrideLoginPrivateKey(rsa_private_text)", group);
+ }
+
+ // If there is a key configured, then make sure there is a key pair for it
+ if (!Strings.isNullOrEmpty(options.getPublicKey())) {
+ createKeyPairForPublicKeyInOptionsAndAddToSet(options, generatedSshKeyIds);
+ }
+
+ // Set all keys (the provided and the auto-generated) in the options object so the
+ // DigitalOceanComputeServiceAdapter adds them all
+ options.sshKeyIds(Sets.union(generatedSshKeyIds, options.getSshKeyIds()));
+
+ Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
+ customizationResponses);
+
+ // Key pairs in DigitalOcean are only required to create the Droplets. They aren't used anymore so it is better
+ // to delete the auto-generated key pairs at this point where we know exactly which ones have been
+ // auto-generated by jclouds.
+ registerAutoGeneratedKeyPairCleanupCallbacks(responses, generatedSshKeyIds);
+
+ return responses;
+ }
+
+ private void createKeyPairForPublicKeyInOptionsAndAddToSet(DigitalOcean2TemplateOptions options,
+ Set<Integer> generatedSshKeyIds) {
+ logger.debug(">> checking if the key pair already exists...");
+
+ PublicKey userKey = sshKeyToPublicKey.apply(options.getPublicKey());
+ String userFingerprint = computeFingerprint(userKey);
+ Key key = api.keyApi().get(userFingerprint);
+
+ if (key == null) {
+ logger.debug(">> key pair not found. creating a new one...");
+
+ Key newKey = api.keyApi().create(userFingerprint, options.getPublicKey());
+
+ generatedSshKeyIds.add(newKey.id());
+ logger.debug(">> key pair created! %s", newKey);
+ } else {
+ logger.debug(">> key pair found! %s", key);
+ generatedSshKeyIds.add(key.id());
+ }
+ }
+
+ private void generateKeyPairAndAddKeyToSet(DigitalOcean2TemplateOptions options, Set<Integer> generatedSshKeyIds, String prefix) {
+ logger.debug(">> creating default keypair for node...");
+
+ Map<String, String> defaultKeys = keyGenerator.get();
+
+ Key defaultKey = api.keyApi().create(prefix + "-" + System.getProperty("user.name"), defaultKeys.get("public"));
+ generatedSshKeyIds.add(defaultKey.id());
+
+ logger.debug(">> keypair created! %s", defaultKey);
+
+ // If a private key has not been explicitly set, configure the auto-generated one
+ if (Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
+ options.overrideLoginPrivateKey(defaultKeys.get("private"));
+ }
+ }
+
+ private void registerAutoGeneratedKeyPairCleanupCallbacks(Map<?, ListenableFuture<Void>> responses,
+ final Set<Integer> generatedSshKeyIds) {
+ // The Futures.allAsList fails immediately if some of the futures fail. The Futures.successfulAsList, however,
+ // returns a list containing the results or 'null' for those futures that failed. We want to wait for all them
+ // (even if they fail), so better use the latter form.
+ ListenableFuture<List<Void>> aggregatedResponses = Futures.successfulAsList(responses.values());
+
+ // Key pairs must be cleaned up after all futures completed (even if some failed).
+ Futures.addCallback(aggregatedResponses, new FutureCallback<List<Void>>() {
+ @Override
+ public void onSuccess(List<Void> result) {
+ cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
+ }
+
+ private void cleanupAutoGeneratedKeyPairs(Set<Integer> generatedSshKeyIds) {
+ logger.debug(">> cleaning up auto-generated key pairs...");
+ for (Integer sshKeyId : generatedSshKeyIds) {
+ try {
+ api.keyApi().delete(sshKeyId);
+ } catch (Exception ex) {
+ logger.warn(">> could not delete key pair %s: %s", sshKeyId, ex.getMessage());
+ }
+ }
+ }
+
+ }, userExecutor);
+ }
+
+ private static String computeFingerprint(PublicKey key) {
+ if (key instanceof RSAPublicKey) {
+ RSAPublicKey rsaKey = (RSAPublicKey) key;
+ return SshKeys.fingerprint(rsaKey.getPublicExponent(), rsaKey.getModulus());
+ } else if (key instanceof DSAPublicKey) {
+ DSAPublicKey dsaKey = (DSAPublicKey) key;
+ return DSAKeys.fingerprint(dsaKey.getParams().getP(), dsaKey.getParams().getQ(), dsaKey.getParams().getG(),
+ dsaKey.getY());
+ } else if (key instanceof ECPublicKey) {
+ ECPublicKey ecdsaKey = (ECPublicKey) key;
+ return ECDSAKeys.fingerprint(ecdsaKey);
+ } else {
+ throw new IllegalArgumentException("Only RSA and DSA keys are supported");
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOcean2HttpApiModule.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOcean2HttpApiModule.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOcean2HttpApiModule.java
new file mode 100644
index 0000000..8bfe266
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOcean2HttpApiModule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.config;
+
+import java.net.URI;
+
+import org.jclouds.digitalocean2.DigitalOcean2Api;
+import org.jclouds.digitalocean2.domain.options.ImageListOptions;
+import org.jclouds.digitalocean2.domain.options.ListOptions;
+import org.jclouds.digitalocean2.functions.LinkToImageListOptions;
+import org.jclouds.digitalocean2.functions.LinkToListOptions;
+import org.jclouds.digitalocean2.handlers.DigitalOcean2ErrorHandler;
+import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.annotation.Redirection;
+import org.jclouds.http.annotation.ServerError;
+import org.jclouds.oauth.v2.config.OAuthScopes;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.config.HttpApiModule;
+
+import com.google.common.base.Function;
+import com.google.inject.TypeLiteral;
+
+@ConfiguresHttpApi
+public class DigitalOcean2HttpApiModule extends HttpApiModule<DigitalOcean2Api> {
+
+ @Override
+ protected void bindErrorHandlers() {
+ bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(DigitalOcean2ErrorHandler.class);
+ bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(DigitalOcean2ErrorHandler.class);
+ bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(DigitalOcean2ErrorHandler.class);
+ }
+
+ @Override
+ protected void configure() {
+ super.configure();
+ bind(OAuthScopes.class).toInstance(OAuthScopes.ReadOrWriteScopes.create("read", "read write"));
+ bind(new TypeLiteral<Function<URI, ListOptions>>() {
+ }).to(LinkToListOptions.class);
+ bind(new TypeLiteral<Function<URI, ImageListOptions>>() {
+ }).to(LinkToImageListOptions.class);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOceanParserModule.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOceanParserModule.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOceanParserModule.java
new file mode 100644
index 0000000..e4bb9bc
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/config/DigitalOceanParserModule.java
@@ -0,0 +1,144 @@
+/*
+ * 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.config;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.collect.Iterables.get;
+import static com.google.common.collect.Iterables.size;
+import static com.google.inject.Scopes.SINGLETON;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.digitalocean2.ssh.DSAKeys;
+import org.jclouds.digitalocean2.ssh.ECDSAKeys;
+import org.jclouds.json.config.GsonModule.DateAdapter;
+import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import org.jclouds.ssh.SshKeys;
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+
+/**
+ * Custom parser bindings.
+ */
+public class DigitalOceanParserModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(DateAdapter.class).to(Iso8601DateAdapter.class).in(SINGLETON);
+ }
+
+ @Singleton
+ public static class SshPublicKeyAdapter extends TypeAdapter<PublicKey> {
+
+ private final Function<PublicKey, String> publicKeyToSshKey;
+ private final Function<String, PublicKey> sshKeyToPublicKey;
+
+ @Inject
+ public SshPublicKeyAdapter(Function<PublicKey, String> publicKeyToSshKey,
+ Function<String, PublicKey> sshKeyToPublicKey) {
+ this.publicKeyToSshKey = checkNotNull(publicKeyToSshKey, "publicKeyToSshKey cannot be null");
+ this.sshKeyToPublicKey = checkNotNull(sshKeyToPublicKey, "sshKeyToPublicKey cannot be null");
+ }
+
+ @Override
+ public void write(JsonWriter out, PublicKey value) throws IOException {
+ out.value(publicKeyToSshKey.apply(value));
+ }
+
+ @Override
+ public PublicKey read(JsonReader in) throws IOException {
+ return sshKeyToPublicKey.apply(in.nextString().trim());
+ }
+ }
+
+ @Provides
+ @Singleton
+ public Function<PublicKey, String> publicKeyToSshKey() {
+ return new Function<PublicKey, String>() {
+ @Override
+ public String apply(PublicKey input) {
+ if (input instanceof RSAPublicKey) {
+ return SshKeys.encodeAsOpenSSH((RSAPublicKey) input);
+ } else if (input instanceof DSAPublicKey) {
+ return DSAKeys.encodeAsOpenSSH((DSAPublicKey) input);
+ } else {
+ throw new IllegalArgumentException("Only RSA and DSA keys are supported");
+ }
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ public Function<String, PublicKey> sshKeyToPublicKey() {
+ return new Function<String, PublicKey>() {
+ @Override
+ public PublicKey apply(String input) {
+ Iterable<String> parts = Splitter.on(' ').split(input);
+ checkArgument(size(parts) >= 2, "bad format, should be: [ssh-rsa|ssh-dss] AAAAB3...");
+ String type = get(parts, 0);
+
+ try {
+ if ("ssh-rsa".equals(type)) {
+ RSAPublicKeySpec spec = SshKeys.publicKeySpecFromOpenSSH(input);
+ return KeyFactory.getInstance("RSA").generatePublic(spec);
+ } else if ("ssh-dss".equals(type)) {
+ DSAPublicKeySpec spec = DSAKeys.publicKeySpecFromOpenSSH(input);
+ return KeyFactory.getInstance("DSA").generatePublic(spec);
+ } else if (type.startsWith("ecdsa-sha2-")) {
+ ECPublicKeySpec spec = ECDSAKeys.publicKeySpecFromOpenSSH(input);
+ return KeyFactory.getInstance("EC").generatePublic(spec);
+ } else {
+ throw new IllegalArgumentException("bad format, jclouds supports ssh-rsa, ssh-dss, ecdsa-sha2-nistp[256|384|521]");
+ }
+ } catch (InvalidKeySpecException ex) {
+ throw propagate(ex);
+ } catch (NoSuchAlgorithmException ex) {
+ throw propagate(ex);
+ }
+ }
+ };
+ }
+
+ @Provides
+ @Singleton
+ public Map<Type, Object> provideCustomAdapterBindings(SshPublicKeyAdapter sshPublicKeyAdapter) {
+ return ImmutableMap.<Type, Object> of(PublicKey.class, sshPublicKeyAdapter);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Action.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Action.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Action.java
new file mode 100644
index 0000000..b800105
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Action.java
@@ -0,0 +1,71 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Date;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+
+@AutoValue
+public abstract class Action {
+
+ public enum Status {
+ COMPLETED, IN_PROGRESS, ERRORED;
+
+ Status() {}
+
+ public static Status fromValue(String value) {
+ Optional<Status> status = Enums.getIfPresent(Status.class, value.toUpperCase());
+ if (!status.isPresent()) {
+ String upperCamelValue = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, value.toLowerCase());
+ status = Enums.getIfPresent(Status.class, upperCamelValue);
+ }
+ checkArgument(status.isPresent(), "Expected one of %s but was", Joiner.on(',').join(Status.values()), value);
+ return status.get();
+ }
+ }
+
+ public abstract int id();
+ public abstract Status status();
+ public abstract String type();
+ public abstract Date startedAt();
+ @Nullable public abstract Date completedAt();
+ public abstract Integer resourceId();
+ public abstract String resourceType();
+ @Nullable public abstract Region region();
+ @Nullable public abstract String regionSlug();
+
+ @SerializedNames({ "id", "status", "type", "started_at", "completed_at", "resource_id", "resource_type",
+ "region", "region_slug" })
+ public static Action create(int id, Status status, String type, Date startedAt, Date completedAt, int resourceId,
+ String resourceType, Region region, String regionSlug) {
+ return new AutoValue_Action(id, status, type, startedAt, completedAt, resourceId, resourceType, region,
+ regionSlug);
+ }
+
+ Action() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Backup.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Backup.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Backup.java
new file mode 100644
index 0000000..536a187
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Backup.java
@@ -0,0 +1,43 @@
+/*
+ * 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.domain;
+
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Backup {
+ public abstract int id();
+ public abstract String name();
+ public abstract String distribution();
+ @Nullable public abstract String slug();
+ public abstract boolean isPublic();
+ public abstract List<String> regions();
+ public abstract int minDiskSize();
+
+ @SerializedNames({ "id", "name", "distribution", "slug", "public", "regions", "min_disk_size" })
+ public static Backup create(int id, String name, String distribution, String slug, boolean isPublic,
+ List<String> regions, int minDiskSize) {
+ return new AutoValue_Backup(id, name, distribution, slug, isPublic, regions, minDiskSize);
+ }
+
+ Backup() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Distribution.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Distribution.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Distribution.java
new file mode 100644
index 0000000..1ea0f0e
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Distribution.java
@@ -0,0 +1,69 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.tryFind;
+import static java.util.Arrays.asList;
+
+import java.util.List;
+
+import org.jclouds.compute.domain.OsFamily;
+import com.google.common.base.Predicate;
+
+/**
+ * DigitalOcean image distributions.
+ */
+public enum Distribution {
+ ARCHLINUX(OsFamily.ARCH, "Arch Linux"),
+ CENTOS(OsFamily.CENTOS, "CentOS"),
+ DEBIAN(OsFamily.DEBIAN, "Debian"),
+ FEDORA(OsFamily.FEDORA, "Fedora"),
+ UBUNTU(OsFamily.UBUNTU, "Ubuntu"),
+ UNRECOGNIZED(OsFamily.UNRECOGNIZED, "");
+
+ private static final List<Distribution> values = asList(Distribution.values());
+
+ private final OsFamily osFamily;
+ private final String value;
+
+ private Distribution(OsFamily osFamily, String value) {
+ this.osFamily = checkNotNull(osFamily, "osFamily cannot be null");
+ this.value = checkNotNull(value, "value cannot be null");
+ }
+
+ public OsFamily osFamily() {
+ return this.osFamily;
+ }
+
+ public String value() {
+ return this.value;
+ }
+
+ public static Distribution fromValue(String value) {
+ return tryFind(values, hasValue(value)).or(UNRECOGNIZED);
+ }
+
+ private static Predicate<Distribution> hasValue(final String value) {
+ return new Predicate<Distribution>() {
+ @Override
+ public boolean apply(Distribution input) {
+ return input.value.equalsIgnoreCase(value);
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Droplet.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Droplet.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Droplet.java
new file mode 100644
index 0000000..4d0dd4c
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Droplet.java
@@ -0,0 +1,92 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.copyOf;
+import static com.google.common.collect.Iterables.concat;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class Droplet {
+
+ public enum Status {
+ NEW, ACTIVE, ARCHIVE, OFF;
+
+ public static Status fromValue(String value) {
+ Optional<Status> status = Enums.getIfPresent(Status.class, value.toUpperCase());
+ checkArgument(status.isPresent(), "Expected one of %s but was %s", Joiner.on(',').join(Status.values()), value);
+ return status.get();
+ }
+ }
+
+ public abstract int id();
+ public abstract String name();
+ public abstract int memory();
+ public abstract int vcpus();
+ public abstract int disk();
+ public abstract boolean locked();
+ public abstract Date createdAt();
+ public abstract Status status();
+ public abstract List<Integer> backupsIds();
+ public abstract List<Integer> snapshotIds();
+ public abstract List<String> features();
+ @Nullable public abstract Region region();
+ @Nullable public abstract Image image();
+ @Nullable public abstract Size size();
+ public abstract String sizeSlug();
+ @Nullable public abstract Networks networks();
+ @Nullable public abstract Kernel kernel();
+
+ @SerializedNames({ "id", "name", "memory", "vcpus", "disk", "locked", "created_at", "status", "backup_ids",
+ "snapshot_ids", "features", "region", "image", "size", "size_slug", "networks", "kernel" })
+ public static Droplet create(int id, String name, int memory, int vcpus, int disk, boolean locked, Date createdAt,
+ Status status, List<Integer> backupIds, List<Integer> snapshotIds, List<String> features, Region region,
+ Image image, Size size, String sizeSlug, Networks network, Kernel kernel) {
+ return new AutoValue_Droplet(id, name, memory, vcpus, disk, locked, createdAt, status,
+ backupIds == null ? ImmutableList.<Integer> of() : copyOf(backupIds),
+ snapshotIds == null ? ImmutableList.<Integer> of() : copyOf(snapshotIds), copyOf(features), region, image,
+ size, sizeSlug, network, kernel);
+ }
+
+ public Set<Networks.Address> getPublicAddresses() {
+ return FluentIterable.from(concat(networks().ipv4(), networks().ipv6()))
+ .filter(Networks.Predicates.publicNetworks())
+ .toSet();
+ }
+
+ public Set<Networks.Address> getPrivateAddresses() {
+ return FluentIterable.from(concat(networks().ipv4(), networks().ipv6()))
+ .filter(Networks.Predicates.privateNetworks())
+ .toSet();
+ }
+
+ Droplet() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/DropletCreate.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/DropletCreate.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/DropletCreate.java
new file mode 100644
index 0000000..06ed12b
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/DropletCreate.java
@@ -0,0 +1,66 @@
+/*
+ * 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.domain;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+
+import java.net.URI;
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class DropletCreate {
+ public abstract Droplet droplet();
+ public abstract Links links();
+
+ @AutoValue
+ public abstract static class Links {
+
+ @AutoValue
+ public abstract static class ActionLink {
+ public abstract int id();
+ public abstract String rel();
+ public abstract URI href();
+
+ @SerializedNames({"id", "rel", "href"})
+ public static ActionLink create(int id, String rel, URI href) {
+ return new AutoValue_DropletCreate_Links_ActionLink(id, rel, href);
+ }
+
+ ActionLink() {}
+ }
+
+ public abstract List<ActionLink> actions();
+
+ @SerializedNames({ "actions" })
+ public static Links create(List<ActionLink> actions) {
+ return new AutoValue_DropletCreate_Links(copyOf(actions));
+ }
+
+ Links() {}
+ }
+
+ @SerializedNames({ "droplet", "links" })
+ public static DropletCreate create(Droplet droplet, Links links) {
+ return new AutoValue_DropletCreate(droplet, links);
+ }
+
+ DropletCreate() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Image.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Image.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Image.java
new file mode 100644
index 0000000..dd2c3b6
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Image.java
@@ -0,0 +1,48 @@
+/*
+ * 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.domain;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+
+import java.util.Date;
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Image {
+
+ public abstract int id();
+ public abstract String name();
+ public abstract String type();
+ public abstract String distribution();
+ @Nullable public abstract String slug();
+ public abstract boolean isPublic();
+ public abstract List<String> regions();
+ public abstract Date createdAt();
+
+ @SerializedNames({ "id", "name", "type", "distribution", "slug", "public", "regions", "created_at" })
+ public static Image create(int id, String name, String type, String distribution, String slug, boolean isPublic,
+ List<String> regions, Date createdAt) {
+ return new AutoValue_Image(id, name, type, distribution, slug, isPublic, copyOf(regions), createdAt);
+ }
+
+ Image() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Kernel.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Kernel.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Kernel.java
new file mode 100644
index 0000000..7eb5467
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Kernel.java
@@ -0,0 +1,35 @@
+/*
+ * 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.domain;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Kernel {
+ public abstract int id();
+ public abstract String name();
+ public abstract String version();
+
+ @SerializedNames({ "id", "name", "version" })
+ public static Kernel create(int id, String name, String version) {
+ return new AutoValue_Kernel(id, name, version);
+ }
+
+ Kernel() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Key.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Key.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Key.java
new file mode 100644
index 0000000..c1a7ae3
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Key.java
@@ -0,0 +1,39 @@
+/*
+ * 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.domain;
+
+import java.security.PublicKey;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Key {
+
+ public abstract int id();
+ public abstract String name();
+ public abstract String fingerprint();
+ public abstract PublicKey publicKey();
+
+ @SerializedNames({ "id", "name", "fingerprint", "public_key" })
+ public static Key create(int id, String name, String fingerprint, PublicKey publicKey) {
+ return new AutoValue_Key(id, name, fingerprint, publicKey);
+ }
+
+ Key() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Networks.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Networks.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Networks.java
new file mode 100644
index 0000000..cd3dbd4
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Networks.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.domain;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Predicate;
+
+@AutoValue
+public abstract class Networks {
+
+ @AutoValue
+ public abstract static class Address {
+ public abstract String ip();
+ public abstract String netmask();
+ public abstract String gateway();
+ public abstract String type();
+
+ @SerializedNames({ "ip_address", "netmask", "gateway", "type"})
+ public static Address create(String ip, String netmask, String gateway, String type) {
+ return new AutoValue_Networks_Address(ip, netmask, gateway, type);
+ }
+
+ Address() {}
+ }
+
+ public abstract List<Address> ipv4();
+ public abstract List<Address> ipv6();
+
+ @SerializedNames({ "v4", "v6" })
+ public static Networks create(List<Address> ipv4, List<Address> ipv6) {
+ return new AutoValue_Networks(copyOf(ipv4), copyOf(ipv6));
+ }
+
+ Networks() {}
+
+ public static class Predicates {
+
+ public static Predicate<Address> publicNetworks() {
+ return new Predicate<Address>() {
+ @Override
+ public boolean apply(Address network) {
+ return network.type().equals("public");
+ }
+ };
+ }
+
+ public static Predicate<Address> privateNetworks() {
+ return new Predicate<Address>() {
+ @Override
+ public boolean apply(Address network) {
+ return network.type().equals("private");
+ }
+ };
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/OperatingSystem.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/OperatingSystem.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/OperatingSystem.java
new file mode 100644
index 0000000..041ea20
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/OperatingSystem.java
@@ -0,0 +1,60 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Strings.nullToEmpty;
+import static java.util.regex.Pattern.compile;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * The operating system of an image.
+ * <p>
+ * This class parses the <code>name</code> string (e.g. "Ubuntu 12.10 x64") of the images and properly sets each field
+ * to the right value.
+ */
+@AutoValue
+public abstract class OperatingSystem {
+
+ // Parse something like "12.10 x64" or "Ubuntu 12.10.1 x64" and matches the version and architecture
+ private static final Pattern VERSION_PATTERN = compile("(?:[a-zA-Z\\s]*\\s+)?(\\d+(?:\\.?\\d+)*)?(?:\\s*(x\\d{2}))?.*");
+ private static final String IS_64_BIT = "x64";
+
+ public abstract Distribution distribution();
+ public abstract String version();
+ public abstract String arch();
+
+ public static OperatingSystem create(String name, String distribution) {
+ return new AutoValue_OperatingSystem(Distribution.fromValue(distribution), match(VERSION_PATTERN, name, 1),
+ match(VERSION_PATTERN, name, 2));
+ }
+
+ public boolean is64bit() {
+ return IS_64_BIT.equals(arch());
+ }
+
+ OperatingSystem() {}
+
+ private static String match(final Pattern pattern, final String input, int group) {
+ Matcher m = pattern.matcher(input);
+ return m.find() ? nullToEmpty(m.group(group)) : "";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Region.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Region.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Region.java
new file mode 100644
index 0000000..2b3441c
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Region.java
@@ -0,0 +1,39 @@
+/*
+ * 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.domain;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Region {
+ public abstract String slug();
+ public abstract String name();
+ public abstract List<String> sizes();
+ public abstract boolean available();
+ public abstract List<String> features();
+
+ @SerializedNames({ "slug", "name", "sizes", "available", "features" })
+ public static Region create(String slug, String name, List<String> sizes, boolean available, List<String> features) {
+ return new AutoValue_Region(slug, name, sizes, available, features);
+ }
+
+ Region() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Size.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Size.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Size.java
new file mode 100644
index 0000000..03d9492
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Size.java
@@ -0,0 +1,46 @@
+/*
+ * 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.domain;
+
+import java.util.List;
+
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Size {
+ public abstract String slug();
+ public abstract boolean available();
+ public abstract float transfer();
+ public abstract float priceMonthly();
+ public abstract float priceHourly();
+ public abstract int memory();
+ public abstract int vcpus();
+ public abstract int disk();
+ public abstract List<String> regions();
+
+ @SerializedNames({ "slug", "available", "transfer", "price_monthly", "price_hourly", "memory", "vcpus", "disk",
+ "regions" })
+ public static Size create(String slug, boolean available, float transfer, float priceMonthly, float priceHourly,
+ int memory, int vcpus, int disk, List<String> regions) {
+ return new AutoValue_Size(slug, available, transfer, priceMonthly, priceHourly, memory, vcpus, disk, regions);
+ }
+
+ Size() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Snapshot.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Snapshot.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Snapshot.java
new file mode 100644
index 0000000..12daaa2
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/Snapshot.java
@@ -0,0 +1,47 @@
+/*
+ * 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.domain;
+
+import java.util.Date;
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+
+@AutoValue
+public abstract class Snapshot {
+ public abstract int id();
+ public abstract String name();
+ public abstract String type();
+ public abstract String distribution();
+ @Nullable public abstract String slug();
+ public abstract boolean isPublic();
+ public abstract List<String> regions();
+ public abstract int minDiskSize();
+ public abstract Date createdAt();
+
+ @SerializedNames({ "id", "name", "type", "distribution", "slug", "public", "regions", "min_disk_size", "created_at"})
+ public static Snapshot create(int id, String name, String type, String distribution, String slug, boolean isPublic,
+ List<String> regions, int minDiskSize, Date createdAt) {
+ return new AutoValue_Snapshot(id, name, type, distribution, slug, isPublic, regions, minDiskSize, createdAt);
+ }
+
+ Snapshot() {}
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/internal/PaginatedCollection.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/internal/PaginatedCollection.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/internal/PaginatedCollection.java
new file mode 100644
index 0000000..8ca7b21
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/internal/PaginatedCollection.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.digitalocean2.domain.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.net.URI;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Base class for all collections that return paginated results.
+ */
+public abstract class PaginatedCollection<T> extends IterableWithMarker<T> {
+
+ @AutoValue
+ public abstract static class Meta {
+ public abstract long total();
+
+ @SerializedNames({ "total" })
+ public static Meta create(long total) {
+ return new AutoValue_PaginatedCollection_Meta(total);
+ }
+
+ Meta() { }
+ }
+
+ @AutoValue
+ public abstract static class Links {
+
+ @AutoValue
+ public abstract static class Pages {
+ @Nullable public abstract URI first();
+ @Nullable public abstract URI prev();
+ @Nullable public abstract URI next();
+ @Nullable public abstract URI last();
+
+ @SerializedNames({ "first", "prev", "next", "last" })
+ public static Pages create(URI first, URI next, URI prev, URI last) {
+ return new AutoValue_PaginatedCollection_Links_Pages(first, next, prev, last);
+ }
+
+ Pages() { }
+ }
+
+ @Nullable public abstract Pages pages();
+
+ @SerializedNames({ "pages" })
+ public static Links create(Pages pages) {
+ return new AutoValue_PaginatedCollection_Links(pages);
+ }
+
+ Links() { }
+ }
+
+ private final List<T> items;
+ private final Meta meta;
+ private final Links links;
+
+ protected PaginatedCollection(List<T> items, Meta meta, Links links) {
+ this.items = ImmutableList.copyOf(checkNotNull(items, "items cannot be null"));
+ this.meta = checkNotNull(meta, "meta cannot be null");
+ this.links = checkNotNull(links, "links cannot be null");
+ }
+
+ public List<T> items() {
+ return items;
+ }
+
+ public Meta meta() {
+ return meta;
+ }
+
+ public Links links() {
+ return links;
+ }
+
+ @Override public Iterator<T> iterator() {
+ return items.iterator();
+ }
+
+ @Override public Optional<Object> nextMarker() {
+ if (links.pages() == null) {
+ return Optional.absent();
+ }
+ return Optional.fromNullable((Object) links.pages().next());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/CreateDropletOptions.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/CreateDropletOptions.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/CreateDropletOptions.java
new file mode 100644
index 0000000..b20fc96
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/CreateDropletOptions.java
@@ -0,0 +1,155 @@
+/*
+ * 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.domain.options;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+import org.jclouds.rest.MapBinder;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * Options to customize droplet creation.
+ */
+public class CreateDropletOptions implements MapBinder {
+
+ @Inject private BindToJsonPayload jsonBinder;
+
+ private final Set<Integer> sshKeys;
+ private final boolean backupsEnabled;
+ private final boolean ipv6Enabled;
+ private final boolean privateNetworking;
+ private final String userData;
+
+ private CreateDropletOptions(Set<Integer> sshKeys, boolean backupsEnabled, boolean ipv6Enabled,
+ boolean privateNetworking, @Nullable String userData) {
+ this.sshKeys = sshKeys;
+ this.backupsEnabled = backupsEnabled;
+ this.ipv6Enabled = ipv6Enabled;
+ this.privateNetworking = privateNetworking;
+ this.userData = userData;
+ }
+
+ @AutoValue
+ abstract static class DropletRequest {
+ abstract String name();
+ abstract String region();
+ abstract String size();
+ abstract String image();
+ abstract Set<Integer> sshKeys();
+ abstract Boolean backups();
+ abstract Boolean ipv6();
+ abstract Boolean privateNetworking();
+ @Nullable abstract String userData();
+
+ @SerializedNames({"name", "region", "size", "image", "ssh_keys", "backups", "ipv6", "private_networking", "user_data"})
+ static DropletRequest create(String name, String region, String size, String image, Set<Integer> sshKeys,
+ Boolean backups, Boolean ipv6, Boolean privateNetworking, String userData) {
+ return new AutoValue_CreateDropletOptions_DropletRequest(name, region, size, image, sshKeys, backups, ipv6,
+ privateNetworking, userData);
+ }
+
+ DropletRequest() {}
+ }
+
+ @Override
+ public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) {
+ DropletRequest droplet = DropletRequest.create(checkNotNull(postParams.get("name"), "name parameter not present").toString(),
+ checkNotNull(postParams.get("region"), "region parameter not present").toString(),
+ checkNotNull(postParams.get("size"), "size parameter not present").toString(),
+ checkNotNull(postParams.get("image"), "image parameter not present").toString(),
+ sshKeys, backupsEnabled, ipv6Enabled, privateNetworking, userData);
+
+ return bindToRequest(request, droplet);
+ }
+
+ @Override
+ public <R extends HttpRequest> R bindToRequest(R request, Object input) {
+ return jsonBinder.bindToRequest(request, input);
+ }
+
+ public Set<Integer> getSshKeys() {
+ return sshKeys;
+ }
+
+ public Boolean getPrivateNetworking() {
+ return privateNetworking;
+ }
+
+ public Boolean getBackupsEnabled() {
+ return backupsEnabled;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private ImmutableSet.Builder<Integer> sshKeyIds = ImmutableSet.builder();
+ private boolean backupsEnabled;
+ private boolean ipv6Enabled;
+ private boolean privateNetworking;
+ private String userData;
+
+ /**
+ * Adds a set of ssh key ids to be added to the droplet.
+ */
+ public Builder addSshKeyIds(Iterable<Integer> sshKeyIds) {
+ this.sshKeyIds.addAll(sshKeyIds);
+ return this;
+ }
+
+ /**
+ * Adds an ssh key id to be added to the droplet.
+ */
+ public Builder addSshKeyId(int sshKeyId) {
+ this.sshKeyIds.add(sshKeyId);
+ return this;
+ }
+
+ /**
+ * Enables a private network interface if the region supports private
+ * networking.
+ */
+ public Builder privateNetworking(boolean privateNetworking) {
+ this.privateNetworking = privateNetworking;
+ return this;
+ }
+
+ /**
+ * Enabled backups for the droplet.
+ */
+ public Builder backupsEnabled(boolean backupsEnabled) {
+ this.backupsEnabled = backupsEnabled;
+ return this;
+ }
+
+ public CreateDropletOptions build() {
+ return new CreateDropletOptions(sshKeyIds.build(), backupsEnabled, ipv6Enabled, privateNetworking, userData);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ImageListOptions.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ImageListOptions.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ImageListOptions.java
new file mode 100644
index 0000000..9f6415d
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ImageListOptions.java
@@ -0,0 +1,74 @@
+/*
+ * 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.domain.options;
+
+/**
+ * Custom options to filter the list of images.
+ */
+public class ImageListOptions extends ListOptions {
+ public static final String TYPE_PARAM = "type";
+ public static final String PRIVATE_PARAM = "private";
+
+ /**
+ * Configures the type of the images to be retrieved.
+ */
+ public ImageListOptions type(String type) {
+ queryParameters.put(TYPE_PARAM, type);
+ return this;
+ }
+
+ /**
+ * Get the images of the current user.
+ */
+ public ImageListOptions privateImages(boolean privateImages) {
+ queryParameters.put(PRIVATE_PARAM, String.valueOf(privateImages));
+ return this;
+ }
+
+ @Override public ImageListOptions perPage(int perPage) {
+ super.perPage(perPage);
+ return this;
+ }
+
+ @Override public ImageListOptions page(int page) {
+ super.page(page);
+ return this;
+ }
+
+ public static final class Builder {
+
+ /**
+ * @see {@link ImageListOptions#type(String)}
+ */
+ public static ImageListOptions type(String type) {
+ return new ImageListOptions().type(type);
+ }
+
+ /**
+ * @see {@link ImageListOptions#privateImages(boolean)}
+ */
+ public static ImageListOptions privateImages(boolean privateImages) {
+ return new ImageListOptions().privateImages(privateImages);
+ }
+ /**
+ * @see {@link ImageListOptions#page(int)}
+ */
+ public static ImageListOptions page(int page) {
+ return new ImageListOptions().page(page);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ListOptions.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ListOptions.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ListOptions.java
new file mode 100644
index 0000000..f859c1c
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/domain/options/ListOptions.java
@@ -0,0 +1,60 @@
+/*
+ * 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.domain.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+/**
+ * Options to customize how paginated lists are returned.
+ */
+public class ListOptions extends BaseHttpRequestOptions {
+ public static final String PAGE_PARAM = "page";
+ public static final String PER_PAGE_PARAM = "per_page";
+
+ /**
+ * Configures the number of entries to return in each page.
+ */
+ public ListOptions perPage(int perPage) {
+ queryParameters.put(PER_PAGE_PARAM, String.valueOf(perPage));
+ return this;
+ }
+
+ /**
+ * Configures the number of the page to be returned.
+ */
+ public ListOptions page(int page) {
+ queryParameters.put(PAGE_PARAM, String.valueOf(page));
+ return this;
+ }
+
+ public static final class Builder {
+
+ /**
+ * @see {@link ListOptions#perPage(int)}
+ */
+ public static ListOptions perPage(int perPage) {
+ return new ListOptions().perPage(perPage);
+ }
+
+ /**
+ * @see {@link ListOptions#page(int)}
+ */
+ public static ListOptions page(int page) {
+ return new ListOptions().page(page);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/057be8df/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/features/ActionApi.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/features/ActionApi.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/features/ActionApi.java
new file mode 100644
index 0000000..4a7a8bd
--- /dev/null
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/features/ActionApi.java
@@ -0,0 +1,113 @@
+/*
+ * 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 java.beans.ConstructorProperties;
+import java.io.Closeable;
+import java.net.URI;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404;
+import org.jclouds.Fallbacks.EmptyPagedIterableOnNotFoundOr404;
+import org.jclouds.Fallbacks.NullOnNotFoundOr404;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.digitalocean2.DigitalOcean2Api;
+import org.jclouds.digitalocean2.domain.Action;
+import org.jclouds.digitalocean2.domain.internal.PaginatedCollection;
+import org.jclouds.digitalocean2.domain.options.ListOptions;
+import org.jclouds.digitalocean2.functions.BaseToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.Json;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.Transform;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Provides access to Actions via the REST API.
+ *
+ * @see <a href="https://developers.digitalocean.com/v2/#actions"/>
+ * @see ActionApi
+ */
+@Path("/actions")
+@RequestFilters(OAuthFilter.class)
+@Consumes(MediaType.APPLICATION_JSON)
+public interface ActionApi extends Closeable {
+
+ @Named("action:list")
+ @GET
+ @ResponseParser(ParseActions.class)
+ @Transform(ParseActions.ToPagedIterable.class)
+ @Fallback(EmptyPagedIterableOnNotFoundOr404.class)
+ PagedIterable<Action> list();
+
+ @Named("action:list")
+ @GET
+ @ResponseParser(ParseActions.class)
+ @Fallback(EmptyIterableWithMarkerOnNotFoundOr404.class)
+ IterableWithMarker<Action> list(ListOptions options);
+
+ static final class ParseActions extends ParseJson<ParseActions.Actions> {
+ @Inject ParseActions(Json json) {
+ super(json, TypeLiteral.get(Actions.class));
+ }
+
+ private static class Actions extends PaginatedCollection<Action> {
+ @ConstructorProperties({ "actions", "meta", "links" })
+ public Actions(List<Action> items, Meta meta, Links links) {
+ super(items, meta, links);
+ }
+ }
+
+ private static class ToPagedIterable extends BaseToPagedIterable<Action, ListOptions> {
+ @Inject ToPagedIterable(DigitalOcean2Api api, Function<URI, ListOptions> linkToOptions) {
+ super(api, linkToOptions);
+ }
+
+ @Override
+ protected IterableWithMarker<Action> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
+ return api.actionApi().list(options);
+ }
+ }
+ }
+
+ @Named("action:get")
+ @GET
+ @SelectJson("action")
+ @Path("/{id}")
+ @Fallback(NullOnNotFoundOr404.class)
+ @Nullable
+ Action get(@PathParam("id") int id);
+
+}
+