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/06/17 20:44:06 UTC

[1/3] jclouds git commit: Properly set the permissions in the ~/.ssh folder

Repository: jclouds
Updated Branches:
  refs/heads/master 04c2394a1 -> bde70457d


Properly set the permissions in the ~/.ssh folder

When writing the ~/.ssh/authorized_keys file in a script that is being
run as sudo, the file is created with the root owner, instead of the
user defined by the node credentials. File ownership should be enforced
to make sure the right owner is alwaays set.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/179ed3b2
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/179ed3b2
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/179ed3b2

Branch: refs/heads/master
Commit: 179ed3b2f6688ca51e579ac3451945b2d015b279
Parents: 04c2394
Author: Ignasi Barrera <na...@apache.org>
Authored: Thu Jun 16 23:32:07 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jun 17 22:16:19 2016 +0200

----------------------------------------------------------------------
 .../config/BaseComputeServiceContextModule.java |  3 -
 .../functions/InstallKeysAndRunScript.java      | 59 +++++++++++++++
 .../NodeAndTemplateOptionsToStatement.java      | 40 +++++++++++
 ...plateOptionsToStatementWithoutPublicKey.java | 60 ++++++++++++++++
 .../functions/TemplateOptionsToStatement.java   | 55 --------------
 ...AndAddToGoodMapOrPutExceptionIntoBadMap.java | 14 ++--
 ...eOptionsToStatementWithoutPublicKeyTest.java | 74 +++++++++++++++++++
 ...ddToGoodMapOrPutExceptionIntoBadMapTest.java | 11 +--
 .../src/test/resources/initscript_with_java.sh  |  1 +
 .../src/test/resources/initscript_with_jetty.sh |  1 +
 .../src/test/resources/runscript_adminUpdate.sh |  1 +
 ...igitalOcean2ComputeServiceContextModule.java |  6 +-
 ...plateOptionsToStatementWithoutPublicKey.java | 59 ---------------
 ...eOptionsToStatementWithoutPublicKeyTest.java | 75 --------------------
 .../scriptbuilder/statements/login/UserAdd.java |  2 +-
 .../statements/ssh/AuthorizeRSAPublicKeys.java  | 16 ++++-
 .../statements/login/UserAddTest.java           |  2 +-
 .../ssh/AuthorizeRSAPublicKeyTest.java          | 28 ++++++--
 .../test/resources/test_adminaccess_flipped.sh  |  1 +
 .../test/resources/test_adminaccess_params.sh   |  1 +
 .../test_adminaccess_params_and_fullname.sh     |  1 +
 .../resources/test_adminaccess_plainuser.sh     |  1 +
 .../test/resources/test_adminaccess_standard.sh |  1 +
 23 files changed, 294 insertions(+), 218 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
index 4acaef9..8d9aa0e 100644
--- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
+++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java
@@ -47,7 +47,6 @@ import org.jclouds.compute.extensions.ImageExtension;
 import org.jclouds.compute.extensions.SecurityGroupExtension;
 import org.jclouds.compute.functions.CreateSshClientOncePortIsListeningOnNode;
 import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials;
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
 import org.jclouds.compute.options.RunScriptOptions;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.reference.ComputeServiceConstants;
@@ -89,8 +88,6 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
       install(new ComputeServiceTimeoutsModule());
       bind(new TypeLiteral<Function<NodeMetadata, SshClient>>() {
       }).to(CreateSshClientOncePortIsListeningOnNode.class);
