Issue 719: add sha1 support to SshKeys

This commit is contained in:
Adrian Cole 2011-10-13 23:57:46 -07:00
parent bf4db76152
commit 7e5a6e68cf
14 changed files with 483 additions and 224 deletions

View File

@ -30,6 +30,7 @@ import javax.inject.Singleton;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.options.TemplateOptions;
import static org.jclouds.crypto.SshKeys.*;
import org.jclouds.ec2.compute.domain.RegionAndName;
import org.jclouds.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
@ -58,8 +59,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
@Inject
public CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions(Cache<RegionAndName, KeyPair> credentialsMap,
@Named("SECURITY") Cache<RegionAndName, String> securityGroupMap,
Provider<RunInstancesOptions> optionsProvider) {
@Named("SECURITY") Cache<RegionAndName, String> securityGroupMap,
Provider<RunInstancesOptions> optionsProvider) {
this.credentialsMap = checkNotNull(credentialsMap, "credentialsMap");
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
this.optionsProvider = checkNotNull(optionsProvider, "optionsProvider");
@ -83,10 +84,10 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
instanceOptions.withUserData(userData);
Set<BlockDeviceMapping> blockDeviceMappings = EC2TemplateOptions.class.cast(template.getOptions())
.getBlockDeviceMappings();
.getBlockDeviceMappings();
if (blockDeviceMappings.size() > 0) {
checkState("ebs".equals(template.getImage().getUserMetadata().get("rootDeviceType")),
"BlockDeviceMapping only available on ebs boot");
"BlockDeviceMapping only available on ebs boot");
instanceOptions.withBlockDeviceMappings(blockDeviceMappings);
}
}
@ -102,30 +103,32 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
public String createNewKeyPairUnlessUserSpecifiedOtherwise(String region, String group, TemplateOptions options) {
String keyPairName = null;
boolean shouldAutomaticallyCreateKeyPair = true;
if (options instanceof EC2TemplateOptions) {
keyPairName = EC2TemplateOptions.class.cast(options).getKeyPair();
if (keyPairName == null)
shouldAutomaticallyCreateKeyPair = EC2TemplateOptions.class.cast(options)
.shouldAutomaticallyCreateKeyPair();
.shouldAutomaticallyCreateKeyPair();
}
if (keyPairName == null && shouldAutomaticallyCreateKeyPair) {
keyPairName = createOrImportKeyPair(region, group, options);
} else if (keyPairName != null) {
if (options.getOverridingCredentials() != null && options.getOverridingCredentials().credential != null) {
KeyPair keyPair = KeyPair.builder().region(region).keyName(keyPairName).keyFingerprint("//TODO")
.keyMaterial(options.getOverridingCredentials().credential).build();
String pem = options.getOverridingCredentials().credential;
KeyPair keyPair = KeyPair.builder().region(region).keyName(keyPairName).fingerprint(
fingerprintPrivateKey(pem)).sha1OfPrivateKey(sha1PrivateKey(pem)).keyMaterial(pem).build();
RegionAndName key = new RegionAndName(region, keyPairName);
credentialsMap.asMap().put(key, keyPair);
}
}
if (options.getRunScript() != null) {
RegionAndName regionAndName = new RegionAndName(region, keyPairName);
String message = String.format("no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)",
regionAndName);
String message = String
.format(
"no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)",
regionAndName);
// test to see if this is in cache.
try {
credentialsMap.getUnchecked(regionAndName);
@ -152,14 +155,14 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptions {
groups.add(markerGroup);
RegionNameAndIngressRules regionNameAndIngessRulesForMarkerGroup;
if (userSpecifiedTheirOwnGroups(options)) {
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, new int[] {},
false);
false);
groups.addAll(EC2TemplateOptions.class.cast(options).getGroups());
} else {
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup,
options.getInboundPorts(), true);
regionNameAndIngessRulesForMarkerGroup = new RegionNameAndIngressRules(region, markerGroup, options
.getInboundPorts(), true);
}
// this will create if not yet exists.
securityGroupMap.getUnchecked(regionNameAndIngessRulesForMarkerGroup);

View File

@ -20,6 +20,7 @@ package org.jclouds.ec2.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.crypto.SshKeys;
import org.jclouds.javax.annotation.Nullable;
/**
@ -32,8 +33,8 @@ import org.jclouds.javax.annotation.Nullable;
public class KeyPair implements Comparable<KeyPair> {
@Override
public String toString() {
return "[region=" + region + ", keyName=" + keyName + ", keyFingerprint=" + keyFingerprint + ", keyMaterial?="
+ (keyMaterial != null) + "]";
return "[region=" + region + ", keyName=" + keyName + ", fingerprint=" + fingerprint + ", sha1OfPrivateKey="
+ sha1OfPrivateKey + ", keyMaterial?=" + (keyMaterial != null) + "]";
}
public static Builder builder() {
@ -43,7 +44,8 @@ public class KeyPair implements Comparable<KeyPair> {
public static class Builder {
private String region;
private String keyName;
private String keyFingerprint;
private String fingerprint;
private String sha1OfPrivateKey;
private String keyMaterial;
public Builder region(String region) {
@ -56,8 +58,8 @@ public class KeyPair implements Comparable<KeyPair> {
return this;
}
public Builder keyFingerprint(String keyFingerprint) {
this.keyFingerprint = keyFingerprint;
public Builder sha1OfPrivateKey(String sha1OfPrivateKey) {
this.sha1OfPrivateKey = sha1OfPrivateKey;
return this;
}
@ -66,27 +68,38 @@ public class KeyPair implements Comparable<KeyPair> {
return this;
}
public Builder fingerprint(String fingerprint) {
this.fingerprint = fingerprint;
return this;
}
public KeyPair build() {
return new KeyPair(region, keyName, keyFingerprint, keyMaterial);
if (fingerprint == null && keyMaterial != null)
fingerprint(SshKeys.fingerprintPrivateKey(keyMaterial));
return new KeyPair(region, keyName, sha1OfPrivateKey, keyMaterial, fingerprint);
}
public static Builder fromKeyPair(KeyPair in) {
return new Builder().region(in.getRegion()).keyName(in.getKeyName()).keyFingerprint(in.getKeyFingerprint())
.keyMaterial(in.getKeyMaterial());
return new Builder().region(in.getRegion()).keyName(in.getKeyName()).sha1OfPrivateKey(in.getKeyFingerprint())
.keyMaterial(in.getKeyMaterial());
}
}
private final String region;
private final String keyName;
private final String keyFingerprint;
private final String sha1OfPrivateKey;
@Nullable
private final String keyMaterial;
@Nullable
private final String fingerprint;
public KeyPair(String region, String keyName, String keyFingerprint, @Nullable String keyMaterial) {
public KeyPair(String region, String keyName, String sha1OfPrivateKey, @Nullable String keyMaterial,
@Nullable String fingerprint) {
this.region = checkNotNull(region, "region");
this.keyName = checkNotNull(keyName, "keyName");
this.keyFingerprint = checkNotNull(keyFingerprint, "keyFingerprint");
this.sha1OfPrivateKey = checkNotNull(sha1OfPrivateKey, "sha1OfPrivateKey");
this.keyMaterial = keyMaterial;// nullable on list
this.fingerprint = fingerprint;// nullable on list
}
/**
@ -104,10 +117,30 @@ public class KeyPair implements Comparable<KeyPair> {
}
/**
* A SHA-1 digest of the DER encoded private key.
* @see #getSha1OfPrivateKey
*/
@Deprecated
public String getKeyFingerprint() {
return keyFingerprint;
return sha1OfPrivateKey;
}
/**
* A SHA-1 digest of the DER encoded private key.
*
* @see SshKeys#sha1
*/
public String getSha1OfPrivateKey() {
return sha1OfPrivateKey;
}
/**
* fingerprint per the following <a
* href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00" >spec</a>
*
* @see SshKeys#fingerprint
*/
public String getFingerprint() {
return fingerprint;
}
/**
@ -128,10 +161,11 @@ public class KeyPair implements Comparable<KeyPair> {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((keyFingerprint == null) ? 0 : keyFingerprint.hashCode());
result = prime * result + ((fingerprint == null) ? 0 : fingerprint.hashCode());
result = prime * result + ((keyMaterial == null) ? 0 : keyMaterial.hashCode());
result = prime * result + ((keyName == null) ? 0 : keyName.hashCode());
result = prime * result + ((region == null) ? 0 : region.hashCode());
result = prime * result + ((sha1OfPrivateKey == null) ? 0 : sha1OfPrivateKey.hashCode());
return result;
}
@ -144,10 +178,10 @@ public class KeyPair implements Comparable<KeyPair> {
if (getClass() != obj.getClass())
return false;
KeyPair other = (KeyPair) obj;
if (keyFingerprint == null) {
if (other.keyFingerprint != null)
if (fingerprint == null) {
if (other.fingerprint != null)
return false;
} else if (!keyFingerprint.equals(other.keyFingerprint))
} else if (!fingerprint.equals(other.fingerprint))
return false;
if (keyMaterial == null) {
if (other.keyMaterial != null)
@ -164,6 +198,11 @@ public class KeyPair implements Comparable<KeyPair> {
return false;
} else if (!region.equals(other.region))
return false;
if (sha1OfPrivateKey == null) {
if (other.sha1OfPrivateKey != null)
return false;
} else if (!sha1OfPrivateKey.equals(other.sha1OfPrivateKey))
return false;
return true;
}

View File

@ -18,12 +18,15 @@
*/
package org.jclouds.ec2.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import java.util.Set;
import javax.inject.Inject;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.KeyPair.Builder;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.location.Region;
@ -36,16 +39,18 @@ import com.google.common.collect.Sets;
* />
* @author Adrian Cole
*/
public class DescribeKeyPairsResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<Set<KeyPair>> {
public class DescribeKeyPairsResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Set<KeyPair>> {
private final String defaultRegion;
private Builder builder;
@Inject
@Region
String defaultRegion;
public DescribeKeyPairsResponseHandler(@Region String defaultRegion) {
this.defaultRegion = defaultRegion;
builder = KeyPair.builder().region(defaultRegion);
}
private StringBuilder currentText = new StringBuilder();
private Set<KeyPair> keyPairs = Sets.newLinkedHashSet();
private String keyFingerprint;
private String keyName;
public Set<KeyPair> getResult() {
return keyPairs;
@ -54,16 +59,19 @@ public class DescribeKeyPairsResponseHandler extends
public void endElement(String uri, String name, String qName) {
if (qName.equals("keyFingerprint")) {
this.keyFingerprint = currentText.toString().trim();
builder.sha1OfPrivateKey(currentOrNull(currentText));
} else if (qName.equals("item")) {
String region = AWSUtils.findRegionInArgsOrNull(getRequest());
if (region == null)
region = defaultRegion;
keyPairs.add(new KeyPair(region, keyName, keyFingerprint, null));
if (region != null)
builder.region(region);
try {
keyPairs.add(builder.build());
} finally {
builder = KeyPair.builder().region(defaultRegion);
}
} else if (qName.equals("keyName")) {
this.keyName = currentText.toString().trim();
builder.keyName(currentOrNull(currentText));
}
currentText = new StringBuilder();
}

View File

@ -18,10 +18,13 @@
*/
package org.jclouds.ec2.xml;
import static org.jclouds.util.SaxUtils.currentOrNull;
import javax.inject.Inject;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.ec2.domain.KeyPair.Builder;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.location.Region;
@ -33,31 +36,36 @@ import org.jclouds.location.Region;
* @author Adrian Cole
*/
public class KeyPairResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<KeyPair> {
private final String defaultRegion;
private Builder builder;
@Inject
@Region
String defaultRegion;
public KeyPairResponseHandler(@Region String defaultRegion) {
this.defaultRegion = defaultRegion;
builder = KeyPair.builder().region(defaultRegion);
}
private StringBuilder currentText = new StringBuilder();
private String keyFingerprint;
private String keyMaterial;
private String keyName;
public KeyPair getResult() {
String region = AWSUtils.findRegionInArgsOrNull(getRequest());
if (region == null)
region = defaultRegion;
return new KeyPair(region, keyName, keyFingerprint, keyMaterial);
if (region != null)
builder.region(region);
try {
return builder.build();
} finally {
builder = KeyPair.builder().region(defaultRegion);
}
}
public void endElement(String uri, String name, String qName) {
if (qName.equals("keyFingerprint")) {
this.keyFingerprint = currentText.toString().trim();
builder.sha1OfPrivateKey(currentOrNull(currentText));
} else if (qName.equals("keyMaterial")) {
this.keyMaterial = currentText.toString().trim();
builder.keyMaterial(currentOrNull(currentText));
} else if (qName.equals("keyName")) {
this.keyName = currentText.toString().trim();
builder.keyName(currentOrNull(currentText));
}
currentText = new StringBuilder();
}

View File

@ -57,6 +57,34 @@ import com.google.common.collect.ImmutableSet;
@Test(groups = "unit", singleThreaded = true, testName = "CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest")
public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public static final Credentials CREDENTIALS = new Credentials(null, "-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIIEowIBAAKCAQEA0CbFlhSdbMdad2ux2BVqk6Ut5fLKb0CdbqubGcEBfwsSz9Rp4Ile76P90MpV\n"
+ "W1BGKL5V4MO+flG6dZnRWPVmgrNVyDTmEsALiMGjfEwbACEZ1A8C6mPa36wWO7MlxuyMjg8OczTB\n"
+ "EXnHNDpxE5a6KowJtzFlmgjHk2Y+Q42UIqPx47lQUv5bdMDCnfNNomSzTVRjOZLUkDja+ybCKdux\n"
+ "gqTsuInhuBRMx+wxff8Z43ECdJV6UPoXK3der1dlZunxGCFkCeYq0kCX7FZ7PV35X744jqhD8P+7\n"
+ "y5prO4W+M3DWgChUx0OlbDbSHtDVlcfdbj/+4AKYKU6rQOqh+4DPDQIDAQABAoIBAHjQuEiXKJSV\n"
+ "1U2RZcVtENInws9AL/2I/Jfa5Qh6vTqXG9EjklywfzkK72x7tDVvD3ngmAoAs5WwLFDL+fXvYhOk\n"
+ "sbql8ZCahVdYRWME7XsSu2IZYHDZipXe1XzLS7b9X8uos5Ns4E8bZuNKtI1RJDdD1vPMqRNR2z0T\n"
+ "0Dn3eC7t+t+t7PWaK5AXu2ot7DoOeG1QhqJbwd5pMkIn2ydBILytgmDk/2P3EtJGePIJIeQBicmw\n"
+ "Z0KrJFa/K2cC8AtmMJUoZMo+mh1yemDbDLCZW30PjFHbZtcszS2cydAgq/HDFkZynvZG0zhbx/To\n"
+ "jzcNza1AyypYwOwb2/9/ulXZp0UCgYEA+QFgWDfYLH2zwjU5b6e0UbIyd/X/yRZ+L8lOEBd0Bbu8\n"
+ "qO3txaDbwi7o2mG7pJENHJ3u62CHjgTGDNW9V9Q8eNoGtj3uHvMvi7FdDEK8B6izdZyR7hmZmQ/5\n"
+ "MIldelyiGZlz1KBSoy4FsCpA7hV7cI6H6x+Im24NxG90/wd/EgMCgYEA1f+cUyUisIO3yKOCf0hQ\n"
+ "aL289q2//F2cbvBxtki6I8JzTg1H3oTO2WVrXQeCA3a/yiuRUatgGH4mxrpCF6byVJyqrEWAj4kU\n"
+ "uTbhMgIYhLGoaF1e+vMirCRXUXox0i5X976ASzHn64V9JSd1B+UbKfpcFTYYnChmrRDzmhKN1a8C\n"
+ "gYBTvIHAyO7ab18/BRUOllAOVSWhr8lXv0eqHEEzKh/rOaoFCRY3qpOcZpgJsGogumK1Z+sLnoeX\n"
+ "W8WaVVp6KbY4UeGF8aedItyvVnLbB6ohzTqkZ4Wvk05S6cs75kXYO0SL5U3NiCiiFXz2NA9nwTOk\n"
+ "s1nD2PPgiQ76Kx0mEkhKLwKBgFhHEJqv+AZu37Kx2NRe5WS/2KK9/DPD/hM5tv7mM3sq7Nvm2J3v\n"
+ "lVDS6J5AyZ5aLzXcER9qncKcz6wtC7SsFs1Wr4VPSoBroRPikrVJbgnXK8yZr+O/xq7Scv7WdJTq\n"
+ "rzkw6cWbObvLnltkUn/GQBVqBPBvF2nbtLdyBbuqKb5bAoGBAI1+aoJnvXEXxT4UHrMkQcY0eXRz\n"
+ "3UdbzJmtjMW9CR6l9s11mV6PcZP4qnODp3nd6a+lPeL3wVYQ47DsTJ/Bx5dI17zA5mU57n6mV0a3\n"
+ "DbSoPKSdaKTQdo2THnVE9P9sPKZWueAcsE4Yw/qcTjoxrtUnAH/AXN250v0tkKIOvMhu\n"
+ "-----END RSA PRIVATE KEY-----");
public static final KeyPair KEYPAIR = KeyPair.builder().region(Region.AP_SOUTHEAST_1).keyName("myKeyPair")
.sha1OfPrivateKey("13:36:74:b9:56:bb:07:96:c0:19:ab:00:7f:9f:06:d2:16:a0:45:32").fingerprint(
"60:15:d1:f1:d9:e2:3c:e2:ee:a9:64:6a:42:a7:34:0c").keyMaterial(CREDENTIALS.credential).build();
private static final Provider<RunInstancesOptions> OPTIONS_PROVIDER = new javax.inject.Provider<RunInstancesOptions>() {
@Override
@ -69,7 +97,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testExecuteWithDefaultOptionsEC2() throws SecurityException, NoSuchMethodException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
Hardware size = EC2HardwareBuilder.m1_small32().build();
String systemGeneratedKeyPairName = "systemGeneratedKeyPair";
String generatedGroup = "group";
@ -95,9 +123,9 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(template.getHardware()).andReturn(size).atLeastOnce();
expect(template.getOptions()).andReturn(options).atLeastOnce();
expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet.<BlockDeviceMapping> of()).atLeastOnce();
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn(
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn(
systemGeneratedKeyPairName);
expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups);
expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups);
expect(options.getUserData()).andReturn(null);
// replay mocks
@ -106,7 +134,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replay(strategy);
// run
RunInstancesOptions customize = strategy.execute(region, tag, template);
RunInstancesOptions customize = strategy.execute(region, group, template);
assertEquals(customize.buildQueryParameters(), ImmutableMultimap.<String, String> of());
assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap.<String, String> of("InstanceType",
size.getProviderId(), "SecurityGroup.1", generatedGroup, "KeyName", systemGeneratedKeyPairName)
@ -124,7 +152,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testExecuteWithUserData() throws SecurityException, NoSuchMethodException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
Hardware size = EC2HardwareBuilder.m1_small32().build();
String systemGeneratedKeyPairName = "systemGeneratedKeyPair";
String generatedGroup = "group";
@ -150,9 +178,9 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(template.getHardware()).andReturn(size).atLeastOnce();
expect(template.getOptions()).andReturn(options).atLeastOnce();
expect(options.getBlockDeviceMappings()).andReturn(ImmutableSet.<BlockDeviceMapping> of()).atLeastOnce();
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options)).andReturn(
expect(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options)).andReturn(
systemGeneratedKeyPairName);
expect(strategy.getSecurityGroupsForTagAndOptions(region, tag, options)).andReturn(generatedGroups);
expect(strategy.getSecurityGroupsForTagAndOptions(region, group, options)).andReturn(generatedGroups);
expect(options.getUserData()).andReturn("hello".getBytes());
// replay mocks
@ -161,7 +189,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replay(strategy);
// run
RunInstancesOptions customize = strategy.execute(region, tag, template);
RunInstancesOptions customize = strategy.execute(region, group, template);
assertEquals(customize.buildQueryParameters(), ImmutableMultimap.<String, String> of());
assertEquals(customize.buildFormParameters().entries(), ImmutableMultimap.<String, String> of("InstanceType",
size.getProviderId(), "SecurityGroup.1", "group", "KeyName", systemGeneratedKeyPairName, "UserData",
@ -179,7 +207,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldTo() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = "myKeyPair";
// create mocks
@ -198,7 +226,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), userSuppliedKeyPair);
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), userSuppliedKeyPair);
// verify mocks
verify(options);
@ -210,7 +238,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptButNoCredentials() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = "myKeyPair";
// create mocks
@ -232,7 +260,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), userSuppliedKeyPair);
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), userSuppliedKeyPair);
// verify mocks
verify(options);
@ -243,7 +271,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptAndCredentialsAlreadyInMap() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = "myKeyPair";
// create mocks
@ -264,7 +292,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), userSuppliedKeyPair);
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), userSuppliedKeyPair);
// verify mocks
verify(options);
@ -276,7 +304,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_reusesKeyWhenToldToWithRunScriptAndCredentialsSpecified() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = "myKeyPair";
// create mocks
@ -287,11 +315,11 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
// setup expectations
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce();
expect(options.getOverridingCredentials()).andReturn(CREDENTIALS).atLeastOnce();
expect(strategy.credentialsMap.asMap()).andReturn(backing);
expect(
backing.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder().region(region).keyName(
userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())).andReturn(null);
// Notice that the fingerprint and sha1 generated
expect(backing.put(new RegionAndName(region, userSuppliedKeyPair), KEYPAIR)).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo"));
expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair);
@ -302,7 +330,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), userSuppliedKeyPair);
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), userSuppliedKeyPair);
// verify mocks
verify(options);
@ -315,7 +343,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
throws ExecutionException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = null;
boolean shouldAutomaticallyCreateKeyPair = true;
String systemGeneratedKeyPairName = "systemGeneratedKeyPair";
@ -329,7 +357,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.shouldAutomaticallyCreateKeyPair()).andReturn(shouldAutomaticallyCreateKeyPair);
expect(keyPair.getKeyName()).andReturn(systemGeneratedKeyPairName).atLeastOnce();
expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, tag))).andReturn(keyPair);
expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, group))).andReturn(keyPair);
expect(options.getRunScript()).andReturn(null);
// replay mocks
@ -338,7 +366,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options),
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options),
systemGeneratedKeyPairName);
// verify mocks
@ -350,7 +378,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testCreateNewKeyPairUnlessUserSpecifiedOtherwise_doesntCreateAKeyPairAndReturnsNullWhenToldNotTo() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String group = "group";
String userSuppliedKeyPair = null;
boolean shouldAutomaticallyCreateKeyPair = false; // here's the important
// part!
@ -371,7 +399,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, tag, options), null);
assertEquals(strategy.createNewKeyPairUnlessUserSpecifiedOtherwise(region, group, options), null);
// verify mocks
verify(options);
@ -383,8 +411,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
throws ExecutionException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1;
String group = "group";
String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1;
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
@ -399,14 +427,14 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
expect(options.getInboundPorts()).andReturn(ports).atLeastOnce();
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(tag);
expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(group);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal);
// verify mocks
verify(options);
@ -417,8 +445,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
throws ExecutionException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1;
String group = "group";
String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1;
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] { 22, 80 };
boolean shouldAuthorizeSelf = true;
@ -440,7 +468,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal);
// verify mocks
verify(options);
@ -451,8 +479,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
throws ExecutionException {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1;
String group = "group";
String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1;
Set<String> groupIds = ImmutableSet.<String> of();
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
@ -474,7 +502,7 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal);
// verify mocks
verify(options);
@ -484,8 +512,8 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
public void testGetSecurityGroupsForTagAndOptions_reusesGroupByDefaultWhenNoPortsAreSpecifiedWhenDoesExistAndAcceptsUserSuppliedGroups() {
// setup constants
String region = Region.AP_SOUTHEAST_1;
String tag = "tag";
String generatedMarkerGroup = "jclouds#tag#" + Region.AP_SOUTHEAST_1;
String group = "group";
String generatedMarkerGroup = "jclouds#group#" + Region.AP_SOUTHEAST_1;
Set<String> groupIds = ImmutableSet.<String> of("group1", "group2");
int[] ports = new int[] {};
boolean shouldAuthorizeSelf = true;
@ -501,14 +529,15 @@ public class CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest {
RegionNameAndIngressRules regionNameAndIngressRules = new RegionNameAndIngressRules(region, generatedMarkerGroup,
ports, shouldAuthorizeSelf);
expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules)).andReturn(groupExisted ? "tag" : null);
expect(strategy.securityGroupMap.getUnchecked(regionNameAndIngressRules))
.andReturn(groupExisted ? "group" : null);
// replay mocks
replay(options);
replayStrategy(strategy);
// run
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, tag, options), returnVal);
assertEquals(strategy.getSecurityGroupsForTagAndOptions(region, group, options), returnVal);
// verify mocks
verify(options);

