You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@whirr.apache.org by to...@apache.org on 2010/09/01 23:37:58 UTC

svn commit: r991712 - in /incubator/whirr/trunk: ./ cli/src/test/java/org/apache/whirr/cli/command/ core/src/main/java/org/apache/whirr/service/ services/cassandra/src/test/resources/ services/hadoop/src/main/java/org/apache/whirr/service/hadoop/ servi...

Author: tomwhite
Date: Wed Sep  1 21:37:57 2010
New Revision: 991712

URL: http://svn.apache.org/viewvc?rev=991712&view=rev
Log:
WHIRR-70. decouple keypairs from the files that hold them. Contributed by Adrian Cole.

Modified:
    incubator/whirr/trunk/CHANGES.txt
    incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/DestroyClusterCommandTest.java
    incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/LaunchClusterCommandTest.java
    incubator/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterSpec.java
    incubator/whirr/trunk/services/cassandra/src/test/resources/whirr-cassandra-test.properties
    incubator/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopProxy.java
    incubator/whirr/trunk/services/hadoop/src/test/resources/whirr-hadoop-test.properties
    incubator/whirr/trunk/services/zookeeper/src/test/resources/whirr-zookeeper-test.properties
    incubator/whirr/trunk/src/site/confluence/configuration-guide.confluence

Modified: incubator/whirr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/CHANGES.txt?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/CHANGES.txt (original)
+++ incubator/whirr/trunk/CHANGES.txt Wed Sep  1 21:37:57 2010
@@ -55,6 +55,8 @@ Trunk (unreleased changes)
 
     WHIRR-77. Document and implement release process. (tomwhite)
 
+    WHIRR-70. decouple keypairs from the files that hold them. (Adrian Cole via tomwhite)
+
   BUG FIXES
 
     WHIRR-50. Cassandra POM should depend on top-level. (tomwhite)

Modified: incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/DestroyClusterCommandTest.java
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/DestroyClusterCommandTest.java?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/DestroyClusterCommandTest.java (original)
+++ incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/DestroyClusterCommandTest.java Wed Sep  1 21:37:57 2010
@@ -25,9 +25,11 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.google.common.io.Files;
 import com.google.inject.internal.Lists;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.PrintStream;
 import java.util.Collections;
 
@@ -75,12 +77,23 @@ public class DestroyClusterCommandTest {
     
     DestroyClusterCommand command = new DestroyClusterCommand(factory);
     
+    File privateKeyFile = File.createTempFile("private", "key");
+    privateKeyFile.deleteOnExit();
+    Files.write("-----BEGIN RSA PRIVATE KEY-----".getBytes(),
+             privateKeyFile);
+    
+    File publicKeyFile = File.createTempFile("public", "key");
+    publicKeyFile.deleteOnExit();
+    Files.write("ssh-rsa".getBytes(), publicKeyFile);
+    
     int rc = command.run(null, out, null, Lists.newArrayList(
         "--service-name", "test-service",
         "--cluster-name", "test-cluster",
         "--provider", "rackspace",
         "--identity", "myusername", "--credential", "mypassword",
-        "--secret-key-file", "secret-key"));
+        "--private-key-file", privateKeyFile.getAbsolutePath(),
+        "--public-key-file", publicKeyFile.getAbsolutePath()
+        ));
     
     assertThat(rc, is(0));
 
@@ -90,7 +103,8 @@ public class DestroyClusterCommandTest {
     expectedClusterSpec.setIdentity("myusername");
     expectedClusterSpec.setCredential("mypassword");
     expectedClusterSpec.setClusterName("test-cluster");
-    expectedClusterSpec.setSecretKeyFile("secret-key");
+    expectedClusterSpec.setPrivateKey(privateKeyFile);
+    expectedClusterSpec.setPublicKey(publicKeyFile);
     
     verify(factory).create("test-service");
     

Modified: incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/LaunchClusterCommandTest.java
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/LaunchClusterCommandTest.java?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/LaunchClusterCommandTest.java (original)
+++ incubator/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/LaunchClusterCommandTest.java Wed Sep  1 21:37:57 2010
@@ -28,9 +28,12 @@ import static org.mockito.Mockito.verify
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.Sets;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
 import com.google.inject.internal.Lists;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.PrintStream;
 import java.util.Collections;
 
@@ -82,13 +85,24 @@ public class LaunchClusterCommandTest {
     
     LaunchClusterCommand command = new LaunchClusterCommand(factory);
     
+    File privateKeyFile = File.createTempFile("private", "key");
+    privateKeyFile.deleteOnExit();
+    Files.write("-----BEGIN RSA PRIVATE KEY-----".getBytes(),
+             privateKeyFile);
+    
+    File publicKeyFile = File.createTempFile("public", "key");
+    publicKeyFile.deleteOnExit();
+    Files.write("ssh-rsa".getBytes(), publicKeyFile);
+    
     int rc = command.run(null, out, null, Lists.newArrayList(
         "--service-name", "test-service",
         "--cluster-name", "test-cluster",
         "--instance-templates", "1 role1+role2,2 role3",
         "--provider", "rackspace",
         "--identity", "myusername", "--credential", "mypassword",
-        "--secret-key-file", "secret-key"));
+        "--private-key-file", privateKeyFile.getAbsolutePath(),
+        "--public-key-file", publicKeyFile.getAbsolutePath()
+        ));
     
     assertThat(rc, is(0));
 
@@ -102,7 +116,8 @@ public class LaunchClusterCommandTest {
     expectedClusterSpec.setIdentity("myusername");
     expectedClusterSpec.setCredential("mypassword");
     expectedClusterSpec.setClusterName("test-cluster");
-    expectedClusterSpec.setSecretKeyFile("secret-key");
+    expectedClusterSpec.setPrivateKey(privateKeyFile);
+    expectedClusterSpec.setPublicKey(publicKeyFile);
     
     verify(factory).create("test-service");
     

Modified: incubator/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterSpec.java
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterSpec.java?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterSpec.java (original)
+++ incubator/whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterSpec.java Wed Sep  1 21:37:57 2010
@@ -18,7 +18,11 @@
 
 package org.apache.whirr.service;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.http.Payloads.newStringPayload;
+import static org.jclouds.http.Payloads.newFilePayload;
+import static org.jclouds.util.Utils.toStringAndClose;
 
 import com.google.common.base.Charsets;
 import com.google.common.base.Objects;
@@ -32,7 +36,9 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
+import org.jclouds.http.Payload;
 
 /**
  * This class represents the specification of a cluster. It is used to describe
@@ -47,7 +53,8 @@ public class ClusterSpec {
     CREDENTIAL(String.class, false),
     IDENTITY(String.class, false),
     CLUSTER_NAME(String.class, false),
-    SECRET_KEY_FILE(String.class, false),
+    PUBLIC_KEY_FILE(String.class, false),
+    PRIVATE_KEY_FILE(String.class, false),
     CLIENT_CIDRS(String.class, true);
     
     private Class<?> type;
@@ -136,11 +143,17 @@ public class ClusterSpec {
   private String identity;
   private String credential;
   private String clusterName;
-  private String secretKeyFile;
+  private Payload privateKey;
+  private Payload publicKey;
   private List<String> clientCidrs = Lists.newArrayList();
   private Configuration config = new PropertiesConfiguration();
   
-  public static ClusterSpec fromConfiguration(Configuration config) {
+  /**
+   * 
+   * @throws ConfigurationException if the public or private key cannot be read.
+   */
+  public static ClusterSpec fromConfiguration(Configuration config)
+  throws ConfigurationException {
     ClusterSpec spec = new ClusterSpec();
     spec.setServiceName(config.getString(Property.SERVICE_NAME.getConfigName()));
     spec.setInstanceTemplates(InstanceTemplate.parse(
@@ -150,7 +163,20 @@ public class ClusterSpec {
         config.getString(Property.IDENTITY.getConfigName()), Property.IDENTITY));
     spec.setCredential(config.getString(Property.CREDENTIAL.getConfigName()));
     spec.setClusterName(config.getString(Property.CLUSTER_NAME.getConfigName()));
-    spec.setSecretKeyFile(config.getString(Property.SECRET_KEY_FILE.getConfigName()));
+    try {
+      String privateKeyPath = config.getString(
+          Property.PRIVATE_KEY_FILE.getConfigName());
+      if (privateKeyPath != null)
+        spec.setPrivateKey(new File(privateKeyPath));
+      String publicKeyPath = config.getString(Property.PUBLIC_KEY_FILE.
+               getConfigName());
+      publicKeyPath = publicKeyPath == null && privateKeyPath != null ?
+                privateKeyPath + ".pub" : publicKeyPath;
+      if (publicKeyPath != null)
+        spec.setPublicKey(new File(publicKeyPath));
+    } catch (IOException e) {
+      throw new ConfigurationException("error reading key from file", e);
+    }
     spec.setClientCidrs(config.getList(Property.CLIENT_CIDRS.getConfigName()));
     spec.config = config;
     return spec;
@@ -188,8 +214,25 @@ public class ClusterSpec {
   public String getClusterName() {
     return clusterName;
   }
-  public String getSecretKeyFile() {
-    return secretKeyFile;
+  public Payload getPrivateKey() {
+    return privateKey;
+  }
+  public Payload getPublicKey() {
+    return publicKey;
+  }
+  /**
+   * @see #getPrivateKey
+   * @throws IOException if the payload cannot be read
+   */
+  public String readPrivateKey() throws IOException {
+    return toStringAndClose(getPrivateKey().getContent());
+  }
+  /**
+   * @see #getPublicKey
+   * @throws IOException if the payload cannot be read
+   */
+  public String readPublicKey() throws IOException {
+    return toStringAndClose(getPublicKey().getContent());
   }
   public List<String> getClientCidrs() {
     return clientCidrs;
@@ -219,8 +262,50 @@ public class ClusterSpec {
     this.clusterName = clusterName;
   }
 
-  public void setSecretKeyFile(String secretKeyFile) {
-    this.secretKeyFile = secretKeyFile;
+  /**
+   * The rsa public key which is authorized to login to your on the cloud nodes.
+   * 
+   * @param publicKey
+   */
+  public void setPublicKey(String publicKey) {
+    checkArgument(checkNotNull(publicKey, "publicKey").startsWith("ssh-rsa"),
+        "key should start with ssh-rsa");
+    this.publicKey = newStringPayload(publicKey);
+  }
+  
+  /**
+   * 
+   * @throws IOException
+   *           if there is a problem reading the file
+   * @see #setPublicKey(String)
+   */
+  public void setPublicKey(File publicKey) throws IOException {
+    this.publicKey = newFilePayload(checkNotNull(publicKey,
+        "publicKey"));
+  }
+
+  /**
+   * The rsa private key which is used as the login identity on the cloud 
+   * nodes.
+   * 
+   * @param privateKey
+   */
+  public void setPrivateKey(String privateKey) {
+    checkArgument(checkNotNull(privateKey, "privateKey")
+        .startsWith("-----BEGIN RSA PRIVATE KEY-----"),
+        "key should start with -----BEGIN RSA PRIVATE KEY-----");
+    this.privateKey = newStringPayload(privateKey);
+  }
+
+  /**
+   * 
+   * @throws IOException
+   *           if there is a problem reading the file
+   * @see #setPrivateKey(String)
+   */
+  public void setPrivateKey(File privateKey) throws IOException {
+    this.privateKey = newFilePayload(
+        checkNotNull(privateKey, "privateKey"));
   }
 
   public void setClientCidrs(List<String> clientCidrs) {
@@ -232,15 +317,7 @@ public class ClusterSpec {
   public Configuration getConfiguration() {
     return config;
   }
-  
-  public String readPrivateKey() throws IOException {
-    return Files.toString(new File(getSecretKeyFile()), Charsets.UTF_8);
-  }
     
-  public String readPublicKey() throws IOException {
-    return Files.toString(new File(getSecretKeyFile() + ".pub"), Charsets.UTF_8);
-  }
-  
   public boolean equals(Object o) {
     if (o instanceof ClusterSpec) {
       ClusterSpec that = (ClusterSpec) o;
@@ -250,7 +327,6 @@ public class ClusterSpec {
         && Objects.equal(identity, that.identity)
         && Objects.equal(credential, that.credential)
         && Objects.equal(clusterName, that.clusterName)
-        && Objects.equal(secretKeyFile, that.secretKeyFile)
         && Objects.equal(clientCidrs, that.clientCidrs);
     }
     return false;
@@ -258,8 +334,8 @@ public class ClusterSpec {
   
   public int hashCode() {
     return Objects.hashCode(instanceTemplates, serviceName,
-        provider, identity, credential, clusterName, secretKeyFile,
-        clientCidrs);
+        provider, identity, credential, clusterName, publicKey,
+        privateKey, clientCidrs);
   }
   
   public String toString() {
@@ -270,7 +346,8 @@ public class ClusterSpec {
       .add("identity", identity)
       .add("credential", credential)
       .add("clusterName", clusterName)
-      .add("secretKeyFile", secretKeyFile)
+      .add("publicKey", publicKey)
+      .add("privateKey", privateKey)
       .add("clientCidrs", clientCidrs)
       .toString();
   }

Modified: incubator/whirr/trunk/services/cassandra/src/test/resources/whirr-cassandra-test.properties
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/services/cassandra/src/test/resources/whirr-cassandra-test.properties?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/services/cassandra/src/test/resources/whirr-cassandra-test.properties (original)
+++ incubator/whirr/trunk/services/cassandra/src/test/resources/whirr-cassandra-test.properties Wed Sep  1 21:37:57 2010
@@ -21,4 +21,4 @@ whirr.instance-templates=2 cassandra
 whirr.provider=ec2
 whirr.identity=${sys:whirr.test.identity}
 whirr.credential=${sys:whirr.test.credential}
-whirr.secret-key-file=${sys:user.home}/.ssh/id_rsa
+whirr.private-key-file=${sys:user.home}/.ssh/id_rsa

Modified: incubator/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopProxy.java
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopProxy.java?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopProxy.java (original)
+++ incubator/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopProxy.java Wed Sep  1 21:37:57 2010
@@ -18,13 +18,18 @@
 
 package org.apache.whirr.service.hadoop;
 
+import static com.google.common.base.Preconditions.checkState;
+
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 
 import org.apache.whirr.service.ClusterSpec;
 
 import com.google.common.collect.Iterables;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
 
 public class HadoopProxy {
 
@@ -40,11 +45,19 @@ public class HadoopProxy {
   public void start() throws IOException {
     // jsch doesn't support SOCKS-based dynamic port forwarding, so we need to shell out...
     // TODO: Use static port forwarding instead?
-    String identityFile = clusterSpec.getSecretKeyFile();
+    checkState(clusterSpec.getPrivateKey() != null, "privateKey is needed");
+    File identity;
+    if (clusterSpec.getPrivateKey().getRawContent() instanceof File) {
+      identity = File.class.cast(clusterSpec.getPrivateKey().getRawContent());
+    } else {
+      identity = File.createTempFile("hadoop", "key");
+      identity.deleteOnExit();
+      Files.write(ByteStreams.toByteArray(clusterSpec.getPrivateKey().getContent()), identity);
+    }
     String user = Iterables.get(cluster.getInstances(), 0).getLoginCredentials().account;
     String server = cluster.getNamenodePublicAddress().getHostName();
     String[] command = new String[] { "ssh",
-      "-i", identityFile,
+      "-i", identity.getAbsolutePath(),
       "-o", "ConnectTimeout=10",
       "-o", "ServerAliveInterval=60",
       "-o", "StrictHostKeyChecking=no",

Modified: incubator/whirr/trunk/services/hadoop/src/test/resources/whirr-hadoop-test.properties
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/services/hadoop/src/test/resources/whirr-hadoop-test.properties?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/services/hadoop/src/test/resources/whirr-hadoop-test.properties (original)
+++ incubator/whirr/trunk/services/hadoop/src/test/resources/whirr-hadoop-test.properties Wed Sep  1 21:37:57 2010
@@ -21,4 +21,4 @@ whirr.instance-templates=1 jt+nn,1 dn+tt
 whirr.provider=ec2
 whirr.identity=${sys:whirr.test.identity}
 whirr.credential=${sys:whirr.test.credential}
-whirr.secret-key-file=${sys:user.home}/.ssh/id_rsa
+whirr.private-key-file=${sys:user.home}/.ssh/id_rsa

Modified: incubator/whirr/trunk/services/zookeeper/src/test/resources/whirr-zookeeper-test.properties
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/services/zookeeper/src/test/resources/whirr-zookeeper-test.properties?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/services/zookeeper/src/test/resources/whirr-zookeeper-test.properties (original)
+++ incubator/whirr/trunk/services/zookeeper/src/test/resources/whirr-zookeeper-test.properties Wed Sep  1 21:37:57 2010
@@ -21,4 +21,4 @@ whirr.instance-templates=2 zk
 whirr.provider=ec2
 whirr.identity=${sys:whirr.test.identity}
 whirr.credential=${sys:whirr.test.credential}
-whirr.secret-key-file=${sys:user.home}/.ssh/id_rsa
+whirr.private-key-file=${sys:user.home}/.ssh/id_rsa

Modified: incubator/whirr/trunk/src/site/confluence/configuration-guide.confluence
URL: http://svn.apache.org/viewvc/incubator/whirr/trunk/src/site/confluence/configuration-guide.confluence?rev=991712&r1=991711&r2=991712&view=diff
==============================================================================
--- incubator/whirr/trunk/src/site/confluence/configuration-guide.confluence (original)
+++ incubator/whirr/trunk/src/site/confluence/configuration-guide.confluence Wed Sep  1 21:37:57 2010
@@ -33,7 +33,7 @@ h2. Comparison
 | {{cloud-provider}} | {{provider}} | |
 | none | {{identity}} | Specified using environment variables for Python. E.g. {{AWS\_ACCESS\_KEY\_ID}}, {{RACKSPACE\_KEY}} |
 | none | {{credential}} | Specified using environment variables for Python. E.g. {{AWS\_ACCESS\_KEY\_ID}}, {{RACKSPACE\_SECRET}} |
-| {{private-key-file}} | {{secret-key-file}} | |
+| {{private-key-file}} | {{private-key-file}} | |
 | {{client-cidr}} | {{client-cidrs}} | Python {{client-cidr}} option may be repeated multiple times, whereas Java {{client-cidrs}} contains comma-separated CIDRs. |
 | {{public-key}} | none | Based on secret key in Java (add {{.pub}}). |
 | {{image-id}} | none | Need to add to Java. Along with more flexible image matching (pattern, OS, arch). See jclouds TemplateBuilder. |