-      bind(new TypeLiteral<Function<TemplateOptions, Statement>>() {
-      }).to(TemplateOptionsToStatement.class);
       bind(LoginCredentials.class).annotatedWith(Names.named("image")).toProvider(
             GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull.class);
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java
new file mode 100644
index 0000000..594c424
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java
@@ -0,0 +1,59 @@
+/*
+ * 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.compute.functions;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.List;
+
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.scriptbuilder.InitScript;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
+import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
+
+import com.google.common.collect.ImmutableSet;
+
+@Singleton
+public class InstallKeysAndRunScript implements NodeAndTemplateOptionsToStatement {
+
+   @Override
+   public Statement apply(NodeMetadata node, TemplateOptions options) {
+      String user = options.getLoginUser();
+      if (user == null && node.getCredentials() != null) {
+         user = node.getCredentials().getUser();
+      }
+      List<Statement> bootstrap = newArrayList();
+      if (options.getPublicKey() != null)
+         bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey()), user));
+      if (options.getRunScript() != null)
+         bootstrap.add(options.getRunScript());
+      if (options.getPrivateKey() != null)
+         bootstrap.add(new InstallRSAPrivateKey(options.getPrivateKey()));
+      if (bootstrap.size() >= 1) {
+         if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript))
+            options.nameTask("bootstrap");
+         return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap);
+      }
+      return null;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java
new file mode 100644
index 0000000..90c74b6
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatement.java
@@ -0,0 +1,40 @@
+/*
+ * 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.compute.functions;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.scriptbuilder.domain.Statement;
+
+import com.google.inject.ImplementedBy;
+
+/**
+ * Returns the statement to be executed on the node.
+ */
+@ImplementedBy(InstallKeysAndRunScript.class)
+public interface NodeAndTemplateOptionsToStatement {
+
+   /**
+    * Returns the script that has to be executed in the given node.
+    * 
+    * @return The script to be executed or <code>null</code> if no script needs
+    *         to be run.
+    */
+   @Nullable
+   Statement apply(NodeMetadata node, TemplateOptions options);
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java
new file mode 100644
index 0000000..c4e26dd
--- /dev/null
+++ b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.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.compute.functions;
+
+import javax.inject.Singleton;
+
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.scriptbuilder.InitScript;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Convert the node and template options into a statement, but ignoring the
+ * public key.
+ * <p>
+ * Providers that can install the public key using their API should bind this
+ * strategy to avoid an unnecessary SSH connection to manually upload it.
+ */
+@Singleton
+public class NodeAndTemplateOptionsToStatementWithoutPublicKey implements NodeAndTemplateOptionsToStatement {
+
+   @Override
+   public Statement apply(NodeMetadata node, TemplateOptions options) {
+      ImmutableList.Builder<Statement> builder = ImmutableList.builder();
+      if (options.getRunScript() != null) {
+         builder.add(options.getRunScript());
+      }
+      if (options.getPrivateKey() != null) {
+         builder.add(new InstallRSAPrivateKey(options.getPrivateKey()));
+      }
+
+      ImmutableList<Statement> bootstrap = builder.build();
+      if (!bootstrap.isEmpty()) {
+         if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript)) {
+            options.nameTask("bootstrap");
+         }
+         return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap);
+      }
+
+      return null;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java
deleted file mode 100644
index 87d299d..0000000
--- a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.compute.functions;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-import java.util.List;
-
-import javax.inject.Singleton;
-
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.scriptbuilder.InitScript;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys;
-import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
-
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
-
-@Singleton
-public class TemplateOptionsToStatement implements Function<TemplateOptions, Statement> {
-
-   @Override
-   public Statement apply(TemplateOptions options) {
-      List<Statement> bootstrap = newArrayList();
-      if (options.getPublicKey() != null)
-         bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey())));
-      if (options.getRunScript() != null)
-         bootstrap.add(options.getRunScript());
-      if (options.getPrivateKey() != null)
-         bootstrap.add(new InstallRSAPrivateKey(options.getPrivateKey()));
-      if (bootstrap.size() >= 1) {
-         if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript))
-            options.nameTask("bootstrap");
-         return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap);
-      }
-      return null;
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java
----------------------------------------------------------------------
diff --git a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java
index 98419d1..2bb383c 100644
--- a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java
+++ b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java
@@ -34,6 +34,7 @@ import org.jclouds.compute.callables.RunScriptOnNode;
 import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.reference.ComputeServiceConstants;
 import org.jclouds.compute.util.OpenSocketFinder;
@@ -66,7 +67,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal
    private final OpenSocketFinder openSocketFinder;
 
    @Nullable
-   private final Statement statement;
+   private final NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement;
    private final TemplateOptions options;
    private AtomicReference<NodeMetadata> node;
    private final Set<NodeMetadata> goodNodes;
