diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Attachment.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Attachment.java
index a3899edad0..e9caf2c56d 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Attachment.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Attachment.java
@@ -18,20 +18,20 @@
*/
package org.jclouds.ec2.domain;
-import static com.google.common.base.Preconditions.checkNotNull;
-
import java.util.Date;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
- *
+ * @author Adrian Cole
* @see
- * @author Adrian Cole
*/
public class Attachment implements Comparable {
public static enum Status {
ATTACHING, ATTACHED, DETACHING, DETACHED, BUSY, UNRECOGNIZED;
+
public String value() {
return name().toLowerCase();
}
@@ -50,6 +50,53 @@ public class Attachment implements Comparable {
}
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String region;
+ private String volumeId;
+ private String instanceId;
+ private String device;
+ private Status status;
+ private Date attachTime;
+
+ public Builder region(String region) {
+ this.region = region;
+ return this;
+ }
+
+ public Builder volumeId(String volumeId) {
+ this.volumeId = volumeId;
+ return this;
+ }
+
+ public Builder instanceId(String instanceId) {
+ this.instanceId = instanceId;
+ return this;
+ }
+
+ public Builder device(String device) {
+ this.device = device;
+ return this;
+ }
+
+ public Builder status(Status status) {
+ this.status = status;
+ return this;
+ }
+
+ public Builder attachTime(Date attachTime) {
+ this.attachTime = attachTime;
+ return this;
+ }
+
+ public Attachment build() {
+ return new Attachment(region, volumeId, instanceId, device, status, attachTime);
+ }
+ }
+
private final String region;
private final String volumeId;
private final String instanceId;
@@ -68,7 +115,6 @@ public class Attachment implements Comparable {
/**
* Snapshots are tied to Regions and can only be used for volumes within the same Region.
- *
*/
public String getRegion() {
return region;
@@ -167,7 +213,7 @@ public class Attachment implements Comparable {
@Override
public String toString() {
return "Attachment [region=" + region + ", volumeId=" + volumeId + ", instanceId=" + instanceId + ", device="
- + device + ", attachTime=" + attachTime + ", status=" + status + "]";
+ + device + ", attachTime=" + attachTime + ", status=" + status + "]";
}
@Override
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
index 1221b7423d..a512cdc4bf 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/domain/Volume.java
@@ -18,23 +18,22 @@
*/
package org.jclouds.ec2.domain;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableSet;
+import org.jclouds.javax.annotation.Nullable;
import java.util.Date;
import java.util.Set;
-import org.jclouds.javax.annotation.Nullable;
-
-import com.google.common.base.CaseFormat;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Sets.newHashSet;
/**
*
* @see
- * @author Adrian Cole
+ * @author Adrian Cole, Andrei Savu
*/
public class Volume implements Comparable {
@@ -83,6 +82,71 @@ public class Volume implements Comparable {
}
}
}
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String region;
+ private String id;
+ private int size;
+ @Nullable
+ private String snapshotId;
+ private String availabilityZone;
+ private Status status;
+ private Date createTime;
+ private Set attachments;
+
+ public Builder region(String region) {
+ this.region = region;
+ return this;
+ }
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder size(int size) {
+ this.size = size;
+ return this;
+ }
+
+ public Builder snapshotId(String snapshotId) {
+ this.snapshotId = snapshotId;
+ return this;
+ }
+
+ public Builder availabilityZone(String availabilityZone) {
+ this.availabilityZone = availabilityZone;
+ return this;
+ }
+
+ public Builder status(Status status) {
+ this.status = status;
+ return this;
+ }
+
+ public Builder createTime(Date createTime) {
+ this.createTime = createTime;
+ return this;
+ }
+
+ public Builder attachments(Attachment... attachments) {
+ this.attachments = newHashSet(attachments);
+ return this;
+ }
+
+ public Builder attachments(Set attachments) {
+ this.attachments = ImmutableSet.copyOf(attachments);
+ return this;
+ }
+
+ public Volume build() {
+ return new Volume(region, id, size, snapshotId, availabilityZone, status, createTime, attachments);
+ }
+ }
private final String region;
private final String id;
@@ -92,7 +156,7 @@ public class Volume implements Comparable {
private final String availabilityZone;
private final Status status;
private final Date createTime;
- private final Set attachments = Sets.newLinkedHashSet();
+ private final Set attachments;
public Volume(String region, String id, int size, String snapshotId, String availabilityZone, Volume.Status status,
Date createTime, Iterable attachments) {
@@ -103,7 +167,7 @@ public class Volume implements Comparable {
this.availabilityZone = availabilityZone;
this.status = status;
this.createTime = createTime;
- Iterables.addAll(this.attachments, attachments);
+ this.attachments = ImmutableSet.copyOf(attachments);
}
/**
diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/predicates/VolumeDetached.java b/apis/ec2/src/main/java/org/jclouds/ec2/predicates/VolumeDetached.java
index 6a9d113ecd..fe94895be0 100644
--- a/apis/ec2/src/main/java/org/jclouds/ec2/predicates/VolumeDetached.java
+++ b/apis/ec2/src/main/java/org/jclouds/ec2/predicates/VolumeDetached.java
@@ -1,5 +1,3 @@
-package com.gravitant.cloud.adapters.provision.providers;
-
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
@@ -19,54 +17,55 @@ package com.gravitant.cloud.adapters.provision.providers;
* under the License.
*/
-import javax.annotation.Resource;
-import javax.inject.Singleton;
+package org.jclouds.ec2.predicates;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import org.jclouds.ec2.domain.Attachment;
import org.jclouds.ec2.domain.Volume;
import org.jclouds.ec2.services.ElasticBlockStoreClient;
import org.jclouds.logging.Logger;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.google.inject.Inject;
+import javax.annotation.Resource;
+
+import static com.google.common.collect.Iterables.getLast;
/**
- *
* Tests to see if a volume is detached.
- *
+ *
* @author Karthik Arunachalam
*/
@Singleton
public class VolumeDetached implements Predicate {
- private final ElasticBlockStoreClient client;
- @Resource
- protected Logger logger = Logger.NULL;
+ private final ElasticBlockStoreClient client;
+ @Resource
+ protected Logger logger = Logger.NULL;
- @Inject
- public VolumeDetached(ElasticBlockStoreClient client) {
- this.client = client;
- }
+ @Inject
+ public VolumeDetached(ElasticBlockStoreClient client) {
+ this.client = client;
+ }
- public boolean apply(Attachment attachment) {
- logger.trace("looking for volume %s", attachment.getVolumeId());
- Volume volume = Iterables.getOnlyElement(client.describeVolumesInRegion(attachment
- .getRegion(), attachment.getVolumeId()));
+ public boolean apply(Attachment attachment) {
+ logger.trace("looking for volume %s", attachment.getVolumeId());
+ Volume volume = Iterables.getOnlyElement(client.describeVolumesInRegion(attachment
+ .getRegion(), attachment.getVolumeId()));
- /*If attachment size is 0 volume is detached for sure.*/
- if (volume.getAttachments().size() == 0) {
- return true;
- }
+ /*If attachment size is 0 volume is detached for sure.*/
+ if (volume.getAttachments().size() == 0) {
+ return true;
+ }
- /* But if attachment size is > 0, then the attachment could be in any state.
- * So we need to check if the status is DETACHED (return true) or not (return false).
- */
- Attachment lastAttachment = Sets.newTreeSet(volume.getAttachments()).last();
- logger.trace("%s: looking for status %s: currently: %s", lastAttachment,
- Attachment.Status.DETACHED, lastAttachment.getStatus());
- return lastAttachment.getStatus() == Attachment.Status.DETACHED;
- }
+ /* But if attachment size is > 0, then the attachment could be in any state.
+ * So we need to check if the status is DETACHED (return true) or not (return false).
+ */
+ Attachment lastAttachment = getLast(volume.getAttachments());
+ logger.trace("%s: looking for status %s: currently: %s", lastAttachment,
+ Attachment.Status.DETACHED, lastAttachment.getStatus());
+ return lastAttachment.getStatus() == Attachment.Status.DETACHED;
+ }
}
diff --git a/apis/ec2/src/test/java/org/jclouds/ec2/predicates/VolumeDetachedTest.java b/apis/ec2/src/test/java/org/jclouds/ec2/predicates/VolumeDetachedTest.java
new file mode 100644
index 0000000000..76ea1a07cc
--- /dev/null
+++ b/apis/ec2/src/test/java/org/jclouds/ec2/predicates/VolumeDetachedTest.java
@@ -0,0 +1,115 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds licenses this file
+ * to you 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.ec2.predicates;
+
+import org.jclouds.ec2.domain.Attachment;
+import org.jclouds.ec2.domain.Volume;
+import org.jclouds.ec2.services.ElasticBlockStoreClient;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.Date;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.jclouds.ec2.domain.Attachment.Status;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Andrei Savu
+ */
+@Test(groups = "unit", singleThreaded = true)
+public class VolumeDetachedTest {
+
+ private ElasticBlockStoreClient client;
+ private VolumeDetached volumeDetached;
+
+ @BeforeMethod
+ public void setUp() {
+ client = createMock(ElasticBlockStoreClient.class);
+ volumeDetached = new VolumeDetached(client);
+ }
+
+ @Test
+ public void testVolumeWithEmptyListOfAttachments() {
+ Attachment attachment = newAttachmentWithStatus(Status.ATTACHED);
+ Set volumes = newHashSet(newVolumeWithAttachments(/* empty */));
+
+ expect(client.describeVolumesInRegion(attachment.getRegion(),
+ attachment.getVolumeId())).andReturn(volumes);
+ replay(client);
+
+ assertTrue(volumeDetached.apply(attachment));
+ verify(client);
+ }
+
+ @DataProvider(name = "notDetachedStatuses")
+ public Object[][] provideNotDetachedStatuses() {
+ return new Object[][]{
+ {Status.ATTACHED},
+ {Status.ATTACHING},
+ {Status.BUSY},
+ {Status.DETACHING},
+ {Status.UNRECOGNIZED}
+ };
+ }
+
+ @Test(dataProvider = "notDetachedStatuses")
+ public void testWithDifferentStatus(Status attachmentStatus) {
+ Attachment attachment = newAttachmentWithStatus(attachmentStatus);
+ Set volumes = newHashSet(newVolumeWithAttachments(attachment));
+
+ expect(client.describeVolumesInRegion(attachment.getRegion(),
+ attachment.getVolumeId())).andReturn(volumes);
+ replay(client);
+
+ assertFalse(volumeDetached.apply(attachment));
+ verify(client);
+ }
+
+ @Test
+ public void testWithStatusDetached() {
+ Attachment attachment = newAttachmentWithStatus(Status.DETACHED);
+ Set volumes = newHashSet(newVolumeWithAttachments(attachment));
+
+ expect(client.describeVolumesInRegion(attachment.getRegion(),
+ attachment.getVolumeId())).andReturn(volumes);
+ replay(client);
+
+ assertTrue(volumeDetached.apply(attachment));
+ verify(client);
+ }
+
+ private Volume newVolumeWithAttachments(Attachment... attachments) {
+ return Volume.builder().region("us-east-1").attachments(attachments).build();
+ }
+
+ private Attachment newAttachmentWithStatus(Status status) {
+ return Attachment.builder()
+ .volumeId("1").status(status).region("us-east-1").attachTime(new Date())
+ .device("/dev/sda").instanceId("us-east-1/i-1234").build();
+ }
+}