Added SocketOpen retryable predicate for connecting to ssh

Some refactoring, including use of guava's checkState and more precise method parameters
This commit is contained in:
Alex Yarmula 2010-02-17 10:40:35 -08:00
parent a46542ee91
commit 543af26e92
5 changed files with 46 additions and 28 deletions

View File

@ -30,7 +30,7 @@
<artifactId>jclouds-aws-demo-ebsresize</artifactId>
<groupId>org.jclouds</groupId>
<name>jclouds EC2 example, to change volume size for EBS-based instances</name>
<name>jclouds EC2 demo, to change volume size for EBS-based instances</name>
<description>For a given running instance, resizes the volume (with several minutes of downtime)</description>
<repositories>

View File

@ -19,6 +19,10 @@
package org.jclouds.tools.ebsresize;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.ec2.EC2AsyncClient;
import org.jclouds.aws.ec2.EC2Client;
@ -30,13 +34,17 @@ import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
import org.jclouds.domain.Credentials;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.predicates.SocketOpen;
import org.jclouds.rest.RestContext;
import org.jclouds.tools.ebsresize.facade.ElasticBlockStoreFacade;
import org.jclouds.tools.ebsresize.facade.InstanceFacade;
import org.jclouds.tools.ebsresize.util.SshExecutor;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Launches a sequence of commands to change the size of
@ -59,6 +67,8 @@ public class InstanceVolumeManager {
private final EC2Client api;
private final ElasticBlockStoreFacade ebsApi;
private final InstanceFacade instanceApi;
private final Predicate<InetSocketAddress> socketOpen =
new RetryablePredicate<InetSocketAddress>(new SocketOpen(), 180, 5, TimeUnit.SECONDS);
public InstanceVolumeManager(String accessKeyId, String secretKey) {
@ -92,14 +102,6 @@ public class InstanceVolumeManager {
api.getElasticBlockStoreServices().deleteVolumeInRegion(instance.getRegion(), volume.getId());
//TODO: how to know that an instance is available for SSH for certain?
// sometimes 20 seconds is enough after it started, sometimes it isn't
try {
Thread.sleep(20000);
} catch(InterruptedException e) {
e.printStackTrace();
}
runRemoteResizeCommands(instance, instanceCredentials, pathToKeyPair);
}
@ -112,8 +114,14 @@ public class InstanceVolumeManager {
NodeMetadata nodeMetadata = addCredentials((NodeMetadata) nodes.get(instance.getId()),
instanceCredentials);
InetSocketAddress socket =
new InetSocketAddress(Iterables.getLast(nodeMetadata.getPublicAddresses()), 22);
SshExecutor sshExecutor = new SshExecutor(nodeMetadata, instanceCredentials,
keyPair, instance);
keyPair, socket);
waitForSocket(socket);
sshExecutor.connect();
sshExecutor.execute("sudo resize2fs " + instance.getRootDeviceName());
}
@ -129,6 +137,11 @@ public class InstanceVolumeManager {
credentials);
}
public void waitForSocket(InetSocketAddress socket) {
checkState(socketOpen.apply(socket),
/*or throw*/ "Couldn't connect to instance");
}
public ElasticBlockStoreFacade getEbsApi() {
return ebsApi;
}

View File

@ -99,7 +99,8 @@ public class ElasticBlockStoreFacade {
public void detachVolumeFromStoppedInstance(Volume volume, RunningInstance stoppedInstance) {
elasticBlockStoreServices.detachVolumeInRegion(stoppedInstance.getRegion(), volume.getId(), false,
DetachVolumeOptions.Builder.fromInstance(stoppedInstance.getId()));
assertTrue(volumeAvailable.apply(volume));
checkState(volumeAvailable.apply(volume),
/*or throw*/ "Couldn't detach the volume from instance");
}
/**
@ -117,12 +118,14 @@ public class ElasticBlockStoreFacade {
Snapshot createdSnapshot =
elasticBlockStoreServices.createSnapshotInRegion(volume.getRegion(), volume.getId(),
CreateSnapshotOptions.Builder.withDescription("snapshot to test extending volumes"));
assertTrue(snapshotCompleted.apply(createdSnapshot));
checkState(snapshotCompleted.apply(createdSnapshot),
/*or throw*/ "Couldn't create a snapshot");
Volume newVolume = elasticBlockStoreServices.createVolumeFromSnapshotInAvailabilityZone(
volume.getAvailabilityZone(), newSize,
createdSnapshot.getId());
assertTrue(volumeAvailable.apply(newVolume));
checkState(volumeAvailable.apply(newVolume),
/*or throw*/ "Couldn't create a volume from the snapshot");
return newVolume;
}
@ -141,10 +144,7 @@ public class ElasticBlockStoreFacade {
Attachment volumeAttachment = elasticBlockStoreServices.
attachVolumeInRegion(instance.getRegion(), volume.getId(), instance.getId(),
instance.getRootDeviceName());
assertTrue(volumeAttached.apply(volumeAttachment));
}
public void assertTrue(boolean value) {
if(!value) throw new RuntimeException("Found false, expected true");
checkState(volumeAttached.apply(volumeAttachment),
/*or throw*/ "Couldn't attach volume back to the instance");
}
}

View File

@ -69,7 +69,8 @@ public class InstanceFacade {
*/
public void startInstance(RunningInstance instance) {
instanceServices.startInstancesInRegion(instance.getRegion(), instance.getId());
assertTrue(instanceRunning.apply(instance));
checkState(instanceRunning.apply(instance),
/*or throw*/ "Couldn't start the instance");
}
/**
@ -84,7 +85,8 @@ public class InstanceFacade {
*/
public void stopInstance(RunningInstance instance) {
instanceServices.stopInstancesInRegion(instance.getRegion(), false, instance.getId());
assertTrue(instanceStopped.apply(instance));
checkState(instanceStopped.apply(instance),
/*or throw*/ "Couldn't stop the instance");
}
/**
@ -99,9 +101,5 @@ public class InstanceFacade {
Reservation reservation = checkNotNull(Iterables.getOnlyElement(reservations));
return Iterables.getOnlyElement(reservation);
}
public void assertTrue(boolean value) {
if(!value) throw new RuntimeException("Found false, expected true");
}
}

View File

@ -35,6 +35,15 @@ import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* Allows remote command execution on instance.
*
* This is based on {@link org.jclouds.ssh.SshClient} and
* {@link org.jclouds.compute.util.ComputeUtils}, with
* some convenience methods.
*
* @see org.jclouds.ssh.SshClient
* @see org.jclouds.compute.util.ComputeUtils
*
* @author Oleksiy Yarmula
*/
public class SshExecutor {
@ -46,21 +55,19 @@ public class SshExecutor {
private final NodeMetadata nodeMetadata;
private final Credentials instanceCredentials;
private final String keyPair;
private final RunningInstance instance;
private final SshClient sshClient;
private final ComputeUtils utils;
public SshExecutor(NodeMetadata nodeMetadata,
Credentials instanceCredentials,
String keyPair,
RunningInstance instance) {
InetSocketAddress socket) {
this.nodeMetadata = nodeMetadata;
this.instanceCredentials = instanceCredentials;
this.keyPair = keyPair;
this.instance = instance;
this.sshClient =
new JschSshClient(new InetSocketAddress(instance.getDnsName(), 22), 60000,
new JschSshClient(socket, 60000,
instanceCredentials.account, keyPair.getBytes());
this.utils = new ComputeUtils(null, runScriptRunning, null);