@@ -78,13 +79,13 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal
    @AssistedInject
    public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(
          @Named(TIMEOUT_NODE_RUNNING) Function<AtomicReference<NodeMetadata>, AtomicReference<NodeMetadata>> pollNodeRunning,
-         OpenSocketFinder openSocketFinder, Function<TemplateOptions, Statement> templateOptionsToStatement,
+         OpenSocketFinder openSocketFinder, NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement,
          InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options,
          @Assisted AtomicReference<NodeMetadata> node, @Assisted Set<NodeMetadata> goodNodes,
          @Assisted Map<NodeMetadata, Exception> badNodes,
          @Assisted Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
-      this.statement = checkNotNull(templateOptionsToStatement, "templateOptionsToStatement").apply(
-            checkNotNull(options, "options"));
+      this.nodeAndTemplateOptionsToStatement = checkNotNull(nodeAndTemplateOptionsToStatement,
+            "nodeAndTemplateOptionsToStatement");
       this.pollNodeRunning = checkNotNull(pollNodeRunning, "pollNodeRunning");
       this.initScriptRunnerFactory = checkNotNull(initScriptRunnerFactory, "initScriptRunnerFactory");
       this.openSocketFinder = checkNotNull(openSocketFinder, "openSocketFinder");
@@ -99,11 +100,11 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal
    public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(
          @Named(TIMEOUT_NODE_RUNNING) Function<AtomicReference<NodeMetadata>, AtomicReference<NodeMetadata>> pollNodeRunning,
          GetNodeMetadataStrategy getNode, OpenSocketFinder openSocketFinder,
-         Function<TemplateOptions, Statement> templateOptionsToStatement,
+         NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement,
          InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options,
          @Assisted Set<NodeMetadata> goodNodes, @Assisted Map<NodeMetadata, Exception> badNodes,
          @Assisted Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
-      this(pollNodeRunning, openSocketFinder, templateOptionsToStatement, initScriptRunnerFactory, options,
+      this(pollNodeRunning, openSocketFinder, nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options,
             new AtomicReference<NodeMetadata>(null), goodNodes, badNodes, customizationResponses);
    }
 
@@ -115,6 +116,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal
       try {
          if (options.shouldBlockUntilRunning()) {
             pollNodeRunning.apply(node);
+            Statement statement = nodeAndTemplateOptionsToStatement.apply(node.get(), options);
             if (statement != null) {
                RunScriptOnNode runner = initScriptRunnerFactory.create(node.get(), statement, options, badNodes).call();
                if (runner != null) {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java
new file mode 100644
index 0000000..d85dc75
--- /dev/null
+++ b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.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.compute.functions;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Map;
+
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.jclouds.scriptbuilder.domain.StatementList;
+import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
+import org.jclouds.ssh.SshKeys;
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link NodeAndTemplateOptionsToStatementWithoutPublicKey} class.
+ */
+@Test(groups = "unit", testName = "NodeAndTemplateOptionsToStatementWithoutPublicKeyTest")
+public class NodeAndTemplateOptionsToStatementWithoutPublicKeyTest {
+
+   @Test
+   public void testPublicKeyDoesNotGenerateAuthorizePublicKeyStatementIfOnlyPublicKeyOptionsConfigured() {
+      Map<String, String> keys = SshKeys.generate();
+      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"));
+      NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey();
+      assertNull(function.apply(null, options));
+   }
+
+   @Test
+   public void testPublicAndRunScriptKeyDoesNotGenerateAuthorizePublicKeyStatementIfRunScriptPresent() {
+      Map<String, String> keys = SshKeys.generate();
+      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")).runScript("uptime");
+
+      NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey();
+      Statement statement = function.apply(null, options);
+
+      assertEquals(statement.render(OsFamily.UNIX), "uptime\n");
+   }
+
+   @Test
+   public void testPublicAndPrivateKeyAndRunScriptDoesNotGenerateAuthorizePublicKeyStatementIfOtherOptionsPresent() {
+      Map<String, String> keys = SshKeys.generate();
+      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"))
+            .installPrivateKey(keys.get("private")).runScript("uptime");
+
+      NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey();
+      Statement statement = function.apply(null, options);
+
+      assertTrue(statement instanceof StatementList);
+      StatementList statements = (StatementList) statement;
+
+      assertEquals(statements.size(), 2);
+      assertEquals(statements.get(0).render(OsFamily.UNIX), "uptime\n");
+      assertTrue(statements.get(1) instanceof InstallRSAPrivateKey);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
----------------------------------------------------------------------
diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
index 822bd84..857c006 100644
--- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
+++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java
@@ -32,7 +32,8 @@ import org.jclouds.compute.config.CustomizationResponse;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.domain.NodeMetadataBuilder;
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
+import org.jclouds.compute.functions.InstallKeysAndRunScript;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.util.OpenSocketFinder;
 import org.jclouds.scriptbuilder.domain.Statement;
@@ -53,7 +54,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest {
    public void testBreakOnIllegalStateExceptionDuringPollNode() {
       InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class);
       OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class);
-      Function<TemplateOptions, Statement> templateOptionsToStatement = new TemplateOptionsToStatement();
+      NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript();
       @SuppressWarnings("unused")
       Statement statement = null;
       TemplateOptions options = new TemplateOptions();
@@ -79,7 +80,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest {
       // run
       AtomicReference<NodeMetadata> atomicNode = Atomics.newReference(pendingNode);
       new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(pollNodeRunning, openSocketFinder,
-            templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes,
+            nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes,
             customizationResponses).apply(atomicNode);
 
       assertEquals(goodNodes.size(), 0);
@@ -95,7 +96,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest {
       int portTimeoutSecs = 2;
       InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class);
       OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class);
-      Function<TemplateOptions, Statement> templateOptionsToStatement = new TemplateOptionsToStatement();
+      NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript();
       TemplateOptions options = new TemplateOptions().blockOnPort(22, portTimeoutSecs);
       Set<NodeMetadata> goodNodes = Sets.newLinkedHashSet();
       Map<NodeMetadata, Exception> badNodes = Maps.newLinkedHashMap();
@@ -124,7 +125,7 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest {
       // run
       AtomicReference<NodeMetadata> atomicNode = Atomics.newReference(pendingNode);
       new CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap(pollNodeRunning, openSocketFinder,
-            templateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes,
+            nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, atomicNode, goodNodes, badNodes,
             customizationResponses).apply(atomicNode);
 
       assertEquals(goodNodes.size(), 0);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/test/resources/initscript_with_java.sh
----------------------------------------------------------------------
diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh
index 6c2d077..bea054a 100644
--- a/compute/src/test/resources/initscript_with_java.sh
+++ b/compute/src/test/resources/initscript_with_java.sh
@@ -219,6 +219,7 @@ END_OF_JCLOUDS_SCRIPT
 		publicKey
 	END_OF_JCLOUDS_FILE
 	chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
+	chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh
 	chown -R defaultAdminUsername /home/users/defaultAdminUsername
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/test/resources/initscript_with_jetty.sh
----------------------------------------------------------------------
diff --git a/compute/src/test/resources/initscript_with_jetty.sh b/compute/src/test/resources/initscript_with_jetty.sh
index 1e66242..3b399cd 100644
--- a/compute/src/test/resources/initscript_with_jetty.sh
+++ b/compute/src/test/resources/initscript_with_jetty.sh
@@ -219,6 +219,7 @@ END_OF_JCLOUDS_SCRIPT
 		publicKey
 	END_OF_JCLOUDS_FILE
 	chmod 600 /home/users/web/.ssh/authorized_keys
+	chown -R web /home/users/web/.ssh
 	chown -R web /home/users/web
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/compute/src/test/resources/runscript_adminUpdate.sh
----------------------------------------------------------------------
diff --git a/compute/src/test/resources/runscript_adminUpdate.sh b/compute/src/test/resources/runscript_adminUpdate.sh
index eca129a..9d2ade9 100644
--- a/compute/src/test/resources/runscript_adminUpdate.sh
+++ b/compute/src/test/resources/runscript_adminUpdate.sh
@@ -100,6 +100,7 @@ END_OF_JCLOUDS_SCRIPT
 		publicKey
 	END_OF_JCLOUDS_FILE
 	chmod 600 /over/ridden/foo/.ssh/authorized_keys
+	chown -R foo /over/ridden/foo/.ssh
 	chown -R foo /over/ridden/foo
 	exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 	PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java
index 03caf85..e5b59ba 100644
--- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java
+++ b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/config/DigitalOcean2ComputeServiceContextModule.java
@@ -32,7 +32,8 @@ import org.jclouds.compute.domain.Image;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.compute.domain.NodeMetadata.Status;
 import org.jclouds.compute.extensions.ImageExtension;
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatementWithoutPublicKey;
 import org.jclouds.compute.options.TemplateOptions;
 import org.jclouds.compute.reference.ComputeServiceConstants.PollPeriod;
 import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts;
@@ -45,7 +46,6 @@ import org.jclouds.digitalocean2.compute.functions.DropletToNodeMetadata;
 import org.jclouds.digitalocean2.compute.functions.ImageInRegionToImage;
 import org.jclouds.digitalocean2.compute.functions.RegionToLocation;
 import org.jclouds.digitalocean2.compute.functions.SizeToHardware;
-import org.jclouds.digitalocean2.compute.functions.TemplateOptionsToStatementWithoutPublicKey;
 import org.jclouds.digitalocean2.compute.internal.ImageInRegion;
 import org.jclouds.digitalocean2.compute.options.DigitalOcean2TemplateOptions;
 import org.jclouds.digitalocean2.compute.strategy.CreateKeyPairsThenCreateNodes;
@@ -91,7 +91,7 @@ public class DigitalOcean2ComputeServiceContextModule extends
 
       bind(CreateNodesInGroupThenAddToSet.class).to(CreateKeyPairsThenCreateNodes.class);
       bind(TemplateOptions.class).to(DigitalOcean2TemplateOptions.class);
-      bind(TemplateOptionsToStatement.class).to(TemplateOptionsToStatementWithoutPublicKey.class);
+      bind(NodeAndTemplateOptionsToStatement.class).to(NodeAndTemplateOptionsToStatementWithoutPublicKey.class);
 
       bind(new TypeLiteral<ImageExtension>() {
       }).to(DigitalOcean2ImageExtension.class);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java b/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
deleted file mode 100644
index 52dcb0e..0000000
--- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.functions;
-
-import javax.inject.Singleton;
-
-import org.jclouds.compute.functions.TemplateOptionsToStatement;
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.scriptbuilder.InitScript;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
-import com.google.common.collect.ImmutableList;
-
-/**
- * Convert the template options into a statement, but ignoring the public key.
- * <p>
- * The {@link org.jclouds.DigitalOcean2ComputeServiceAdapter.compute.strategy.DigitalOceanComputeServiceAdapter} already takes care of
- * installing it using the {@link org.jclouds.digitalocean.features.KeyPairApi}.
- */
-@Singleton
-public class TemplateOptionsToStatementWithoutPublicKey extends TemplateOptionsToStatement {
-
-   @Override
-   public Statement apply(TemplateOptions options) {
-      ImmutableList.Builder<Statement> builder = ImmutableList.builder();
-      if (options.getRunScript() != null) {
-         builder.add(options.getRunScript());
-      }
-      if (options.getPrivateKey() != null) {
-         builder.add(new InstallRSAPrivateKey(options.getPrivateKey()));
-      }
-
-      ImmutableList<Statement> bootstrap = builder.build();
-      if (!bootstrap.isEmpty()) {
-         if (options.getTaskName() == null && !(options.getRunScript() instanceof InitScript)) {
-            options.nameTask("bootstrap");
-         }
-         return bootstrap.size() == 1 ? bootstrap.get(0) : new StatementList(bootstrap);
-      }
-
-      return null;
-   }
-
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
----------------------------------------------------------------------
diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java b/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
deleted file mode 100644
index c3a6cd2..0000000
--- a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.functions;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.Map;
-
-import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.scriptbuilder.domain.OsFamily;
-import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.domain.StatementList;
-import org.jclouds.scriptbuilder.statements.ssh.InstallRSAPrivateKey;
-import org.jclouds.ssh.SshKeys;
-import org.testng.annotations.Test;
-
-/**
- * Unit tests for the {@link TemplateOptionsToStatementWithoutPublicKey} class.
- */
-@Test(groups = "unit", testName = "TemplateOptionsToStatementWithoutPublicKeyTest")
-public class TemplateOptionsToStatementWithoutPublicKeyTest {
-
-   @Test
-   public void testPublicKeyDoesNotGenerateAuthorizePublicKeyStatementIfOnlyPublicKeyOptionsConfigured() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"));
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      assertNull(function.apply(options));
-   }
-
-   @Test
-   public void testPublicAndRunScriptKeyDoesNotGenerateAuthorizePublicKeyStatementIfRunScriptPresent() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")).runScript("uptime");
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      Statement statement = function.apply(options);
-
-      assertEquals(statement.render(OsFamily.UNIX), "uptime\n");
-   }
-
-   @Test
-   public void testPublicAndPrivateKeyAndRunScriptDoesNotGenerateAuthorizePublicKeyStatementIfOtherOptionsPresent() {
-      Map<String, String> keys = SshKeys.generate();
-      TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public"))
-            .installPrivateKey(keys.get("private")).runScript("uptime");
-
-      TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey();
-      Statement statement = function.apply(options);
-
-      assertTrue(statement instanceof StatementList);
-      StatementList statements = (StatementList) statement;
-
-      assertEquals(statements.size(), 2);
-      assertEquals(statements.get(0).render(OsFamily.UNIX), "uptime\n");
-      assertTrue(statements.get(1) instanceof InstallRSAPrivateKey);
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java
index fe48d66..4d12b86 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/login/UserAdd.java
@@ -231,7 +231,7 @@ public class UserAdd implements Statement {
       if (!authorizeRSAPublicKeys.isEmpty() || installRSAPrivateKey != null) {
          String sshDir = homeDir + "{fs}.ssh";
          if (!authorizeRSAPublicKeys.isEmpty())
-            statements.add(new AuthorizeRSAPublicKeys(sshDir, authorizeRSAPublicKeys));
+            statements.add(new AuthorizeRSAPublicKeys(sshDir, authorizeRSAPublicKeys, login));
          if (installRSAPrivateKey != null)
             statements.add(new InstallRSAPrivateKey(sshDir, installRSAPrivateKey));
       }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java
index 384a59e..13ec2dd 100644
--- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java
+++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeys.java
@@ -22,6 +22,7 @@ import static org.jclouds.scriptbuilder.domain.Statements.exec;
 
 import java.util.List;
 
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.scriptbuilder.domain.OsFamily;
 import org.jclouds.scriptbuilder.domain.Statement;
 import org.jclouds.scriptbuilder.domain.StatementList;
@@ -34,14 +35,20 @@ import com.google.common.collect.ImmutableList.Builder;
 public class AuthorizeRSAPublicKeys implements Statement {
    private final String sshDir;
    private final List<String> publicKeys;
-
+   private final String owner;
+   
    public AuthorizeRSAPublicKeys(Iterable<String> publicKeys) {
-      this("~/.ssh", publicKeys);
+      this("~/.ssh", publicKeys, null);
+   }
+   
+   public AuthorizeRSAPublicKeys(Iterable<String> publicKeys, @Nullable String owner) {
+      this("~/.ssh", publicKeys, owner);
    }
 
-   public AuthorizeRSAPublicKeys(String sshDir, Iterable<String> publicKeys) {
+   public AuthorizeRSAPublicKeys(String sshDir, Iterable<String> publicKeys, @Nullable String owner) {
       this.sshDir = checkNotNull(sshDir, "sshDir");
       this.publicKeys = ImmutableList.copyOf(checkNotNull(publicKeys, "publicKeys"));
+      this.owner = owner;
    }
 
    @Override
@@ -59,6 +66,9 @@ public class AuthorizeRSAPublicKeys implements Statement {
       String authorizedKeys = sshDir + "{fs}authorized_keys";
       statements.add(appendFile(authorizedKeys, Splitter.on('\n').split(Joiner.on("\n\n").join(publicKeys))));
       statements.add(exec("chmod 600 " + authorizedKeys));
+      if (owner != null) {
+         statements.add(exec(String.format("chown -R %s %s", owner, sshDir)));
+      }
       return new StatementList(statements.build()).render(family);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java
index 76d64e6..f24022d 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/login/UserAddTest.java
@@ -70,7 +70,7 @@ public class UserAddTest {
    public void testWithSshAuthorizedKeyUNIX() {
       assertEquals(
                UserAdd.builder().login("me").authorizeRSAPublicKey("rsapublickey").build().render(OsFamily.UNIX),
-               "mkdir -p /home/users\nchmod 0755 /home/users\nuseradd -c me -s /bin/bash -m  -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n\trsapublickey\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me\n");
+               "mkdir -p /home/users\nchmod 0755 /home/users\nuseradd -c me -s /bin/bash -m  -d /home/users/me me\nmkdir -p /home/users/me/.ssh\ncat >> /home/users/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n\trsapublickey\nEND_OF_JCLOUDS_FILE\nchmod 600 /home/users/me/.ssh/authorized_keys\nchown -R me /home/users/me/.ssh\nchown -R me /home/users/me\n");
    }
 
    public void testWithSshInstalledKeyUNIX() {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java
index acc76a4..ba11e98 100644
--- a/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java
+++ b/scriptbuilder/src/test/java/org/jclouds/scriptbuilder/statements/ssh/AuthorizeRSAPublicKeyTest.java
@@ -28,6 +28,17 @@ public class AuthorizeRSAPublicKeyTest {
 
    public void testAuthorizeRSAPublicKeyUNIXCurrentUser() {
       assertEquals(
+               new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.UNIX),
+               "mkdir -p ~/.ssh\n" +
+               "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" +
+               "\tssh-dss AAAAB\n" +
+               "END_OF_JCLOUDS_FILE\n" +
+               "chmod 600 ~/.ssh/authorized_keys\n" +
+               "chown -R jclouds ~/.ssh\n");
+   }
+   
+   public void testAuthorizeRSAPublicKeyUNIXNoOwner() {
+      assertEquals(
                new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX),
                "mkdir -p ~/.ssh\n" +
                "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" +
@@ -38,29 +49,31 @@ public class AuthorizeRSAPublicKeyTest {
 
    public void testAuthorizeRSAPublicKeyUNIXCurrentUserWith2Keys() {
       assertEquals(
-               new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD")).render(OsFamily.UNIX),
+               new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD"), "jclouds").render(OsFamily.UNIX),
                "mkdir -p ~/.ssh\n" +
                "cat >> ~/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" +
                "\tssh-dss AAAAB\n" +
                "\t\n" +
                "\tssh-dss CCCCD\n" +
                "END_OF_JCLOUDS_FILE\n" +
-               "chmod 600 ~/.ssh/authorized_keys\n");
+               "chmod 600 ~/.ssh/authorized_keys\n" +
+               "chown -R jclouds ~/.ssh\n");
    }
 
    public void testAuthorizeRSAPublicKeyUNIXSpecifiedDir() {
       assertEquals(
-               new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.UNIX),
+               new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.UNIX),
                "mkdir -p /home/me/.ssh\n" +
                "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" +
                "\tssh-dss AAAAB\n" +
                "END_OF_JCLOUDS_FILE\n" +
-               "chmod 600 /home/me/.ssh/authorized_keys\n");
+               "chmod 600 /home/me/.ssh/authorized_keys\n" +
+               "chown -R jclouds /home/me/.ssh\n");
    }
 
    public void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() {
       assertEquals(
-               new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD"))
+               new AuthorizeRSAPublicKeys("/home/me/.ssh", ImmutableSet.of("ssh-dss AAAAB", "ssh-dss CCCCD"), "jclouds")
                         .render(OsFamily.UNIX),
                         "mkdir -p /home/me/.ssh\n" +
                               "cat >> /home/me/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'\n" +
@@ -68,11 +81,12 @@ public class AuthorizeRSAPublicKeyTest {
                               "\t\n" +
                               "\tssh-dss CCCCD\n" +
                               "END_OF_JCLOUDS_FILE\n" +
-                              "chmod 600 /home/me/.ssh/authorized_keys\n");
+                              "chmod 600 /home/me/.ssh/authorized_keys\n" +
+                              "chown -R jclouds /home/me/.ssh\n");
    }
 
    @Test(expectedExceptions = UnsupportedOperationException.class)
    public void testAuthorizeRSAPublicKeyWINDOWS() {
-      new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB")).render(OsFamily.WINDOWS);
+      new AuthorizeRSAPublicKeys(ImmutableSet.of("ssh-dss AAAAB"), "jclouds").render(OsFamily.WINDOWS);
    }
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh
index cd2695f..f2c6e04 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh
@@ -15,6 +15,7 @@ cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<'END_OF_FILE'
 publicKey
 END_OF_FILE
 chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
+chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh
 chown -R defaultAdminUsername /home/users/defaultAdminUsername
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/resources/test_adminaccess_params.sh
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params.sh b/scriptbuilder/src/test/resources/test_adminaccess_params.sh
index 42d4852..ee61aa6 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_params.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_params.sh
@@ -14,6 +14,7 @@ cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
 	fooPublicKey
 END_OF_JCLOUDS_FILE
 chmod 600 /over/ridden/foo/.ssh/authorized_keys
+chown -R foo /over/ridden/foo/.ssh
 chown -R foo /over/ridden/foo
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh b/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh
index 20b1ee6..2ec9b27 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_params_and_fullname.sh
@@ -14,6 +14,7 @@ cat >> /over/ridden/foo/.ssh/authorized_keys <<-'END_OF_JCLOUDS_FILE'
 	fooPublicKey
 END_OF_JCLOUDS_FILE
 chmod 600 /over/ridden/foo/.ssh/authorized_keys
+chown -R foo /over/ridden/foo/.ssh
 chown -R foo /over/ridden/foo
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh
index 5583ab4..1f5164c 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh
@@ -6,6 +6,7 @@ cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_
 	publicKey
 END_OF_JCLOUDS_FILE
 chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
+chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh
 mkdir -p /home/users/defaultAdminUsername/.ssh
 rm /home/users/defaultAdminUsername/.ssh/id_rsa
 cat >> /home/users/defaultAdminUsername/.ssh/id_rsa <<-'END_OF_JCLOUDS_FILE'

http://git-wip-us.apache.org/repos/asf/jclouds/blob/179ed3b2/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
----------------------------------------------------------------------
diff --git a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
index db10e19..5e0c79d 100644
--- a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
+++ b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh
@@ -14,6 +14,7 @@ cat >> /home/users/defaultAdminUsername/.ssh/authorized_keys <<-'END_OF_JCLOUDS_
 	publicKey
 END_OF_JCLOUDS_FILE
 chmod 600 /home/users/defaultAdminUsername/.ssh/authorized_keys
+chown -R defaultAdminUsername /home/users/defaultAdminUsername/.ssh
 chown -R defaultAdminUsername /home/users/defaultAdminUsername
 exec 3<> /etc/ssh/sshd_config && awk -v TEXT="PasswordAuthentication no
 PermitRootLogin no


[3/3] jclouds git commit: JCLOUDS-1105: Do not create the default security group if custom groups are specified

Posted by na...@apache.org.
JCLOUDS-1105: Do not create the default security group if custom groups are specified


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/bde70457
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/bde70457
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/bde70457

Branch: refs/heads/master
Commit: bde70457d0e313497eee08175ef9397fcad825df
Parents: 71b3a2b
Author: Ignasi Barrera <na...@apache.org>
Authored: Thu Apr 21 17:14:56 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jun 17 22:18:35 2016 +0200

----------------------------------------------------------------------
 ...curityGroupsAsNeededAndReturnRunOptions.java | 11 +++------
 ...tyGroupsAsNeededAndReturnRunOptionsTest.java | 13 ++--------
 ...tyGroupsAsNeededAndReturnRunOptionsTest.java | 26 +++-----------------
 3 files changed, 10 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/bde70457/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
index e860fa1..32a6bae 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions.java
@@ -167,19 +167,16 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
       if (group != null) {
          String markerGroup = namingConvention.create().sharedNameForGroup(group);
 
-         RegionNameAndIngressRules regionNameAndIngressRulesForMarkerGroup;
-
          if (userSpecifiedTheirOwnGroups(options)) {
-            regionNameAndIngressRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, new int[] {},
-                     false, vpcId);
             groups.addAll(EC2TemplateOptions.class.cast(options).getGroups());
          } else {
-            regionNameAndIngressRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, options
+            RegionNameAndIngressRules regionNameAndIngressRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, options
                      .getInboundPorts(), true, vpcId);
+            // this will create if not yet exists.
+            groups.add(securityGroupMap.getUnchecked(regionNameAndIngressRulesForMarkerGroup));
          }
-         // this will create if not yet exists.
-         groups.add(securityGroupMap.getUnchecked(regionNameAndIngressRulesForMarkerGroup));
       }
+
       return groups.build();
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bde70457/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
----------------------------------------------------------------------
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
index 055eebe..8e9f062 100644
--- a/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/compute/strategy/CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
@@ -511,16 +511,12 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
       verifyStrategy(strategy);
    }
 
-   public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
+   public void testGetSecurityGroupsForTagAndOptions_DoesNorReuseGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
       // setup constants
       String region = Region.AP_SOUTHEAST_1;
       String group = "group";
-      String generatedMarkerGroup = "jclouds#group";
       Set<String> groupIds = ImmutableSet.<String> of("group1", "group2");
-      int[] ports = {};
-      boolean shouldAuthorizeSelf = true;
-      boolean groupExisted = true;
-      Set<String> returnVal = ImmutableSet.<String> of(generatedMarkerGroup, "group1", "group2");
+      Set<String> returnVal = ImmutableSet.<String> of("group1", "group2");
 
       // create mocks
       CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
@@ -528,11 +524,6 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
 
       // setup expectations
       expect(options.getGroups()).andReturn(groupIds).atLeastOnce();
-      RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
-            ports, shouldAuthorizeSelf, null);
-
-      expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules))
-            .andReturn(groupExisted ? generatedMarkerGroup : null);
 
       // replay mocks
       replay(options);

http://git-wip-us.apache.org/repos/asf/jclouds/blob/bde70457/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
----------------------------------------------------------------------
diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
index 27f20b1..f73ebc1 100644
--- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
+++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsTest.java
@@ -757,16 +757,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
       verifyStrategy(strategy);
    }
 
-   public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
+   public void testGetSecurityGroupsForTagAndOptions_DoesNotReuseGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
       // setup constants
       String region = Region.AP_SOUTHEAST_1;
       String group = "group";
-      String generatedMarkerGroup = "jclouds#group";
       Set<String> groupNames = ImmutableSet.<String> of("group1", "group2");
-      int[] ports = {};
-      boolean shouldAuthorizeSelf = true;
-      boolean groupExisted = true;
-      Set<String> returnVal = ImmutableSet.<String> of("sg-12345", "group1", "group2");
+      Set<String> returnVal = ImmutableSet.<String> of("group1", "group2");
 
       // create mocks
       CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
@@ -775,11 +771,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
       // setup expectations
       expect(options.getGroupIds()).andReturn(ImmutableSet.<String> of());
       expect(options.getGroups()).andReturn(groupNames).atLeastOnce();
-      RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
-            ports, shouldAuthorizeSelf, null);
-
-      expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules))
-            .andReturn(groupExisted ? "sg-12345" : null);
 
       // replay mocks
       replay(options);
@@ -793,16 +784,12 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
       verifyStrategy(strategy);
    }
 
-   public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroupIds() {
+   public void testGetSecurityGroupsForTagAndOptions_DoesNotReuseGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroupIds() {
       // setup constants
       String region = Region.AP_SOUTHEAST_1;
       String group = "group";
-      String generatedMarkerGroup = "jclouds#group";
       Set<String> groupNames = ImmutableSet.<String> of();
-      int[] ports = {};
-      boolean shouldAuthorizeSelf = true;
-      boolean groupExisted = true;
-      Set<String> returnVal = ImmutableSet.<String> of("sg-12345");
+      Set<String> returnVal = ImmutableSet.<String> of();
 
       // create mocks
       CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
@@ -811,11 +798,6 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
       // setup expectations
       expect(options.getGroupIds()).andReturn(ImmutableSet.<String> of("group1", "group2"));
       expect(options.getGroups()).andReturn(groupNames).atLeastOnce();
-      RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
-            ports, shouldAuthorizeSelf, null);
-
-      expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules))
-            .andReturn(groupExisted ? "sg-12345" : null);
 
       // replay mocks
       replay(options);


