From 14d9862853db362bc885640081cb4ea2b3efd54a Mon Sep 17 00:00:00 2001 From: Alex Yarmula Date: Thu, 18 Feb 2010 16:56:36 -0800 Subject: [PATCH] Added a new live test (InstanceVolumeManagerLiveTest.java). Changed the snapshot taking behavior: snapshot is deleted when it's no longer needed. Exposed some of the object on InstanceVolumeManager for tests --- aws/demos/resize-ebs/README.txt | 4 +- .../ebsresize/InstanceVolumeManager.java | 17 +- .../facade/ElasticBlockStoreFacade.java | 2 +- .../InstanceVolumeManagerLiveTest.java | 199 ++++++++++++++++++ 4 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 aws/demos/resize-ebs/resize-ebs-java/src/test/java/org/jclouds/tools/ebsresize/InstanceVolumeManagerLiveTest.java diff --git a/aws/demos/resize-ebs/README.txt b/aws/demos/resize-ebs/README.txt index 5c9405ece2..e2d97f64e4 100644 --- a/aws/demos/resize-ebs/README.txt +++ b/aws/demos/resize-ebs/README.txt @@ -11,4 +11,6 @@ $ mvn jruby:run Pre-requisites: - running EBS-backed instance at Amazon EC2 - Ubuntu image -- proper settings in place (EbsResizeMain or launcher.rb) \ No newline at end of file +- proper settings in place (EbsResizeMain or launcher.rb) +- file system is ext3 (otherwise, change is needed in org.jclouds.tools.ebsresize.InstanceVolumeManager) +- the demo doesn't take care of freezing the database, filesystem and application data \ No newline at end of file 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 e6c6d3e09c..1056a409d0 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 @@ -23,6 +23,8 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.inject.Module; +import com.google.inject.internal.ImmutableSet; import org.jclouds.aws.domain.Region; import org.jclouds.aws.ec2.EC2AsyncClient; import org.jclouds.aws.ec2.EC2Client; @@ -44,6 +46,7 @@ import org.jclouds.tools.ebsresize.util.SshExecutor; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; +import java.util.Properties; import java.util.concurrent.TimeUnit; /** @@ -71,10 +74,14 @@ public class InstanceVolumeManager { new RetryablePredicate(new SocketOpen(), 180, 5, TimeUnit.SECONDS); public InstanceVolumeManager(String accessKeyId, String secretKey) { + this(accessKeyId, secretKey, new Properties()); + } + public InstanceVolumeManager(String accessKeyId, String secretKey, Properties overridesForContext) { try { context = new ComputeServiceContextFactory() - .createContext("ec2", accessKeyId, secretKey); + .createContext("ec2", accessKeyId, secretKey, + ImmutableSet. of(), overridesForContext); } catch(IOException e) { throw new RuntimeException(e); } ec2context = context.getProviderSpecificContext(); @@ -142,6 +149,10 @@ public class InstanceVolumeManager { /*or throw*/ "Couldn't connect to instance"); } + public void closeContext() { + context.close(); + } + public ElasticBlockStoreFacade getEbsApi() { return ebsApi; } @@ -153,4 +164,8 @@ public class InstanceVolumeManager { public EC2Client getApi() { return api; } + + public ComputeServiceContext getContext() { + return context; + } } 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 f0c361d762..1de75a2ac0 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 @@ -72,7 +72,6 @@ public class ElasticBlockStoreFacade { "Only storage for instances with EBS type of root device can be resized."); String rootDevice = checkNotNull(runningInstance.getRootDeviceName()); - runningInstance.getRootDeviceType(); //get volume id String volumeId = null; @@ -127,6 +126,7 @@ public class ElasticBlockStoreFacade { checkState(volumeAvailable.apply(newVolume), /*or throw*/ "Couldn't create a volume from the snapshot"); + elasticBlockStoreServices.deleteSnapshotInRegion(volume.getRegion(), createdSnapshot.getId()); return newVolume; } diff --git a/aws/demos/resize-ebs/resize-ebs-java/src/test/java/org/jclouds/tools/ebsresize/InstanceVolumeManagerLiveTest.java b/aws/demos/resize-ebs/resize-ebs-java/src/test/java/org/jclouds/tools/ebsresize/InstanceVolumeManagerLiveTest.java new file mode 100644 index 0000000000..98a47be1f6 --- /dev/null +++ b/aws/demos/resize-ebs/resize-ebs-java/src/test/java/org/jclouds/tools/ebsresize/InstanceVolumeManagerLiveTest.java @@ -0,0 +1,199 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +package org.jclouds.tools.ebsresize; + +import com.google.common.base.Charsets; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import org.jclouds.aws.domain.Region; +import org.jclouds.aws.ec2.domain.*; +import org.jclouds.aws.ec2.domain.Volume; +import org.jclouds.aws.ec2.predicates.InstanceStateRunning; +import org.jclouds.aws.ec2.reference.EC2Constants; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.*; +import org.jclouds.domain.Credentials; +import org.jclouds.predicates.RetryablePredicate; +import org.testng.annotations.*; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; +import java.util.SortedSet; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.easymock.classextension.EasyMock.*; + + +/** + * Tests the resizing of instance's root EBS device. + * + * This test creates an instance, then resizes its root volume + * (which includes stopping it) and terminates it afterwards. + * + * @author Oleksiy Yarmula + */ +@Test(groups = {"live" }, enabled = false, testName = "ec2.InstanceVolumeManagerLiveTest") +public class InstanceVolumeManagerLiveTest { + + private String tag; + private String secret; + private InstanceVolumeManager manager; + private ComputeService client; + private Template template; + private Predicate instanceRunning; + private RunningInstance instanceCreated; + + @BeforeTest + public void setupClient() throws IOException { + + //set up the constants needed to run + String user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); + String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); + + String secretKeyFile = checkNotNull(System.getProperty("jclouds.test.ssh.keyfile"), + "jclouds.test.ssh.keyfile"); + tag = "ec2"; + + //ended setting up the constants + + secret = Files.toString(new File(secretKeyFile), Charsets.UTF_8); + assert secret.startsWith("-----BEGIN RSA PRIVATE KEY-----") : "invalid key:\n" + secret; + + //create a new manager + Properties testOnlyProperties = new Properties(); + testOnlyProperties.put(EC2Constants.PROPERTY_EC2_AMI_OWNERS, "819060954727"); + manager = new InstanceVolumeManager(user, password, testOnlyProperties); + client = manager.getContext().getComputeService(); + + TemplateBuilder templateBuilder = client.templateBuilder(); + template = + templateBuilder.imageId("ami-2675974f"). + build(); + template.getOptions().installPrivateKey(secret); + + instanceRunning = + new RetryablePredicate(new InstanceStateRunning(manager.getApi(). + getInstanceServices()), + 600, 10, TimeUnit.SECONDS); + } + + @Test + void testResizeVolume() { + SortedSet nodes = Sets.newTreeSet( + client.runNodesWithTag(tag, 1, template).values() + ); + assert nodes.size() == 1 : "Expected to have 1 nodes created; found: " + nodes.size(); + + NodeMetadata launchedNode = Iterables.getOnlyElement(nodes); + + AvailabilityZone availabilityZone = AvailabilityZone.fromValue(launchedNode.getLocationId()); + Region region = getRegionNameForAvailabilityZone(availabilityZone); + + waitForInstanceInRunningState(launchedNode.getId(), region); + + manager.resizeVolume(launchedNode.getId(), region, new Credentials("ubuntu", ""), + secret, 6); + + instanceCreated = + Iterables.getOnlyElement( + Iterables.getOnlyElement( + manager.getApi(). + getInstanceServices(). + describeInstancesInRegion(region, launchedNode.getId() + ) + ) + ); + Volume volumeAttached = manager.getEbsApi().getRootVolumeForInstance(instanceCreated); + assert volumeAttached.getSize() == 6; + } + + @AfterTest + public void close() { + manager.getApi().getInstanceServices().terminateInstancesInRegion + (instanceCreated.getRegion(), instanceCreated.getId()); + manager.closeContext(); + } + + /** + * Returns region that has the given availability zone, or null, + * when nothing is found. + * This operates under the assumption that names of availability zones are + * unique, or else it returns the first matched region. + * + * @param zone + * zone that needs to be matched with region. This can not be null. + * @return region + * that has the provided zone + */ + private Region getRegionNameForAvailabilityZone(AvailabilityZone zone) { + for (Region region : ImmutableSet.of(Region.DEFAULT, Region.EU_WEST_1, Region.US_EAST_1, + Region.US_WEST_1)) { + SortedSet allResults = Sets.newTreeSet(manager.getApi(). + getAvailabilityZoneAndRegionServices() + .describeAvailabilityZonesInRegion(region)); + for(AvailabilityZoneInfo zoneInfo : allResults) { + if(zone == zoneInfo.getZone()) return zoneInfo.getRegion(); + } + } + return null; /*by contract*/ + } + + /** + * Blocks until the instance with given id and region is + * in 'running' state. + * + * @param id + * id of the instance being run + * @param region + * region where the instance is launched + */ + private void waitForInstanceInRunningState(String id, Region region) { + RunningInstance instanceMock = + createInstanceFromIdAndRegion(id, region); + + checkState(instanceRunning.apply(instanceMock), + /*or throw*/ "Couldn't run the instance"); + } + + /** + * Uses EasyMock to create a mock {@link RunningInstance}. + * + * @param id + * id of the instance + * @param region + * region where it's launched + * @return + * instance of mock {@link org.jclouds.aws.ec2.domain.RunningInstance} + */ + private RunningInstance createInstanceFromIdAndRegion(String id, Region region) { + RunningInstance instanceMock = createMock(RunningInstance.class); + expect(instanceMock.getId()).andStubReturn(id); + expect(instanceMock.getRegion()).andStubReturn(region); + replay(instanceMock); + return instanceMock; + } + +}