View File

@ -39,7 +39,7 @@ import com.google.common.collect.ImmutableSet;
*
* @author Adrian Cole
*/
//NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
@Test(groups = "unit", testName = "DescribeKeyPairsResponseHandlerTest")
public class DescribeKeyPairsResponseHandlerTest extends BaseEC2HandlerTest {
public void testApplyInputStream() {
@ -47,10 +47,9 @@ public class DescribeKeyPairsResponseHandlerTest extends BaseEC2HandlerTest {
InputStream is = getClass().getResourceAsStream("/describe_keypairs.xml");
Set<KeyPair> expected = ImmutableSet.of(new KeyPair(defaultRegion, "gsg-keypair",
"1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", null));
"1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f", null, null));
DescribeKeyPairsResponseHandler handler = injector
.getInstance(DescribeKeyPairsResponseHandler.class);
DescribeKeyPairsResponseHandler handler = injector.getInstance(DescribeKeyPairsResponseHandler.class);
addDefaultRegionToHandler(handler);
Set<KeyPair> result = factory.create(handler).parse(is);
assertEquals(result, expected);
@ -58,7 +57,7 @@ public class DescribeKeyPairsResponseHandlerTest extends BaseEC2HandlerTest {
private void addDefaultRegionToHandler(ParseSax.HandlerWithResult<?> handler) {
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
expect(request.getArgs()).andReturn(ImmutableList.<Object>of());
expect(request.getArgs()).andReturn(ImmutableList.<Object> of());
replay(request);
handler.setContext(request);
}

View File

@ -23,8 +23,10 @@ import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import org.jclouds.crypto.SshKeys;
import org.jclouds.ec2.domain.KeyPair;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -37,51 +39,53 @@ import com.google.common.collect.ImmutableList;
*
* @author Adrian Cole
*/
//NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire
@Test(groups = "unit", testName = "KeyPairResponseHandlerTest")
public class KeyPairResponseHandlerTest extends BaseEC2HandlerTest {
public void testApplyInputStream() {
public void testApplyInputStream() throws IOException {
InputStream is = getClass().getResourceAsStream("/create_keypair.xml");
KeyPair expected = new KeyPair(
defaultRegion,
"gsg-keypair",
"1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f",
KeyPair expected = KeyPair.builder().region(defaultRegion).keyName("adriancole-ec21").sha1OfPrivateKey(
"13:36:74:b9:56:bb:07:96:c0:19:ab:00:7f:9f:06:d2:16:a0:45:32").keyMaterial(
"-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIIEoQIBAAKCAQBuLFg5ujHrtm1jnutSuoO8Xe56LlT+HM8v/xkaa39EstM3/aFxTHgElQiJLChp\n"
+ "HungXQ29VTc8rc1bW0lkdi23OH5eqkMHGhvEwqa0HWASUMll4o3o/IX+0f2UcPoKCOVUR+jx71Sg\n"
+ "5AU52EQfanIn3ZQ8lFW7Edp5a3q4DhjGlUKToHVbicL5E+g45zfB95wIyywWZfeW/UUF3LpGZyq/\n"
+ "ebIUlq1qTbHkLbCC2r7RTn8vpQWp47BGVYGtGSBMpTRP5hnbzzuqj3itkiLHjU39S2sJCJ0TrJx5\n"
+ "i8BygR4s3mHKBj8l+ePQxG1kGbF6R4yg6sECmXn17MRQVXODNHZbAgMBAAECggEAY1tsiUsIwDl5\n"
+ "91CXirkYGuVfLyLflXenxfI50mDFms/mumTqloHO7tr0oriHDR5K7wMcY/YY5YkcXNo7mvUVD1pM\n"
+ "ZNUJs7rw9gZRTrf7LylaJ58kOcyajw8TsC4e4LPbFaHwS1d6K8rXh64o6WgW4SrsB6ICmr1kGQI7\n"
+ "3wcfgt5ecIu4TZf0OE9IHjn+2eRlsrjBdeORi7KiUNC/pAG23I6MdDOFEQRcCSigCj+4/mciFUSA\n"
+ "SWS4dMbrpb9FNSIcf9dcLxVM7/6KxgJNfZc9XWzUw77Jg8x92Zd0fVhHOux5IZC+UvSKWB4dyfcI\n"
+ "tE8C3p9bbU9VGyY5vLCAiIb4qQKBgQDLiO24GXrIkswF32YtBBMuVgLGCwU9h9HlO9mKAc2m8Cm1\n"
+ "jUE5IpzRjTedc9I2qiIMUTwtgnw42auSCzbUeYMURPtDqyQ7p6AjMujp9EPemcSVOK9vXYL0Ptco\n"
+ "xW9MC0dtV6iPkCN7gOqiZXPRKaFbWADp16p8UAIvS/a5XXk5jwKBgQCKkpHi2EISh1uRkhxljyWC\n"
+ "iDCiK6JBRsMvpLbc0v5dKwP5alo1fmdR5PJaV2qvZSj5CYNpMAy1/EDNTY5OSIJU+0KFmQbyhsbm\n"
+ "rdLNLDL4+TcnT7c62/aH01ohYaf/VCbRhtLlBfqGoQc7+sAc8vmKkesnF7CqCEKDyF/dhrxYdQKB\n"
+ "gC0iZzzNAapayz1+JcVTwwEid6j9JqNXbBc+Z2YwMi+T0Fv/P/hwkX/ypeOXnIUcw0Ih/YtGBVAC\n"
+ "DQbsz7LcY1HqXiHKYNWNvXgwwO+oiChjxvEkSdsTTIfnK4VSCvU9BxDbQHjdiNDJbL6oar92UN7V\n"
+ "rBYvChJZF7LvUH4YmVpHAoGAbZ2X7XvoeEO+uZ58/BGKOIGHByHBDiXtzMhdJr15HTYjxK7OgTZm\n"
+ "gK+8zp4L9IbvLGDMJO8vft32XPEWuvI8twCzFH+CsWLQADZMZKSsBasOZ/h1FwhdMgCMcY+Qlzd4\n"
+ "JZKjTSu3i7vhvx6RzdSedXEMNTZWN4qlIx3kR5aHcukCgYA9T+Zrvm1F0seQPbLknn7EqhXIjBaT\n"
+ "P8TTvW/6bdPi23ExzxZn7KOdrfclYRph1LHMpAONv/x2xALIf91UB+v5ohy1oDoasL0gij1houRe\n"
+ "2ERKKdwz0ZL9SWq6VTdhr/5G994CK72fy5WhyERbDjUIdHaK3M849JJuf8cSrvSb4g==\n"
+ "-----END RSA PRIVATE KEY-----");
+ "MIIEowIBAAKCAQEA0CbFlhSdbMdad2ux2BVqk6Ut5fLKb0CdbqubGcEBfwsSz9Rp4Ile76P90MpV\n"
+ "W1BGKL5V4MO+flG6dZnRWPVmgrNVyDTmEsALiMGjfEwbACEZ1A8C6mPa36wWO7MlxuyMjg8OczTB\n"
+ "EXnHNDpxE5a6KowJtzFlmgjHk2Y+Q42UIqPx47lQUv5bdMDCnfNNomSzTVRjOZLUkDja+ybCKdux\n"
+ "gqTsuInhuBRMx+wxff8Z43ECdJV6UPoXK3der1dlZunxGCFkCeYq0kCX7FZ7PV35X744jqhD8P+7\n"
+ "y5prO4W+M3DWgChUx0OlbDbSHtDVlcfdbj/+4AKYKU6rQOqh+4DPDQIDAQABAoIBAHjQuEiXKJSV\n"
+ "1U2RZcVtENInws9AL/2I/Jfa5Qh6vTqXG9EjklywfzkK72x7tDVvD3ngmAoAs5WwLFDL+fXvYhOk\n"
+ "sbql8ZCahVdYRWME7XsSu2IZYHDZipXe1XzLS7b9X8uos5Ns4E8bZuNKtI1RJDdD1vPMqRNR2z0T\n"
+ "0Dn3eC7t+t+t7PWaK5AXu2ot7DoOeG1QhqJbwd5pMkIn2ydBILytgmDk/2P3EtJGePIJIeQBicmw\n"
+ "Z0KrJFa/K2cC8AtmMJUoZMo+mh1yemDbDLCZW30PjFHbZtcszS2cydAgq/HDFkZynvZG0zhbx/To\n"
+ "jzcNza1AyypYwOwb2/9/ulXZp0UCgYEA+QFgWDfYLH2zwjU5b6e0UbIyd/X/yRZ+L8lOEBd0Bbu8\n"
+ "qO3txaDbwi7o2mG7pJENHJ3u62CHjgTGDNW9V9Q8eNoGtj3uHvMvi7FdDEK8B6izdZyR7hmZmQ/5\n"
+ "MIldelyiGZlz1KBSoy4FsCpA7hV7cI6H6x+Im24NxG90/wd/EgMCgYEA1f+cUyUisIO3yKOCf0hQ\n"
+ "aL289q2//F2cbvBxtki6I8JzTg1H3oTO2WVrXQeCA3a/yiuRUatgGH4mxrpCF6byVJyqrEWAj4kU\n"
+ "uTbhMgIYhLGoaF1e+vMirCRXUXox0i5X976ASzHn64V9JSd1B+UbKfpcFTYYnChmrRDzmhKN1a8C\n"
+ "gYBTvIHAyO7ab18/BRUOllAOVSWhr8lXv0eqHEEzKh/rOaoFCRY3qpOcZpgJsGogumK1Z+sLnoeX\n"
+ "W8WaVVp6KbY4UeGF8aedItyvVnLbB6ohzTqkZ4Wvk05S6cs75kXYO0SL5U3NiCiiFXz2NA9nwTOk\n"
+ "s1nD2PPgiQ76Kx0mEkhKLwKBgFhHEJqv+AZu37Kx2NRe5WS/2KK9/DPD/hM5tv7mM3sq7Nvm2J3v\n"
+ "lVDS6J5AyZ5aLzXcER9qncKcz6wtC7SsFs1Wr4VPSoBroRPikrVJbgnXK8yZr+O/xq7Scv7WdJTq\n"
+ "rzkw6cWbObvLnltkUn/GQBVqBPBvF2nbtLdyBbuqKb5bAoGBAI1+aoJnvXEXxT4UHrMkQcY0eXRz\n"
+ "3UdbzJmtjMW9CR6l9s11mV6PcZP4qnODp3nd6a+lPeL3wVYQ47DsTJ/Bx5dI17zA5mU57n6mV0a3\n"
+ "DbSoPKSdaKTQdo2THnVE9P9sPKZWueAcsE4Yw/qcTjoxrtUnAH/AXN250v0tkKIOvMhu\n"
+ "-----END RSA PRIVATE KEY-----").build();
KeyPairResponseHandler handler = injector.getInstance(KeyPairResponseHandler.class);
addDefaultRegionToHandler(handler);
KeyPair result = factory.create(handler).parse(is);
assertEquals(result, expected);
assert SshKeys.privateKeyHasSha1(result.getKeyMaterial(), result.getSha1OfPrivateKey());
assert SshKeys.privateKeyHasFingerprint(result.getKeyMaterial(), result.getFingerprint());
}
private void addDefaultRegionToHandler(ParseSax.HandlerWithResult<?> handler) {
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
expect(request.getArgs()).andReturn(ImmutableList.<Object>of()).atLeastOnce();
expect(request.getArgs()).andReturn(ImmutableList.<Object> of()).atLeastOnce();
replay(request);
handler.setContext(request);
}

View File

@ -1,27 +1,28 @@
<CreateKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/2009-11-30/">
<keyName>gsg-keypair</keyName>
<keyFingerprint>1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f</keyFingerprint>
<keyMaterial>-----BEGIN RSA PRIVATE KEY-----
MIIEoQIBAAKCAQBuLFg5ujHrtm1jnutSuoO8Xe56LlT+HM8v/xkaa39EstM3/aFxTHgElQiJLChp
HungXQ29VTc8rc1bW0lkdi23OH5eqkMHGhvEwqa0HWASUMll4o3o/IX+0f2UcPoKCOVUR+jx71Sg
5AU52EQfanIn3ZQ8lFW7Edp5a3q4DhjGlUKToHVbicL5E+g45zfB95wIyywWZfeW/UUF3LpGZyq/
ebIUlq1qTbHkLbCC2r7RTn8vpQWp47BGVYGtGSBMpTRP5hnbzzuqj3itkiLHjU39S2sJCJ0TrJx5
i8BygR4s3mHKBj8l+ePQxG1kGbF6R4yg6sECmXn17MRQVXODNHZbAgMBAAECggEAY1tsiUsIwDl5
91CXirkYGuVfLyLflXenxfI50mDFms/mumTqloHO7tr0oriHDR5K7wMcY/YY5YkcXNo7mvUVD1pM
ZNUJs7rw9gZRTrf7LylaJ58kOcyajw8TsC4e4LPbFaHwS1d6K8rXh64o6WgW4SrsB6ICmr1kGQI7
3wcfgt5ecIu4TZf0OE9IHjn+2eRlsrjBdeORi7KiUNC/pAG23I6MdDOFEQRcCSigCj+4/mciFUSA
SWS4dMbrpb9FNSIcf9dcLxVM7/6KxgJNfZc9XWzUw77Jg8x92Zd0fVhHOux5IZC+UvSKWB4dyfcI
tE8C3p9bbU9VGyY5vLCAiIb4qQKBgQDLiO24GXrIkswF32YtBBMuVgLGCwU9h9HlO9mKAc2m8Cm1
jUE5IpzRjTedc9I2qiIMUTwtgnw42auSCzbUeYMURPtDqyQ7p6AjMujp9EPemcSVOK9vXYL0Ptco
xW9MC0dtV6iPkCN7gOqiZXPRKaFbWADp16p8UAIvS/a5XXk5jwKBgQCKkpHi2EISh1uRkhxljyWC
iDCiK6JBRsMvpLbc0v5dKwP5alo1fmdR5PJaV2qvZSj5CYNpMAy1/EDNTY5OSIJU+0KFmQbyhsbm
rdLNLDL4+TcnT7c62/aH01ohYaf/VCbRhtLlBfqGoQc7+sAc8vmKkesnF7CqCEKDyF/dhrxYdQKB
gC0iZzzNAapayz1+JcVTwwEid6j9JqNXbBc+Z2YwMi+T0Fv/P/hwkX/ypeOXnIUcw0Ih/YtGBVAC
DQbsz7LcY1HqXiHKYNWNvXgwwO+oiChjxvEkSdsTTIfnK4VSCvU9BxDbQHjdiNDJbL6oar92UN7V
rBYvChJZF7LvUH4YmVpHAoGAbZ2X7XvoeEO+uZ58/BGKOIGHByHBDiXtzMhdJr15HTYjxK7OgTZm
gK+8zp4L9IbvLGDMJO8vft32XPEWuvI8twCzFH+CsWLQADZMZKSsBasOZ/h1FwhdMgCMcY+Qlzd4
JZKjTSu3i7vhvx6RzdSedXEMNTZWN4qlIx3kR5aHcukCgYA9T+Zrvm1F0seQPbLknn7EqhXIjBaT
P8TTvW/6bdPi23ExzxZn7KOdrfclYRph1LHMpAONv/x2xALIf91UB+v5ohy1oDoasL0gij1houRe
2ERKKdwz0ZL9SWq6VTdhr/5G994CK72fy5WhyERbDjUIdHaK3M849JJuf8cSrvSb4g==
<CreateKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/2011-05-15/">
<requestId>bb5e3a3c-e254-454d-ba29-d607cdf6357a</requestId>
<keyName>adriancole-ec21</keyName>
<keyFingerprint>13:36:74:b9:56:bb:07:96:c0:19:ab:00:7f:9f:06:d2:16:a0:45:32</keyFingerprint>
<keyMaterial>-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA0CbFlhSdbMdad2ux2BVqk6Ut5fLKb0CdbqubGcEBfwsSz9Rp4Ile76P90MpV
W1BGKL5V4MO+flG6dZnRWPVmgrNVyDTmEsALiMGjfEwbACEZ1A8C6mPa36wWO7MlxuyMjg8OczTB
EXnHNDpxE5a6KowJtzFlmgjHk2Y+Q42UIqPx47lQUv5bdMDCnfNNomSzTVRjOZLUkDja+ybCKdux
gqTsuInhuBRMx+wxff8Z43ECdJV6UPoXK3der1dlZunxGCFkCeYq0kCX7FZ7PV35X744jqhD8P+7
y5prO4W+M3DWgChUx0OlbDbSHtDVlcfdbj/+4AKYKU6rQOqh+4DPDQIDAQABAoIBAHjQuEiXKJSV
1U2RZcVtENInws9AL/2I/Jfa5Qh6vTqXG9EjklywfzkK72x7tDVvD3ngmAoAs5WwLFDL+fXvYhOk
sbql8ZCahVdYRWME7XsSu2IZYHDZipXe1XzLS7b9X8uos5Ns4E8bZuNKtI1RJDdD1vPMqRNR2z0T
0Dn3eC7t+t+t7PWaK5AXu2ot7DoOeG1QhqJbwd5pMkIn2ydBILytgmDk/2P3EtJGePIJIeQBicmw
Z0KrJFa/K2cC8AtmMJUoZMo+mh1yemDbDLCZW30PjFHbZtcszS2cydAgq/HDFkZynvZG0zhbx/To
jzcNza1AyypYwOwb2/9/ulXZp0UCgYEA+QFgWDfYLH2zwjU5b6e0UbIyd/X/yRZ+L8lOEBd0Bbu8
qO3txaDbwi7o2mG7pJENHJ3u62CHjgTGDNW9V9Q8eNoGtj3uHvMvi7FdDEK8B6izdZyR7hmZmQ/5
MIldelyiGZlz1KBSoy4FsCpA7hV7cI6H6x+Im24NxG90/wd/EgMCgYEA1f+cUyUisIO3yKOCf0hQ
aL289q2//F2cbvBxtki6I8JzTg1H3oTO2WVrXQeCA3a/yiuRUatgGH4mxrpCF6byVJyqrEWAj4kU
uTbhMgIYhLGoaF1e+vMirCRXUXox0i5X976ASzHn64V9JSd1B+UbKfpcFTYYnChmrRDzmhKN1a8C
gYBTvIHAyO7ab18/BRUOllAOVSWhr8lXv0eqHEEzKh/rOaoFCRY3qpOcZpgJsGogumK1Z+sLnoeX
W8WaVVp6KbY4UeGF8aedItyvVnLbB6ohzTqkZ4Wvk05S6cs75kXYO0SL5U3NiCiiFXz2NA9nwTOk
s1nD2PPgiQ76Kx0mEkhKLwKBgFhHEJqv+AZu37Kx2NRe5WS/2KK9/DPD/hM5tv7mM3sq7Nvm2J3v
lVDS6J5AyZ5aLzXcER9qncKcz6wtC7SsFs1Wr4VPSoBroRPikrVJbgnXK8yZr+O/xq7Scv7WdJTq
rzkw6cWbObvLnltkUn/GQBVqBPBvF2nbtLdyBbuqKb5bAoGBAI1+aoJnvXEXxT4UHrMkQcY0eXRz
3UdbzJmtjMW9CR6l9s11mV6PcZP4qnODp3nd6a+lPeL3wVYQ47DsTJ/Bx5dI17zA5mU57n6mV0a3
DbSoPKSdaKTQdo2THnVE9P9sPKZWueAcsE4Yw/qcTjoxrtUnAH/AXN250v0tkKIOvMhu
-----END RSA PRIVATE KEY-----</keyMaterial>
</CreateKeyPairResponse>

View File

@ -140,7 +140,38 @@ public class CryptoStreams {
}
});
}
/**
* Computes and returns the SHA1 value for a supplied input stream. A digest
* object is created and disposed of at runtime, consider using
* {@link #digest} to be more efficient.
*
* @param supplier
* the input stream factory
*
* @return the result of {@link MessageDigest#digest()} after updating the
* sha1 object with all of the bytes in the stream
* @throws IOException
* if an I/O error occurs
*/
public static byte[] sha1(InputSupplier<? extends InputStream> supplier) throws IOException {
try {
return digest(supplier, MessageDigest.getInstance("SHA1"));
} catch (NoSuchAlgorithmException e) {
Throwables.propagate(e);
return null;
}
}
public static byte[] sha1(byte[] in) {
try {
return sha1(ByteStreams.newInputStreamSupplier(in));
} catch (IOException e) {
Throwables.propagate(e);
return null;
}
}
/**
* Computes and returns the MD5 value for a supplied input stream. A digest
* object is created and disposed of at runtime, consider using

View File

@ -316,7 +316,7 @@ public class Pems {
}
// TODO find a way to do this without using bouncycastle
static byte[] getEncoded(RSAPrivateCrtKey key) {
public static byte[] getEncoded(RSAPrivateCrtKey key) {
RSAPrivateKeyStructure keyStruct = new RSAPrivateKeyStructure(key.getModulus(), key.getPublicExponent(),
key.getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(),
key.getPrimeExponentQ(), key.getCrtCoefficient());

View File

@ -31,12 +31,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
@ -52,8 +54,8 @@ import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Iterables;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.io.InputSupplier;
/**
@ -68,36 +70,38 @@ import com.google.common.io.InputSupplier;
public class SshKeys {
/**
* Executes {@link Pems#publicKeySpecFromOpenSSH(InputSupplier)} on the
* string which was OpenSSH Base64 Encoded {@code id_rsa.pub}
* Executes {@link Pems#publicKeySpecFromOpenSSH(InputSupplier)} on the string which was OpenSSH
* Base64 Encoded {@code id_rsa.pub}
*
* @param idRsaPub
* formatted {@code ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB...}
* @see Pems#publicKeySpecFromOpenSSH(InputSupplier)
*/
public static RSAPublicKeySpec publicKeySpecFromOpenSSH(String idRsaPub) throws IOException {
return publicKeySpecFromOpenSSH(InputSuppliers.of(idRsaPub));
public static RSAPublicKeySpec publicKeySpecFromOpenSSH(String idRsaPub) {
try {
return publicKeySpecFromOpenSSH(InputSuppliers.of(idRsaPub));
} catch (IOException e) {
propagate(e);
return null;
}
}
/**
* Returns {@link RSAPublicKeySpec} which was OpenSSH Base64 Encoded
* {@code id_rsa.pub}
* Returns {@link RSAPublicKeySpec} which was OpenSSH Base64 Encoded {@code id_rsa.pub}
*
* @param supplier
* the input stream factory, formatted
* {@code ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB...}
* the input stream factory, formatted {@code ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB...}
*
* @return the {@link RSAPublicKeySpec} which was OpenSSH Base64 Encoded
* {@code id_rsa.pub}
* @return the {@link RSAPublicKeySpec} which was OpenSSH Base64 Encoded {@code id_rsa.pub}
* @throws IOException
* if an I/O error occurs
*/
public static RSAPublicKeySpec publicKeySpecFromOpenSSH(InputSupplier<? extends InputStream> supplier)
throws IOException {
throws IOException {
InputStream stream = supplier.getInput();
Iterable<String> parts = Splitter.on(' ').split(Strings2.toStringAndClose(stream));
checkArgument(Iterables.size(parts) >= 2 && "ssh-rsa".equals(Iterables.get(parts, 0)),
"bad format, should be: ssh-rsa AAAAB3...");
"bad format, should be: ssh-rsa AAAAB3...");
stream = new ByteArrayInputStream(Base64.decode(Iterables.get(parts, 1)));
String marker = new String(readLengthFirst(stream));
checkArgument("ssh-rsa".equals(marker), "looking for marker ssh-rsa but got %s", marker);
@ -132,8 +136,7 @@ public class SshKeys {
}
/**
* return a "public" -> rsa public key, "private" -> its corresponding
* private key
* return a "public" -> rsa public key, "private" -> its corresponding private key
*/
public static Map<String, String> generate() {
try {
@ -177,39 +180,142 @@ public class SshKeys {
* RSA private key in PEM format
* @param publicKeyOpenSSH
* RSA public key in OpenSSH format
* @return true if the keypair are matches
* @return true if the keypairs match
*/
public static boolean privateKeyMatchesPublicKey(String privateKeyPEM, String publicKeyOpenSSH) {
try {
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
"incorrect format expected RSAPrivateCrtKeySpec was %s", privateKeySpec);
return privateKeyMatchesPublicKey(RSAPrivateCrtKeySpec.class.cast(privateKeySpec),
return privateKeyMatchesPublicKey(RSAPrivateCrtKeySpec.class.cast(privateKeySpec),
publicKeySpecFromOpenSSH(publicKeyOpenSSH));
} catch (IOException e) {
propagate(e);
return false;
}
}
/**
* @return true if the keypair are matches
* @return true if the keypairs match
*/
public static boolean privateKeyMatchesPublicKey(RSAPrivateCrtKeySpec privateKey, RSAPublicKeySpec publicKey) {
return privateKey.getPublicExponent().equals(publicKey.getPublicExponent())
&& privateKey.getModulus().equals(publicKey.getModulus());
&& privateKey.getModulus().equals(publicKey.getModulus());
}
/**
* Create a fingerprint per the following <a
* href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00"
* >spec</a>
* @return true if the keypair has the same fingerprint as supplied
*/
public static boolean privateKeyHasFingerprint(RSAPrivateCrtKeySpec privateKey, String fingerprint) {
return fingerprint(privateKey.getPublicExponent(), privateKey.getModulus()).equals(fingerprint);
}
/**
* @param privateKeyPEM
* RSA private key in PEM format
* @param fingerprint
* ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
* @return true if the keypair has the same fingerprint as supplied
*/
public static boolean privateKeyHasFingerprint(String privateKeyPEM, String fingerprint) {
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
"incorrect format expected RSAPrivateCrtKeySpec was %s", privateKeySpec);
return privateKeyHasFingerprint(RSAPrivateCrtKeySpec.class.cast(privateKeySpec), fingerprint);
}
/**
* @param privateKeyPEM
* RSA private key in PEM format
* @return fingerprint ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
*/
public static String fingerprintPrivateKey(String privateKeyPEM) {
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
"incorrect format expected RSAPrivateCrtKeySpec was %s", privateKeySpec);
RSAPrivateCrtKeySpec certKeySpec = RSAPrivateCrtKeySpec.class.cast(privateKeySpec);
return fingerprint(certKeySpec.getPublicExponent(), certKeySpec.getModulus());
}
/**
* @return true if the keypair has the same SHA1 fingerprint as supplied
*/
public static boolean privateKeyHasSha1(RSAPrivateCrtKeySpec privateKey, String fingerprint) {
return sha1(privateKey).equals(fingerprint);
}
/**
* @param privateKeyPEM
* RSA private key in PEM format
* @param sha1HexColonDelimited
* ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
* @return true if the keypair has the same fingerprint as supplied
*/
public static boolean privateKeyHasSha1(String privateKeyPEM, String sha1HexColonDelimited) {
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
"incorrect format expected RSAPrivateCrtKeySpec was %s", privateKeySpec);
return privateKeyHasSha1(RSAPrivateCrtKeySpec.class.cast(privateKeySpec), sha1HexColonDelimited);
}
/**
* @param privateKeyPEM
* RSA private key in PEM format
* @return sha1HexColonDelimited ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
*/
public static String sha1PrivateKey(String privateKeyPEM) {
KeySpec privateKeySpec = privateKeySpec(privateKeyPEM);
checkArgument(privateKeySpec instanceof RSAPrivateCrtKeySpec,
"incorrect format expected RSAPrivateCrtKeySpec was %s", privateKeySpec);
RSAPrivateCrtKeySpec certKeySpec = RSAPrivateCrtKeySpec.class.cast(privateKeySpec);
return sha1(certKeySpec);
}
/**
* Create a SHA-1 digest of the DER encoded private key.
*
* @param publicExponent
* @param modulus
*
* @return hex fingerprint ex.
* {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
* @return hex sha1HexColonDelimited ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
*/
public static String sha1(RSAPrivateCrtKeySpec privateKey) {
try {
String sha1 = Joiner.on(":").join(
Splitter.fixedLength(2).split(
hex(CryptoStreams.sha1(KeyFactory.getInstance("RSA").generatePrivate(privateKey)
.getEncoded()))));
return sha1;
} catch (InvalidKeySpecException e) {
propagate(e);
return null;
} catch (NoSuchAlgorithmException e) {
propagate(e);
return null;
}
}
/**
* @return true if the keypair has the same fingerprint as supplied
*/
public static boolean publicKeyHasFingerprint(RSAPublicKeySpec publicKey, String fingerprint) {
return fingerprint(publicKey.getPublicExponent(), publicKey.getModulus()).equals(fingerprint);
}
/**
* @param publicKeyOpenSSH
* RSA public key in OpenSSH format
* @param fingerprint
* ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
* @return true if the keypair has the same fingerprint as supplied
*/
public static boolean publicKeyHasFingerprint(String publicKeyOpenSSH, String fingerprint) {
return publicKeyHasFingerprint(publicKeySpecFromOpenSSH(publicKeyOpenSSH), fingerprint);
}
/**
* Create a fingerprint per the following <a
* href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00" >spec</a>
*
* @param publicExponent
* @param modulus
*
* @return hex fingerprint ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9}
*/
public static String fingerprint(BigInteger publicExponent, BigInteger modulus) {
byte[] keyBlob = keyBlob(publicExponent, modulus);

View File

@ -20,7 +20,9 @@ package org.jclouds.crypto;
import static org.jclouds.crypto.SshKeys.fingerprint;
import static org.jclouds.crypto.SshKeys.generate;
import static org.jclouds.crypto.SshKeys.privateKeyHasFingerprint;
import static org.jclouds.crypto.SshKeys.privateKeyMatchesPublicKey;
import static org.jclouds.crypto.SshKeys.privateKeyHasSha1;
import static org.jclouds.crypto.SshKeys.publicKeySpecFromOpenSSH;
import static org.testng.Assert.assertEquals;
@ -45,6 +47,7 @@ import org.testng.annotations.Test;
public class SshKeysTest {
String expectedFingerprint = "2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9";
String expectedSha1 = "c8:01:34:c0:3c:8c:91:ac:e1:da:cf:72:15:d7:f2:e5:99:5b:28:d4";
@Test
public void testCanReadRsaAndCompareFingerprintOnPublicRSAKey() throws IOException {
@ -62,6 +65,32 @@ public class SshKeysTest {
assertEquals(fingerPrint, expectedFingerprint);
}
@Test
public void testPrivateKeyMatchesFingerprintTyped() throws IOException {
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
RSAPrivateCrtKeySpec privateKey = (RSAPrivateCrtKeySpec) Pems.privateKeySpec(privKey);
assert privateKeyHasFingerprint(privateKey, expectedFingerprint);
}
@Test
public void testPrivateKeyMatchesFingerprintString() throws IOException {
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
assert privateKeyHasFingerprint(privKey, expectedFingerprint);
}
@Test
public void testPrivateKeyMatchesSha1Typed() throws IOException {
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
RSAPrivateCrtKeySpec privateKey = (RSAPrivateCrtKeySpec) Pems.privateKeySpec(privKey);
assert privateKeyHasSha1(privateKey, expectedSha1);
}
@Test
public void testPrivateKeyMatchesSha1String() throws IOException {
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
assert privateKeyHasSha1(privKey, expectedSha1);
}
@Test
public void testPrivateKeyMatchesPublicKeyTyped() throws IOException {
String privKey = Strings2.toStringAndClose(getClass().getResourceAsStream("/test"));
@ -90,7 +119,7 @@ public class SshKeysTest {
@Test
public void testEncodeAsOpenSSH() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
String encoded = SshKeys.encodeAsOpenSSH((RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(
SshKeys.publicKeySpecFromOpenSSH(Payloads.newPayload(getClass().getResourceAsStream("/test.pub")))));
SshKeys.publicKeySpecFromOpenSSH(Payloads.newPayload(getClass().getResourceAsStream("/test.pub")))));
assertEquals(encoded, Strings2.toStringAndClose(getClass().getResourceAsStream("/test.pub")).trim());
}

View File

@ -25,11 +25,11 @@ import static com.google.common.base.Predicates.instanceOf;
import static com.google.common.base.Predicates.or;
import static com.google.common.base.Throwables.getCausalChain;
import static com.google.common.collect.Iterables.any;
import static org.jclouds.crypto.SshKeys.fingerprint;
import static org.jclouds.crypto.SshKeys.fingerprintPrivateKey;
import static org.jclouds.crypto.SshKeys.sha1PrivateKey;
import java.io.IOException;
import java.io.InputStream;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
@ -50,7 +50,6 @@ import net.schmizz.sshj.xfer.InMemorySourceFile;
import org.apache.commons.io.input.ProxyInputStream;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.crypto.Pems;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.io.Payload;
import org.jclouds.io.Payloads;
@ -96,17 +95,17 @@ public class SshjSshClient implements SshClient {
private final String username;
private final String password;
private final String toString;
@Inject(optional = true)
@Named("jclouds.ssh.max-retries")
@VisibleForTesting
int sshRetries = 5;
@Inject(optional = true)
@Named("jclouds.ssh.retry-auth")
@VisibleForTesting
boolean retryAuth;
@Inject(optional = true)
@Named("jclouds.ssh.retryable-messages")
@VisibleForTesting
@ -116,7 +115,7 @@ public class SshjSshClient implements SshClient {
@Named("jclouds.ssh.retry-predicate")
// NOTE cannot retry io exceptions, as SSHException is a part of the chain
private Predicate<Throwable> retryPredicate = or(instanceOf(ConnectionException.class),
instanceOf(TransportException.class));
instanceOf(TransportException.class));
@Resource
@Named("jclouds.ssh")
@ -129,7 +128,7 @@ public class SshjSshClient implements SshClient {
private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
public SshjSshClient(BackoffLimitedRetryHandler backoffLimitedRetryHandler, IPSocket socket, int timeout,
String username, String password, byte[] privateKey) {
String username, String password, byte[] privateKey) {
this.host = checkNotNull(socket, "socket").getAddress();
checkArgument(socket.getPort() > 0, "ssh port must be greater then zero" + socket.getPort());
checkArgument(password != null || privateKey != null, "you must specify a password or a key");
@ -140,11 +139,12 @@ public class SshjSshClient implements SshClient {
this.password = password;
this.privateKey = privateKey;
if (privateKey == null) {
this.toString = String.format("%s@%s:%d", username, host, port);
this.toString = String.format("%s:password@%s:%d", username, host, port);
} else {
RSAPrivateCrtKeySpec key = (RSAPrivateCrtKeySpec) Pems.privateKeySpec(new String(privateKey));
String fingerPrint = fingerprint(key.getPublicExponent(), key.getModulus());
this.toString = String.format("%s:[%s]@%s:%d", username, fingerPrint, host, port);
String fingerPrint = fingerprintPrivateKey(new String(privateKey));
String sha1 = sha1PrivateKey(new String(privateKey));
this.toString = String.format("%s:rsa[fingerprint(%s),sha1(%s)]@%s:%d", username, fingerPrint, sha1, host,
port);
}
}
@ -198,7 +198,7 @@ public class SshjSshClient implements SshClient {
@Override
public String toString() {
return String.format("SSHClient(%s)", SshjSshClient.this.toString());
return String.format("SSHClient(timeout=%d)", timeoutMillis);
}
};
@ -266,7 +266,7 @@ public class SshjSshClient implements SshClient {
@Override
public String toString() {
return "SFTPClient(" + SshjSshClient.this.toString() + ")";
return "SFTPClient()";
}
};
@ -288,12 +288,12 @@ public class SshjSshClient implements SshClient {
public Payload create() throws Exception {
sftp = acquire(sftpConnection);
return Payloads.newInputStreamPayload(new CloseFtpChannelOnCloseInputStream(sftp.getSFTPEngine().open(path)
.getInputStream(), sftp));
.getInputStream(), sftp));
}
@Override
public String toString() {
return "Payload(" + SshjSshClient.this.toString() + ")[" + path + "]";
return "Payload(path=[" + path + "])";
}
};
@ -351,7 +351,7 @@ public class SshjSshClient implements SshClient {
@Override
public String toString() {
return "Put(" + SshjSshClient.this.toString() + ")[" + path + "]";
return "Put(path=[" + path + "])";
}
};
@ -362,8 +362,8 @@ public class SshjSshClient implements SshClient {
@VisibleForTesting
boolean shouldRetry(Exception from) {
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable>or(retryPredicate, instanceOf(AuthorizationException.class))
: retryPredicate;
Predicate<Throwable> predicate = retryAuth ? Predicates.<Throwable> or(retryPredicate,
instanceOf(AuthorizationException.class)) : retryPredicate;
if (any(getCausalChain(from), predicate))
return true;
if (!retryableMessages.equals(""))
@ -397,12 +397,12 @@ public class SshjSshClient implements SshClient {
if (e instanceof UserAuthException)
throw new AuthorizationException("(" + toString() + ") " + message, e);
throw e instanceof SshException ? SshException.class.cast(e) : new SshException(
"(" + toString() + ") " + message, e);
"(" + toString() + ") " + message, e);
}
@Override
public String toString() {
return toString ;
return toString;
}
@PreDestroy
@ -436,7 +436,7 @@ public class SshjSshClient implements SshClient {
@Override
public String toString() {
return "Session(" + SshjSshClient.this.toString() + ")";
return "Session()";
}
};
@ -473,7 +473,7 @@ public class SshjSshClient implements SshClient {
@Override
public String toString() {
return "ExecResponse(" + SshjSshClient.this.toString() + ")[" + command + "]";
return "ExecResponse(command=[" + command + "])";
}
}

View File

@ -23,6 +23,8 @@ import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions.Builder.keyPair;
import static org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.CREDENTIALS;
import static org.jclouds.ec2.compute.strategy.CreateKeyPairAndSecurityGroupsAsNeededAndReturnRunOptionsTest.KEYPAIR;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Method;
@ -471,10 +473,8 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
expect(backing.containsKey(new RegionAndName(region, group))).andReturn(false);
expect(options.getKeyPair()).andReturn(userSuppliedKeyPair);
expect(options.getPublicKey()).andReturn(null).times(2);
expect(options.getOverridingCredentials()).andReturn(new Credentials(null, "MyRsa")).atLeastOnce();
expect(
backing.put(new RegionAndName(region, userSuppliedKeyPair), KeyPair.builder().region(region).keyName(
userSuppliedKeyPair).keyFingerprint("//TODO").keyMaterial("MyRsa").build())).andReturn(null);
expect(options.getOverridingCredentials()).andReturn(CREDENTIALS).atLeastOnce();
expect(backing.put(new RegionAndName(region, userSuppliedKeyPair), KEYPAIR)).andReturn(null);
expect(options.getRunScript()).andReturn(Statements.exec("echo foo"));
expect(strategy.credentialsMap.getUnchecked(new RegionAndName(region, userSuppliedKeyPair))).andReturn(keyPair);
@ -504,17 +504,19 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
// create mocks
CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
AWSEC2TemplateOptions options = keyPair(group).authorizePublicKey("ssh-rsa").overrideCredentialsWith(
new Credentials("foo", "private"));
KeyPair keyPair = new KeyPair(region, group, "//TODO", null);
new Credentials("foo", CREDENTIALS.credential));
KeyPair keyPair = new KeyPair(region, group, "//TODO", null, null);
ConcurrentMap<RegionAndName, KeyPair> backing = createMock(ConcurrentMap.class);
// setup expectations
expect(strategy.credentialsMap.asMap()).andReturn(backing).atLeastOnce();
expect(backing.containsKey(new RegionAndName(region, group))).andReturn(false);
expect(strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group, "private")))
.andReturn(keyPair);
expect(backing.put(new RegionAndName(region, group), keyPair.toBuilder().keyMaterial("private").build()))
.andReturn(null);
expect(
strategy.importExistingKeyPair.apply(new RegionNameAndPublicKeyMaterial(region, group,
CREDENTIALS.credential))).andReturn(keyPair);
expect(
backing.put(new RegionAndName(region, group), keyPair.toBuilder().keyMaterial(CREDENTIALS.credential)
.build())).andReturn(null);
// replay mocks
replay(backing);
@ -539,7 +541,7 @@ public class CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptionsT
// create mocks
CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions strategy = setupStrategy();
KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null);
KeyPair keyPair = new KeyPair(region, "jclouds#" + group, "fingerprint", null, null);
ConcurrentMap<RegionAndName, KeyPair> backing = createMock(ConcurrentMap.class);