From 543af26e925a066750f57209045ade5c319427f5 Mon Sep 17 00:00:00 2001 From: Alex Yarmula Date: Wed, 17 Feb 2010 10:40:35 -0800 Subject: [PATCH] Added SocketOpen retryable predicate for connecting to ssh Some refactoring, including use of guava's checkState and more precise method parameters --- aws/demos/resize-ebs/resize-ebs-java/pom.xml | 2 +- .../ebsresize/InstanceVolumeManager.java | 31 +++++++++++++------ .../facade/ElasticBlockStoreFacade.java | 16 +++++----- .../ebsresize/facade/InstanceFacade.java | 10 +++--- .../tools/ebsresize/util/SshExecutor.java | 15 ++++++--- 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/aws/demos/resize-ebs/resize-ebs-java/pom.xml b/aws/demos/resize-ebs/resize-ebs-java/pom.xml index 1dd75f7ea9..734aeec3dc 100644 --- a/aws/demos/resize-ebs/resize-ebs-java/pom.xml +++ b/aws/demos/resize-ebs/resize-ebs-java/pom.xml @@ -30,7 +30,7 @@ jclouds-aws-demo-ebsresize org.jclouds - jclouds EC2 example, to change volume size for EBS-based instances + jclouds EC2 demo, to change volume size for EBS-based instances For a given running instance, resizes the volume (with several minutes of downtime) diff --git a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/InstanceVolumeManager.java b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/InstanceVolumeManager.java index 963520f9b6..e6c6d3e09c 100644 --- a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/InstanceVolumeManager.java +++ b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/InstanceVolumeManager.java @@ -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 socketOpen = + new RetryablePredicate(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; } diff --git a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/ElasticBlockStoreFacade.java b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/ElasticBlockStoreFacade.java index 7f02df06fd..f0c361d762 100644 --- a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/ElasticBlockStoreFacade.java +++ b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/ElasticBlockStoreFacade.java @@ -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"); } } diff --git a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/InstanceFacade.java b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/InstanceFacade.java index d0e0846c57..1bd30a6dd2 100644 --- a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/InstanceFacade.java +++ b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/facade/InstanceFacade.java @@ -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"); - } } diff --git a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/util/SshExecutor.java b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/util/SshExecutor.java index 793bdadb39..9256e9afd8 100644 --- a/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/util/SshExecutor.java +++ b/aws/demos/resize-ebs/resize-ebs-java/src/main/java/org/jclouds/tools/ebsresize/util/SshExecutor.java @@ -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);