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); + +} +
