mirror of https://github.com/apache/jclouds.git
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:
parent
a46542ee91
commit
543af26e92
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue