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 4acaef9fe18..8d9aa0ee327 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.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 @@ protected void configure() { install(new ComputeServiceTimeoutsModule()); bind(new TypeLiteral>() { }).to(CreateSshClientOncePortIsListeningOnNode.class); - bind(new TypeLiteral>() { - }).to(TemplateOptionsToStatement.class); bind(LoginCredentials.class).annotatedWith(Names.named("image")).toProvider( GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull.class); diff --git a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java similarity index 83% rename from compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java rename to compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java index 87d299da3a1..594c4247d1b 100644 --- a/compute/src/main/java/org/jclouds/compute/functions/TemplateOptionsToStatement.java +++ b/compute/src/main/java/org/jclouds/compute/functions/InstallKeysAndRunScript.java @@ -22,6 +22,7 @@ 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; @@ -29,17 +30,20 @@ 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 { +public class InstallKeysAndRunScript implements NodeAndTemplateOptionsToStatement { @Override - public Statement apply(TemplateOptions options) { + public Statement apply(NodeMetadata node, TemplateOptions options) { + String user = options.getLoginUser(); + if (user == null && node.getCredentials() != null) { + user = node.getCredentials().getUser(); + } List bootstrap = newArrayList(); if (options.getPublicKey() != null) - bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey()))); + bootstrap.add(new AuthorizeRSAPublicKeys(ImmutableSet.of(options.getPublicKey()), user)); if (options.getRunScript() != null) bootstrap.add(options.getRunScript()); if (options.getPrivateKey() != null) 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 00000000000..90c74b60593 --- /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 null if no script needs + * to be run. + */ + @Nullable + Statement apply(NodeMetadata node, TemplateOptions options); +} diff --git a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java similarity index 76% rename from providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java rename to compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java index 52dcb0ef590..c4e26dd0b2a 100644 --- a/providers/digitalocean2/src/main/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKey.java +++ b/compute/src/main/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKey.java @@ -14,29 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.digitalocean2.compute.functions; +package org.jclouds.compute.functions; import javax.inject.Singleton; -import org.jclouds.compute.functions.TemplateOptionsToStatement; +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 template options into a statement, but ignoring the public key. + * Convert the node and template options into a statement, but ignoring the + * public key. *

- * The {@link org.jclouds.DigitalOcean2ComputeServiceAdapter.compute.strategy.DigitalOceanComputeServiceAdapter} already takes care of - * installing it using the {@link org.jclouds.digitalocean.features.KeyPairApi}. + * 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 TemplateOptionsToStatementWithoutPublicKey extends TemplateOptionsToStatement { +public class NodeAndTemplateOptionsToStatementWithoutPublicKey implements NodeAndTemplateOptionsToStatement { @Override - public Statement apply(TemplateOptions options) { + public Statement apply(NodeMetadata node, TemplateOptions options) { ImmutableList.Builder builder = ImmutableList.builder(); if (options.getRunScript() != null) { builder.add(options.getRunScript()); @@ -55,5 +57,4 @@ public Statement apply(TemplateOptions options) { return null; } - } 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 98419d1b8b2..2bb383c41be 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.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 @@ Function, Void> create(TemplateOptions options, Se private final OpenSocketFinder openSocketFinder; @Nullable - private final Statement statement; + private final NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement; private final TemplateOptions options; private AtomicReference node; private final Set goodNodes; @@ -78,13 +79,13 @@ Function, Void> create(TemplateOptions options, Se @AssistedInject public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named(TIMEOUT_NODE_RUNNING) Function, AtomicReference> pollNodeRunning, - OpenSocketFinder openSocketFinder, Function templateOptionsToStatement, + OpenSocketFinder openSocketFinder, NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted AtomicReference node, @Assisted Set goodNodes, @Assisted Map badNodes, @Assisted Multimap 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 CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( public CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap( @Named(TIMEOUT_NODE_RUNNING) Function, AtomicReference> pollNodeRunning, GetNodeMetadataStrategy getNode, OpenSocketFinder openSocketFinder, - Function templateOptionsToStatement, + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement, InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory, @Assisted TemplateOptions options, @Assisted Set goodNodes, @Assisted Map badNodes, @Assisted Multimap customizationResponses) { - this(pollNodeRunning, openSocketFinder, templateOptionsToStatement, initScriptRunnerFactory, options, + this(pollNodeRunning, openSocketFinder, nodeAndTemplateOptionsToStatement, initScriptRunnerFactory, options, new AtomicReference(null), goodNodes, badNodes, customizationResponses); } @@ -115,6 +116,7 @@ public Void call() { 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) { diff --git a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java similarity index 75% rename from providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java rename to compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java index c3a6cd26247..d85dc750e33 100644 --- a/providers/digitalocean2/src/test/java/org/jclouds/digitalocean2/compute/functions/TemplateOptionsToStatementWithoutPublicKeyTest.java +++ b/compute/src/test/java/org/jclouds/compute/functions/NodeAndTemplateOptionsToStatementWithoutPublicKeyTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jclouds.digitalocean2.compute.functions; +package org.jclouds.compute.functions; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; @@ -31,18 +31,17 @@ import org.testng.annotations.Test; /** - * Unit tests for the {@link TemplateOptionsToStatementWithoutPublicKey} class. + * Unit tests for the {@link NodeAndTemplateOptionsToStatementWithoutPublicKey} class. */ -@Test(groups = "unit", testName = "TemplateOptionsToStatementWithoutPublicKeyTest") -public class TemplateOptionsToStatementWithoutPublicKeyTest { +@Test(groups = "unit", testName = "NodeAndTemplateOptionsToStatementWithoutPublicKeyTest") +public class NodeAndTemplateOptionsToStatementWithoutPublicKeyTest { @Test public void testPublicKeyDoesNotGenerateAuthorizePublicKeyStatementIfOnlyPublicKeyOptionsConfigured() { Map keys = SshKeys.generate(); TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")); - - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - assertNull(function.apply(options)); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + assertNull(function.apply(null, options)); } @Test @@ -50,8 +49,8 @@ public void testPublicAndRunScriptKeyDoesNotGenerateAuthorizePublicKeyStatementI Map keys = SshKeys.generate(); TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")).runScript("uptime"); - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - Statement statement = function.apply(options); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + Statement statement = function.apply(null, options); assertEquals(statement.render(OsFamily.UNIX), "uptime\n"); } @@ -62,8 +61,8 @@ public void testPublicAndPrivateKeyAndRunScriptDoesNotGenerateAuthorizePublicKey TemplateOptions options = TemplateOptions.Builder.authorizePublicKey(keys.get("public")) .installPrivateKey(keys.get("private")).runScript("uptime"); - TemplateOptionsToStatementWithoutPublicKey function = new TemplateOptionsToStatementWithoutPublicKey(); - Statement statement = function.apply(options); + NodeAndTemplateOptionsToStatementWithoutPublicKey function = new NodeAndTemplateOptionsToStatementWithoutPublicKey(); + Statement statement = function.apply(null, options); assertTrue(statement instanceof StatementList); StatementList statements = (StatementList) statement; 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 822bd848968..857c0063155 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.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 templateOptionsToStatement = new TemplateOptionsToStatement(); + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript(); @SuppressWarnings("unused") Statement statement = null; TemplateOptions options = new TemplateOptions(); @@ -79,7 +80,7 @@ public AtomicReference apply(AtomicReference node) { // run AtomicReference 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 void testBreakGraceWhenNodeSocketFailsToOpen() { int portTimeoutSecs = 2; InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory = createMock(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class); OpenSocketFinder openSocketFinder = createMock(OpenSocketFinder.class); - Function templateOptionsToStatement = new TemplateOptionsToStatement(); + NodeAndTemplateOptionsToStatement nodeAndTemplateOptionsToStatement = new InstallKeysAndRunScript(); TemplateOptions options = new TemplateOptions().blockOnPort(22, portTimeoutSecs); Set goodNodes = Sets.newLinkedHashSet(); Map badNodes = Maps.newLinkedHashMap(); @@ -124,7 +125,7 @@ public AtomicReference apply(AtomicReference node) { // run AtomicReference 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); diff --git a/compute/src/test/resources/initscript_with_java.sh b/compute/src/test/resources/initscript_with_java.sh index 6c2d077cd7a..bea054a86fc 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 diff --git a/compute/src/test/resources/initscript_with_jetty.sh b/compute/src/test/resources/initscript_with_jetty.sh index 1e662427f1f..3b399cd5280 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 diff --git a/compute/src/test/resources/runscript_adminUpdate.sh b/compute/src/test/resources/runscript_adminUpdate.sh index eca129a7844..9d2ade99a01 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 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 03caf856873..e5b59ba91ef 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.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.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 @@ protected void configure() { 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() { }).to(DigitalOcean2ImageExtension.class); 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 fe48d66f9bd..4d12b867745 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 String render(OsFamily family) { 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)); } 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 384a59ed74d..13ec2ddf50d 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 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 @@ public class AuthorizeRSAPublicKeys implements Statement { private final String sshDir; private final List publicKeys; - + private final String owner; + public AuthorizeRSAPublicKeys(Iterable publicKeys) { - this("~/.ssh", publicKeys); + this("~/.ssh", publicKeys, null); + } + + public AuthorizeRSAPublicKeys(Iterable publicKeys, @Nullable String owner) { + this("~/.ssh", publicKeys, owner); } - public AuthorizeRSAPublicKeys(String sshDir, Iterable publicKeys) { + public AuthorizeRSAPublicKeys(String sshDir, Iterable 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 String render(OsFamily family) { 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); } } 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 76d64e6dacc..f24022dbffc 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 void testWithPasswordUNIX() { 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() { 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 acc76a42266..ba11e982160 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 @@ -27,6 +27,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" + @@ -38,29 +49,31 @@ public void testAuthorizeRSAPublicKeyUNIXCurrentUser() { 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 void testAuthorizeRSAPublicKeyUNIXSpecifiedDirWith2Keys() { "\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); } } diff --git a/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh b/scriptbuilder/src/test/resources/test_adminaccess_flipped.sh index cd2695fdcf3..f2c6e044599 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 diff --git a/scriptbuilder/src/test/resources/test_adminaccess_params.sh b/scriptbuilder/src/test/resources/test_adminaccess_params.sh index 42d4852ca66..ee61aa6c222 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 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 20b1ee63b98..2ec9b27e623 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 diff --git a/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh b/scriptbuilder/src/test/resources/test_adminaccess_plainuser.sh index 5583ab4ff44..1f5164ca8be 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' diff --git a/scriptbuilder/src/test/resources/test_adminaccess_standard.sh b/scriptbuilder/src/test/resources/test_adminaccess_standard.sh index db10e19cdfb..5e0c79dcb2a 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