You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/04/15 07:51:37 UTC
[1/2] incubator-brooklyn git commit: Adds CreateUserPolicy
Repository: incubator-brooklyn
Updated Branches:
refs/heads/master 4f6fa6ca9 -> 10853fc04
Adds CreateUserPolicy
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/bea997bc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/bea997bc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/bea997bc
Branch: refs/heads/master
Commit: bea997bc762cc55d1c188245ccb98a53606db883
Parents: 4f6fa6c
Author: Aled Sage <al...@gmail.com>
Authored: Tue Apr 14 14:48:53 2015 -0500
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Apr 15 00:40:45 2015 -0500
----------------------------------------------------------------------
.../brooklyn/policy/os/CreateUserPolicy.java | 174 +++++++++++++++++++
.../policy/os/CreateUserPolicyLiveTest.java | 113 ++++++++++++
.../policy/os/CreateUserPolicyTest.java | 138 +++++++++++++++
3 files changed, 425 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bea997bc/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
new file mode 100644
index 0000000..80851da
--- /dev/null
+++ b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java
@@ -0,0 +1,174 @@
+/*
+ * 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 brooklyn.policy.os;
+
+import java.util.List;
+
+import org.jclouds.compute.config.AdminAccessConfiguration;
+import org.jclouds.scriptbuilder.functions.InitAdminAccess;
+import org.jclouds.scriptbuilder.statements.login.AdminAccess;
+import org.jclouds.scriptbuilder.statements.ssh.SshdConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.AbstractEntity;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.SensorEvent;
+import brooklyn.event.SensorEventListener;
+import brooklyn.event.basic.Sensors;
+import brooklyn.location.Location;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.policy.basic.AbstractPolicy;
+import brooklyn.util.flags.SetFromFlag;
+import brooklyn.util.internal.ssh.SshTool;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * When attached to an entity, this will monitor for when an {@link SshMachineLocation} is added to that entity
+ * (e.g. when a VM has been provisioned for it).
+ *
+ * The policy will then (asynchronously) add a new user to the VM, with a randomly generated password.
+ * The ssh details will be set as a sensor on the entity.
+ *
+ * If this is used, it is strongly encouraged to tell users to change the password on first login.
+ *
+ * A preferred mechanism would be for an external key-management tool to generate ssh key-pairs for
+ * the user, and for the public key to be passed to Brooklyn. However, there is not a customer
+ * requirement for that yet, so focusing on the password-approach.
+ */
+@Beta
+public class CreateUserPolicy extends AbstractPolicy implements SensorEventListener<Location> {
+
+ // TODO Should add support for authorizing ssh keys as well
+
+ // TODO Should review duplication with:
+ // - JcloudsLocationConfig.GRANT_USER_SUDO
+ // (but config default/description and context of use are different)
+ // - AdminAccess in JcloudsLocation.createUserStatements
+
+ // TODO Could make the password explicitly configurable, or auto-generate if not set?
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateUserPolicy.class);
+
+ @SetFromFlag("user")
+ public static final ConfigKey<String> VM_USERNAME = ConfigKeys.newStringConfigKey("createuser.vm.user.name");
+
+ @SetFromFlag("grantSudo")
+ public static final ConfigKey<Boolean> GRANT_SUDO = ConfigKeys.newBooleanConfigKey(
+ "createuser.vm.user.grantSudo",
+ "Whether to give the new user sudo rights",
+ false);
+
+ public static final AttributeSensor<String> VM_USER_CREDENTIALS = Sensors.newStringSensor(
+ "createuser.vm.user.credentials",
+ "The \"<user> : <password> @ <hostname>:<port>\"");
+
+ public void setEntity(EntityLocal entity) {
+ super.setEntity(entity);
+ subscribe(entity, AbstractEntity.LOCATION_ADDED, this);
+ }
+
+ @Override
+ public void onEvent(SensorEvent<Location> event) {
+ final Entity entity = event.getSource();
+ final Location loc = event.getValue();
+ if (loc instanceof SshMachineLocation) {
+ addUserAsync(entity, (SshMachineLocation)loc);
+ }
+ }
+
+ protected void addUserAsync(final Entity entity, final SshMachineLocation machine) {
+ ((EntityInternal)entity).getExecutionContext().execute(new Runnable() {
+ public void run() {
+ addUser(entity, machine);
+ }});
+ }
+
+ protected void addUser(Entity entity, SshMachineLocation machine) {
+ boolean grantSudo = getRequiredConfig(GRANT_SUDO);
+ String user = getRequiredConfig(VM_USERNAME);
+ String password = Identifiers.makeRandomId(12);
+ String hostname = machine.getAddress().getHostName();
+ int port = machine.getPort();
+ String creds = user + " : " + password + " @ " +hostname + ":" + port;
+
+ LOG.info("Adding auto-generated user "+user+" @ "+hostname+":"+port);
+
+ // Build the command to create the user
+ // Note AdminAccess requires _all_ fields set, due to http://code.google.com/p/jclouds/issues/detail?id=1095
+ // If jclouds grants Sudo rights, it overwrites the /etc/sudoers, which makes integration tests very dangerous! Not using it.
+ AdminAccess adminAccess = AdminAccess.builder()
+ .adminUsername(user)
+ .adminPassword(password)
+ .grantSudoToAdminUser(false)
+ .resetLoginPassword(true)
+ .loginPassword(password)
+ .authorizeAdminPublicKey(false)
+ .adminPublicKey("ignored")
+ .installAdminPrivateKey(false)
+ .adminPrivateKey("ignore")
+ .lockSsh(false)
+ .build();
+
+ org.jclouds.scriptbuilder.domain.OsFamily scriptOsFamily = (machine.getMachineDetails().getOsDetails().isWindows())
+ ? org.jclouds.scriptbuilder.domain.OsFamily.WINDOWS
+ : org.jclouds.scriptbuilder.domain.OsFamily.UNIX;
+
+ InitAdminAccess initAdminAccess = new InitAdminAccess(new AdminAccessConfiguration.Default());
+ initAdminAccess.visit(adminAccess);
+ String cmd = adminAccess.render(scriptOsFamily);
+
+ // Exec command to create the user
+ int result = machine.execScript(ImmutableMap.of(SshTool.PROP_RUN_AS_ROOT.getName(), true), "create-user-"+user, ImmutableList.of(cmd));
+ if (result != 0) {
+ throw new IllegalStateException("Failed to auto-generate user, using command "+cmd);
+ }
+
+ // Exec command to grant password-access to sshd (which may have been disabled earlier).
+ cmd = new SshdConfig(ImmutableMap.of("PasswordAuthentication", "yes")).render(scriptOsFamily);
+ result = machine.execScript(ImmutableMap.of(SshTool.PROP_RUN_AS_ROOT.getName(), true), "create-user-"+user, ImmutableList.of(cmd));
+ if (result != 0) {
+ throw new IllegalStateException("Failed to enable ssh-login-with-password, using command "+cmd);
+ }
+
+ // Exec command to grant sudo rights.
+ if (grantSudo) {
+ List<String> cmds = ImmutableList.of(
+ "cat >> /etc/sudoers <<-'END_OF_JCLOUDS_FILE'\n"+
+ user+" ALL = (ALL) NOPASSWD:ALL\n"+
+ "END_OF_JCLOUDS_FILE\n",
+ "chmod 0440 /etc/sudoers");
+ result = machine.execScript(ImmutableMap.of(SshTool.PROP_RUN_AS_ROOT.getName(), true), "add-user-to-sudoers-"+user, cmds);
+ if (result != 0) {
+ throw new IllegalStateException("Failed to auto-generate user, using command "+cmd);
+ }
+ }
+
+ ((EntityLocal)entity).setAttribute(VM_USER_CREDENTIALS, creds);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bea997bc/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
new file mode 100644
index 0000000..2913096
--- /dev/null
+++ b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyLiveTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.policy.os;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppLiveTestSupport;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.location.LocationSpec;
+import brooklyn.location.MachineProvisioningLocation;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.os.CreateUserPolicy;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.internal.ssh.SshTool;
+import brooklyn.util.text.Identifiers;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class CreateUserPolicyLiveTest extends BrooklynAppLiveTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateUserPolicyLiveTest.class);
+
+ // TODO Fails on OS X
+ @Test(groups={"Integration", "WIP"})
+ public void testIntegrationCreatesUser() throws Exception {
+ LocalhostMachineProvisioningLocation loc = app.newLocalhostProvisioningLocation();
+ runTestCreatesUser(loc);
+ }
+
+ @Test(groups="Live")
+ @SuppressWarnings("unchecked")
+ public void testLiveCreatesUser() throws Exception {
+ String locSpec = "jclouds:softlayer:ams01";
+ ImmutableMap<String, String> locArgs = ImmutableMap.of("imageId", "CENTOS_6_64");
+ Location loc = mgmt.getLocationRegistry().resolve(locSpec, locArgs);
+ runTestCreatesUser((MachineProvisioningLocation<SshMachineLocation>) loc);
+ }
+
+ public void runTestCreatesUser(MachineProvisioningLocation<SshMachineLocation> loc) throws Exception {
+ String newUsername = Identifiers.makeRandomId(16);
+ SshMachineLocation machine = loc.obtain(ImmutableMap.of());
+
+ try {
+ app.createAndManageChild(EntitySpec.create(TestEntity.class)
+ .policy(PolicySpec.create(CreateUserPolicy.class)
+ .configure(CreateUserPolicy.GRANT_SUDO, true)
+ .configure(CreateUserPolicy.VM_USERNAME, newUsername)));
+ TestEntity entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+
+ app.start(ImmutableList.of(machine));
+
+ String creds = EntityTestUtils.assertAttributeEventuallyNonNull(entity, CreateUserPolicy.VM_USER_CREDENTIALS);
+ Pattern pattern = Pattern.compile("(.*) : (.*) @ (.*):(.*)");
+ Matcher matcher = pattern.matcher(creds);
+ assertTrue(matcher.matches());
+ String username = matcher.group(1).trim();
+ String password = matcher.group(2).trim();
+ String hostname = matcher.group(3).trim();
+ String port = matcher.group(4).trim();
+
+ assertEquals(newUsername, username);
+ assertEquals(hostname, machine.getAddress().getHostName());
+ assertEquals(port, ""+machine.getPort());
+
+ SshMachineLocation machine2 = mgmt.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure(SshTool.PROP_USER, newUsername)
+ .configure(SshMachineLocation.PASSWORD, password)
+ .configure("address", hostname)
+ .configure(SshMachineLocation.SSH_PORT, Integer.parseInt(port)));
+
+ LOG.info("Checking ssh'able for auto-generated user: machine="+machine+"; creds="+creds);
+ assertTrue(machine2.isSshable(), "machine="+machine+"; creds="+creds);
+
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ LOG.info("Deleting auto-generated user "+newUsername);
+ machine.execScript("delete-user-"+newUsername, ImmutableList.of("sudo userdel -f "+newUsername));
+
+ loc.release(machine);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/bea997bc/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
new file mode 100644
index 0000000..855d994
--- /dev/null
+++ b/locations/jclouds/src/test/java/brooklyn/policy/os/CreateUserPolicyTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 brooklyn.policy.os;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppUnitTestSupport;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.LocationSpec;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.policy.PolicySpec;
+import brooklyn.policy.os.CreateUserPolicy;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.internal.ssh.SshTool;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+public class CreateUserPolicyTest extends BrooklynAppUnitTestSupport {
+
+ @SuppressWarnings("unused")
+ private static final Logger LOG = LoggerFactory.getLogger(CreateUserPolicyTest.class);
+
+ public static class RecordingSshMachineLocation extends SshMachineLocation {
+ private static final long serialVersionUID = 1641930081769106380L;
+
+ public static List<List<String>> execScriptCalls = Lists.newArrayList();
+
+ @Override
+ public int execScript(String summary, List<String> cmds) {
+ execScriptCalls.add(cmds);
+ return 0;
+ }
+ @Override
+ public int execScript(Map<String,?> props, String summaryForLogging, List<String> cmds) {
+ execScriptCalls.add(cmds);
+ return 0;
+ }
+ @Override
+ public int execScript(String summaryForLogging, List<String> cmds, Map<String,?> env) {
+ execScriptCalls.add(cmds);
+ return 0;
+ }
+ @Override
+ public int execScript(Map<String,?> props, String summaryForLogging, List<String> cmds, Map<String,?> env) {
+ execScriptCalls.add(cmds);
+ return 0;
+ }
+ }
+
+ @BeforeMethod(alwaysRun=true)
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ RecordingSshMachineLocation.execScriptCalls.clear();
+ }
+
+ @AfterMethod(alwaysRun=true)
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ super.tearDown();
+ } finally {
+ RecordingSshMachineLocation.execScriptCalls.clear();
+ }
+ }
+
+ @Test
+ public void testCallsCreateUser() throws Exception {
+ SshMachineLocation machine = mgmt.getLocationManager().createLocation(LocationSpec.create(RecordingSshMachineLocation.class)
+ .configure(SshTool.PROP_USER, "myuser")
+ .configure(SshTool.PROP_PASSWORD, "mypassword")
+ .configure("address", "1.2.3.4")
+ .configure(SshTool.PROP_PORT, 1234));
+
+ String newUsername = "mynewuser";
+
+ app.createAndManageChild(EntitySpec.create(TestEntity.class)
+ .policy(PolicySpec.create(CreateUserPolicy.class)
+ .configure(CreateUserPolicy.GRANT_SUDO, true)
+ .configure(CreateUserPolicy.VM_USERNAME, newUsername)));
+ TestEntity entity = (TestEntity) Iterables.getOnlyElement(app.getChildren());
+ app.start(ImmutableList.of(machine));
+
+ String creds = EntityTestUtils.assertAttributeEventuallyNonNull(entity, CreateUserPolicy.VM_USER_CREDENTIALS);
+ Pattern pattern = Pattern.compile("(.*) : (.*) @ (.*):(.*)");
+ Matcher matcher = pattern.matcher(creds);
+ assertTrue(matcher.matches());
+ String username2 = matcher.group(1).trim();
+ String password = matcher.group(2).trim();
+ String hostname = matcher.group(3).trim();
+ String port = matcher.group(4).trim();
+
+ assertEquals(newUsername, username2);
+ assertEquals(hostname, "1.2.3.4");
+ assertEquals(password.length(), 12);
+ assertEquals(port, "1234");
+
+ boolean found = false;
+ for (List<String> cmds : RecordingSshMachineLocation.execScriptCalls) {
+ if (cmds.toString().contains("useradd")) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue(found, "useradd not found in: "+RecordingSshMachineLocation.execScriptCalls);
+ }
+}
[2/2] incubator-brooklyn git commit: This closes #598
Posted by al...@apache.org.
This closes #598
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/10853fc0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/10853fc0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/10853fc0
Branch: refs/heads/master
Commit: 10853fc04f63ed42a2ee8e4a17ea1cd9830b755f
Parents: 4f6fa6c bea997b
Author: Aled Sage <al...@gmail.com>
Authored: Wed Apr 15 00:51:08 2015 -0500
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Apr 15 00:51:08 2015 -0500
----------------------------------------------------------------------
.../brooklyn/policy/os/CreateUserPolicy.java | 174 +++++++++++++++++++
.../policy/os/CreateUserPolicyLiveTest.java | 113 ++++++++++++
.../policy/os/CreateUserPolicyTest.java | 138 +++++++++++++++
3 files changed, 425 insertions(+)
----------------------------------------------------------------------