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>
|
<artifactId>jclouds-aws-demo-ebsresize</artifactId>
|
||||||
<groupId>org.jclouds</groupId>
|
<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>
|
<description>For a given running instance, resizes the volume (with several minutes of downtime)</description>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
package org.jclouds.tools.ebsresize;
|
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.domain.Region;
|
||||||
import org.jclouds.aws.ec2.EC2AsyncClient;
|
import org.jclouds.aws.ec2.EC2AsyncClient;
|
||||||
import org.jclouds.aws.ec2.EC2Client;
|
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.NodeMetadata;
|
||||||
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
|
import org.jclouds.compute.domain.internal.NodeMetadataImpl;
|
||||||
import org.jclouds.domain.Credentials;
|
import org.jclouds.domain.Credentials;
|
||||||
|
import org.jclouds.predicates.RetryablePredicate;
|
||||||
|
import org.jclouds.predicates.SocketOpen;
|
||||||
import org.jclouds.rest.RestContext;
|
import org.jclouds.rest.RestContext;
|
||||||
import org.jclouds.tools.ebsresize.facade.ElasticBlockStoreFacade;
|
import org.jclouds.tools.ebsresize.facade.ElasticBlockStoreFacade;
|
||||||
import org.jclouds.tools.ebsresize.facade.InstanceFacade;
|
import org.jclouds.tools.ebsresize.facade.InstanceFacade;
|
||||||
import org.jclouds.tools.ebsresize.util.SshExecutor;
|
import org.jclouds.tools.ebsresize.util.SshExecutor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launches a sequence of commands to change the size of
|
* Launches a sequence of commands to change the size of
|
||||||
|
@ -59,6 +67,8 @@ public class InstanceVolumeManager {
|
||||||
private final EC2Client api;
|
private final EC2Client api;
|
||||||
private final ElasticBlockStoreFacade ebsApi;
|
private final ElasticBlockStoreFacade ebsApi;
|
||||||
private final InstanceFacade instanceApi;
|
private final InstanceFacade instanceApi;
|
||||||
|
private final Predicate<InetSocketAddress> socketOpen =
|
||||||
|
new RetryablePredicate<InetSocketAddress>(new SocketOpen(), 180, 5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
public InstanceVolumeManager(String accessKeyId, String secretKey) {
|
public InstanceVolumeManager(String accessKeyId, String secretKey) {
|
||||||
|
|
||||||
|
@ -92,14 +102,6 @@ public class InstanceVolumeManager {
|
||||||
|
|
||||||
api.getElasticBlockStoreServices().deleteVolumeInRegion(instance.getRegion(), volume.getId());
|
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);
|
runRemoteResizeCommands(instance, instanceCredentials, pathToKeyPair);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +114,14 @@ public class InstanceVolumeManager {
|
||||||
NodeMetadata nodeMetadata = addCredentials((NodeMetadata) nodes.get(instance.getId()),
|
NodeMetadata nodeMetadata = addCredentials((NodeMetadata) nodes.get(instance.getId()),
|
||||||
instanceCredentials);
|
instanceCredentials);
|
||||||
|
|
||||||
|
InetSocketAddress socket =
|
||||||
|
new InetSocketAddress(Iterables.getLast(nodeMetadata.getPublicAddresses()), 22);
|
||||||
|
|
||||||
SshExecutor sshExecutor = new SshExecutor(nodeMetadata, instanceCredentials,
|
SshExecutor sshExecutor = new SshExecutor(nodeMetadata, instanceCredentials,
|
||||||
keyPair, instance);
|
keyPair, socket);
|
||||||
|
|
||||||
|
waitForSocket(socket);
|
||||||
|
|
||||||
sshExecutor.connect();
|
sshExecutor.connect();
|
||||||
sshExecutor.execute("sudo resize2fs " + instance.getRootDeviceName());
|
sshExecutor.execute("sudo resize2fs " + instance.getRootDeviceName());
|
||||||
}
|
}
|
||||||
|
@ -129,6 +137,11 @@ public class InstanceVolumeManager {
|
||||||
credentials);
|
credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void waitForSocket(InetSocketAddress socket) {
|
||||||
|
checkState(socketOpen.apply(socket),
|
||||||
|
/*or throw*/ "Couldn't connect to instance");
|
||||||
|
}
|
||||||
|
|
||||||
public ElasticBlockStoreFacade getEbsApi() {
|
public ElasticBlockStoreFacade getEbsApi() {
|
||||||
return ebsApi;
|
return ebsApi;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,8 @@ public class ElasticBlockStoreFacade {
|
||||||
public void detachVolumeFromStoppedInstance(Volume volume, RunningInstance stoppedInstance) {
|
public void detachVolumeFromStoppedInstance(Volume volume, RunningInstance stoppedInstance) {
|
||||||
elasticBlockStoreServices.detachVolumeInRegion(stoppedInstance.getRegion(), volume.getId(), false,
|
elasticBlockStoreServices.detachVolumeInRegion(stoppedInstance.getRegion(), volume.getId(), false,
|
||||||
DetachVolumeOptions.Builder.fromInstance(stoppedInstance.getId()));
|
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 =
|
Snapshot createdSnapshot =
|
||||||
elasticBlockStoreServices.createSnapshotInRegion(volume.getRegion(), volume.getId(),
|
elasticBlockStoreServices.createSnapshotInRegion(volume.getRegion(), volume.getId(),
|
||||||
CreateSnapshotOptions.Builder.withDescription("snapshot to test extending volumes"));
|
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 newVolume = elasticBlockStoreServices.createVolumeFromSnapshotInAvailabilityZone(
|
||||||
volume.getAvailabilityZone(), newSize,
|
volume.getAvailabilityZone(), newSize,
|
||||||
createdSnapshot.getId());
|
createdSnapshot.getId());
|
||||||
assertTrue(volumeAvailable.apply(newVolume));
|
checkState(volumeAvailable.apply(newVolume),
|
||||||
|
/*or throw*/ "Couldn't create a volume from the snapshot");
|
||||||
|
|
||||||
return newVolume;
|
return newVolume;
|
||||||
}
|
}
|
||||||
|
@ -141,10 +144,7 @@ public class ElasticBlockStoreFacade {
|
||||||
Attachment volumeAttachment = elasticBlockStoreServices.
|
Attachment volumeAttachment = elasticBlockStoreServices.
|
||||||
attachVolumeInRegion(instance.getRegion(), volume.getId(), instance.getId(),
|
attachVolumeInRegion(instance.getRegion(), volume.getId(), instance.getId(),
|
||||||
instance.getRootDeviceName());
|
instance.getRootDeviceName());
|
||||||
assertTrue(volumeAttached.apply(volumeAttachment));
|
checkState(volumeAttached.apply(volumeAttachment),
|
||||||
}
|
/*or throw*/ "Couldn't attach volume back to the instance");
|
||||||
|
|
||||||
public void assertTrue(boolean value) {
|
|
||||||
if(!value) throw new RuntimeException("Found false, expected true");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,8 @@ public class InstanceFacade {
|
||||||
*/
|
*/
|
||||||
public void startInstance(RunningInstance instance) {
|
public void startInstance(RunningInstance instance) {
|
||||||
instanceServices.startInstancesInRegion(instance.getRegion(), instance.getId());
|
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) {
|
public void stopInstance(RunningInstance instance) {
|
||||||
instanceServices.stopInstancesInRegion(instance.getRegion(), false, instance.getId());
|
instanceServices.stopInstancesInRegion(instance.getRegion(), false, instance.getId());
|
||||||
assertTrue(instanceStopped.apply(instance));
|
checkState(instanceStopped.apply(instance),
|
||||||
|
/*or throw*/ "Couldn't stop the instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,8 +102,4 @@ public class InstanceFacade {
|
||||||
return Iterables.getOnlyElement(reservation);
|
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;
|
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
|
* @author Oleksiy Yarmula
|
||||||
*/
|
*/
|
||||||
public class SshExecutor {
|
public class SshExecutor {
|
||||||
|
@ -46,21 +55,19 @@ public class SshExecutor {
|
||||||
private final NodeMetadata nodeMetadata;
|
private final NodeMetadata nodeMetadata;
|
||||||
private final Credentials instanceCredentials;
|
private final Credentials instanceCredentials;
|
||||||
private final String keyPair;
|
private final String keyPair;
|
||||||
private final RunningInstance instance;
|
|
||||||
private final SshClient sshClient;
|
private final SshClient sshClient;
|
||||||
private final ComputeUtils utils;
|
private final ComputeUtils utils;
|
||||||
|
|
||||||
public SshExecutor(NodeMetadata nodeMetadata,
|
public SshExecutor(NodeMetadata nodeMetadata,
|
||||||
Credentials instanceCredentials,
|
Credentials instanceCredentials,
|
||||||
String keyPair,
|
String keyPair,
|
||||||
RunningInstance instance) {
|
InetSocketAddress socket) {
|
||||||
this.nodeMetadata = nodeMetadata;
|
this.nodeMetadata = nodeMetadata;
|
||||||
this.instanceCredentials = instanceCredentials;
|
this.instanceCredentials = instanceCredentials;
|
||||||
this.keyPair = keyPair;
|
this.keyPair = keyPair;
|
||||||
this.instance = instance;
|
|
||||||
|
|
||||||
this.sshClient =
|
this.sshClient =
|
||||||
new JschSshClient(new InetSocketAddress(instance.getDnsName(), 22), 60000,
|
new JschSshClient(socket, 60000,
|
||||||
instanceCredentials.account, keyPair.getBytes());
|
instanceCredentials.account, keyPair.getBytes());
|
||||||
|
|
||||||
this.utils = new ComputeUtils(null, runScriptRunning, null);
|
this.utils = new ComputeUtils(null, runScriptRunning, null);
|
||||||
|
|
Loading…
Reference in New Issue