diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceAdapter.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceAdapter.java index f520f457f0..17825513b3 100644 --- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceAdapter.java +++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceAdapter.java @@ -26,7 +26,6 @@ import static com.google.common.collect.Sets.newHashSet; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED; -import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue; import java.util.List; import java.util.Map; @@ -83,6 +82,17 @@ public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter this.json = json; } + private void setUserDataIfSupported(Template template, CreateDropletOptions.Builder options, String userData) { + @SuppressWarnings("unchecked") + List regionFeatures = (List) template.getLocation().getMetadata().get("features"); + if (regionFeatures.contains("metadata")) { + options.userData(userData); + } else { + logger.debug(">> region %s does not support metadata, ignoring provided user data", template.getLocation() + .getId()); + } + } + @Override public NodeAndInitialCredentials createNodeWithGroupEncodedIntoName(String group, final String name, Template template) { @@ -96,16 +106,15 @@ public class DigitalOcean2ComputeServiceAdapter implements ComputeServiceAdapter options.addSshKeyIds(templateOptions.getSshKeyIds()); } - Map metadataAndTags = metadataAndTagsAsCommaDelimitedValue(templateOptions); - if (!metadataAndTags.isEmpty()) { - @SuppressWarnings("unchecked") - List regionFeatures = (List) template.getLocation().getMetadata().get("features"); - if (regionFeatures.contains("metadata")) { - options.userData(json.toJson(metadataAndTags)); - } else { - logger.debug(">> region %s does not support metadata, ignoring provided user data", template.getLocation() - .getId()); - } + // In DigitalOcean, user_data is a SINGLE string, NOT a map! + // Encoding tags or anything else than user_data in here breaks their functionality. + if (null != templateOptions.getUserData()) { + setUserDataIfSupported(template, options, new String(templateOptions.getUserData())); + } + // Backwards compatible variant, getting userData from userMetaData map. + Map metadataAndTags = templateOptions.getUserMetadata(); + if (null != metadataAndTags.get("user_data")) { + setUserDataIfSupported(template, options, metadataAndTags.get("user_data")); } DropletCreate dropletCreated = api.dropletApi().create(name, diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptions.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptions.java index cafcdb1a16..bc18a590d8 100644 --- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptions.java +++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptions.java @@ -19,6 +19,7 @@ package org.jclouds.digitalocean2.compute.options; import static com.google.common.base.Objects.equal; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Arrays; import java.util.Set; import org.jclouds.compute.options.TemplateOptions; @@ -35,6 +36,7 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo private boolean privateNetworking = false; private boolean backupsEnabled = false; private boolean autoCreateKeyPair = true; + private byte[] userData; /** * Enables a private network interface if the region supports private networking. @@ -68,6 +70,14 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo return this; } + /** + * Sets the userData member. + */ + public DigitalOcean2TemplateOptions userData(byte[] userData) { + this.userData = userData; + return this; + } + public Set getSshKeyIds() { return sshKeyIds; } @@ -84,6 +94,10 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo return autoCreateKeyPair; } + public byte[] getUserData() { + return userData; + } + @Override public DigitalOcean2TemplateOptions clone() { DigitalOcean2TemplateOptions options = new DigitalOcean2TemplateOptions(); @@ -100,12 +114,16 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo eTo.backupsEnabled(backupsEnabled); eTo.autoCreateKeyPair(autoCreateKeyPair); eTo.sshKeyIds(sshKeyIds); + if (null != getUserData()) { + eTo.userData(getUserData()); + } } } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), backupsEnabled, privateNetworking, autoCreateKeyPair, sshKeyIds); + return Objects.hashCode(super.hashCode(), backupsEnabled, privateNetworking, autoCreateKeyPair, sshKeyIds, + Arrays.hashCode(userData)); } @Override @@ -122,7 +140,8 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo DigitalOcean2TemplateOptions other = (DigitalOcean2TemplateOptions) obj; return super.equals(other) && equal(this.backupsEnabled, other.backupsEnabled) && equal(this.privateNetworking, other.privateNetworking) - && equal(this.autoCreateKeyPair, other.autoCreateKeyPair) && equal(this.sshKeyIds, other.sshKeyIds); + && equal(this.autoCreateKeyPair, other.autoCreateKeyPair) && equal(this.sshKeyIds, other.sshKeyIds) + && Arrays.equals(this.userData, other.userData); } @Override @@ -134,6 +153,7 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo toString.add("sshKeyIds", sshKeyIds); } toString.add("autoCreateKeyPair", autoCreateKeyPair); + toString.add("userData", userData); return toString; } @@ -170,5 +190,13 @@ public class DigitalOcean2TemplateOptions extends TemplateOptions implements Clo DigitalOcean2TemplateOptions options = new DigitalOcean2TemplateOptions(); return options.autoCreateKeyPair(autoCreateKeyPair); } + + /** + * @see DigitalOcean2TemplateOptions#userData + */ + public static DigitalOcean2TemplateOptions userData(byte[] userData) { + DigitalOcean2TemplateOptions options = new DigitalOcean2TemplateOptions(); + return options.userData(userData); + } } } diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceLiveTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceLiveTest.java index f45a73f282..139ad523e6 100644 --- a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceLiveTest.java +++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/DigitalOcean2ComputeServiceLiveTest.java @@ -55,12 +55,12 @@ public class DigitalOcean2ComputeServiceLiveTest extends BaseComputeServiceLiveT @Override protected void checkTagsInNodeEquals(NodeMetadata node, ImmutableSet tags) { - // We encode the tags in the user data but the DigitalOcean API does not return it + // We do not support tags yet. } @Override protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap userMetadata) { - // The DigitalOcean API does not return the user data + // The DigitalOcean API does not support user metadata. } } diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptionsTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptionsTest.java index 982224c894..e58161b7ca 100644 --- a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptionsTest.java +++ b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/options/DigitalOcean2TemplateOptionsTest.java @@ -43,10 +43,17 @@ public class DigitalOcean2TemplateOptionsTest { TemplateOptions options = new DigitalOcean2TemplateOptions().backupsEnabled(true); assertEquals(options.as(DigitalOcean2TemplateOptions.class).getBackupsEnabled(), true); } - + @Test public void testAutoCreateKeyPair() { TemplateOptions options = new DigitalOcean2TemplateOptions().autoCreateKeyPair(false); assertEquals(options.as(DigitalOcean2TemplateOptions.class).getAutoCreateKeyPair(), false); } + + @Test + public void testUserData() { + byte[] userData = "Lorem ipsum".getBytes(); + TemplateOptions options = new DigitalOcean2TemplateOptions().userData(userData); + assertEquals(options.as(DigitalOcean2TemplateOptions.class).getUserData(), userData); + } }