[2/3] jclouds git commit: JCLOUDS-1129:Add more interfaces of the same network

Posted by na...@apache.org.
JCLOUDS-1129:Add more interfaces of the same network


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/71b3a2b6
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/71b3a2b6
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/71b3a2b6

Branch: refs/heads/master
Commit: 71b3a2b6313e5045100edcf249eee1c88f05e084
Parents: 179ed3b
Author: urban <za...@iskratel.si>
Authored: Fri Jun 17 11:10:09 2016 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri Jun 17 22:16:59 2016 +0200

----------------------------------------------------------------------
 .../nova/v2_0/options/CreateServerOptions.java  | 23 ++++++++++----------
 1 file changed, 12 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/71b3a2b6/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
index c57e296..89338eb 100644
--- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java
@@ -42,6 +42,7 @@ import com.google.common.base.Objects;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ForwardingObject;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -105,8 +106,8 @@ public class CreateServerOptions implements MapBinder {
    private List<File> personality = Lists.newArrayList();
    private byte[] userData;
    private String diskConfig;
-   private Set<String> networks = ImmutableSet.of();
-   private Set<Network> novaNetworks = ImmutableSet.of();
+   private List<String> networks = ImmutableList.of();
+   private List<Network> novaNetworks = ImmutableList.of();
    private String availabilityZone;
    private boolean configDrive;
    private Set<BlockDeviceMapping> blockDeviceMappings = ImmutableSet.of();
@@ -180,7 +181,7 @@ public class CreateServerOptions implements MapBinder {
       String user_data;
       @Named("OS-DCF:diskConfig")
       String diskConfig;
-      Set<Map<String, String>> networks;
+      List<Map<String, String>> networks;
       @Named("config_drive")
       String configDrive;
       @Named("block_device_mapping_v2")
@@ -224,7 +225,7 @@ public class CreateServerOptions implements MapBinder {
          server.diskConfig = diskConfig;
       }
       if (!networks.isEmpty() || !novaNetworks.isEmpty()) {
-         server.networks = Sets.newLinkedHashSet(); // ensures ordering is preserved - helps testing and more intuitive for users.
+         server.networks = Lists.newArrayList(); // ensures ordering is preserved - helps testing and more intuitive for users.
          for (Network network : novaNetworks) {
             // Avoid serializing null values, which are common here.
             ImmutableMap.Builder<String, String> networkMap = new ImmutableMap.Builder<String, String>();
@@ -383,7 +384,7 @@ public class CreateServerOptions implements MapBinder {
     * @see #getNetworks()
     */
    public CreateServerOptions networks(Iterable<String> networks) {
-      this.networks = ImmutableSet.copyOf(networks);
+      this.networks = ImmutableList.copyOf(networks);
       return this;
    }
 
@@ -392,7 +393,7 @@ public class CreateServerOptions implements MapBinder {
     * Overwrites networks supplied by {@link #networks(Iterable)}
     */
    public CreateServerOptions novaNetworks(Iterable<Network> networks) {
-      this.novaNetworks = ImmutableSet.copyOf(networks);
+      this.novaNetworks = ImmutableList.copyOf(networks);
       return this;
    }
 
@@ -400,7 +401,7 @@ public class CreateServerOptions implements MapBinder {
     * @see #getNetworks()
     */
    public CreateServerOptions networks(String... networks) {
-      return networks(ImmutableSet.copyOf(networks));
+      return networks(ImmutableList.copyOf(networks));
    }
 
    /**
@@ -438,20 +439,20 @@ public class CreateServerOptions implements MapBinder {
    /**
     * Get custom networks specified for the server.
     *
-    * @return A set of uuids defined by Neutron (previously Quantum)
+    * @return A list of uuids defined by Neutron (previously Quantum)
     * @see <a href="https://wiki.openstack.org/wiki/Neutron/APIv2-specification#Network">Neutron Networks<a/>
     */
-   public Set<String> getNetworks() {
+   public List<String> getNetworks() {
       return networks;
    }
 
    /**
     * Get custom networks specified for the server.
     *
-    * @return A set of uuids defined by Neutron (previously Quantum)
+    * @return A list of uuids defined by Neutron (previously Quantum)
     * @see <a href="https://wiki.openstack.org/wiki/Neutron/APIv2-specification#Network">Neutron Networks<a/>
     */
-   public Set<Network> getNovaNetworks() {
+   public List<Network> getNovaNetworks() {
       return novaNetworks;
    }