From 6c94f3eb9ec82cda40ee8b1718594789e99805d0 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Mon, 27 Jul 2015 15:10:07 +0200 Subject: [PATCH] JCLOUDS-972. Fix parsing of spot reqs, set sane default for validUntil. Get the faultCode and faultMessage to actually be parsed (though I'm not sure they're ever used), add statusCode, statusMessage and statusUpdateTime, and have AWSEC2TemplateOptions default to a sane 30 minute lifetime for spot instance requests, so they don't get orphaned forever if the price is too low etc. --- .../ec2/compute/AWSEC2TemplateOptions.java | 7 +- .../aws/ec2/domain/SpotInstanceRequest.java | 68 +++++++++++++++++-- .../aws/ec2/xml/SpotInstanceHandler.java | 42 ++++++++++-- .../aws/ec2/xml/SpotInstanceHandlerTest.java | 10 +++ .../test/resources/describe_spot_instance.xml | 7 ++ .../resources/request_spot_instances-ebs.xml | 7 ++ 6 files changed, 131 insertions(+), 10 deletions(-) diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java index 943a6129802..760a0aa9b5c 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateOptions.java @@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.emptyToNull; +import java.util.Calendar; +import java.util.Date; import java.util.Map; import java.util.Set; @@ -217,7 +219,10 @@ public AWSEC2TemplateOptions spotPrice(Float spotPrice) { * Options for starting spot instances */ public AWSEC2TemplateOptions spotOptions(RequestSpotInstancesOptions spotOptions) { - this.spotOptions = spotOptions != null ? spotOptions : RequestSpotInstancesOptions.NONE; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + cal.add(Calendar.MINUTE, 30); + this.spotOptions = spotOptions != null ? spotOptions : RequestSpotInstancesOptions.Builder.validUntil(cal.getTime()); return this; } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/SpotInstanceRequest.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/SpotInstanceRequest.java index 58f5819caba..6ccf36ff966 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/SpotInstanceRequest.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/domain/SpotInstanceRequest.java @@ -21,12 +21,11 @@ import java.util.Date; import java.util.Map; -import org.jclouds.javax.annotation.Nullable; - import com.google.common.base.CaseFormat; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import org.jclouds.javax.annotation.Nullable; public class SpotInstanceRequest implements Comparable { public static Builder builder() { @@ -51,6 +50,9 @@ public static class Builder { private Type type; private Date validFrom; private Date validUntil; + private String statusCode; + private String statusMessage; + private Date statusUpdateTime; private Map tags = Maps.newLinkedHashMap(); public Builder clear() { @@ -71,6 +73,9 @@ public Builder clear() { this.type = null; this.validFrom = null; this.validUntil = null; + this.statusCode = null; + this.statusMessage = null; + this.statusUpdateTime = null; tags = Maps.newLinkedHashMap(); return this; } @@ -170,10 +175,25 @@ public Builder validUntil(Date validUntil) { return this; } + public Builder statusCode(String statusCode) { + this.statusCode = statusCode; + return this; + } + + public Builder statusMessage(String statusMessage) { + this.statusMessage = statusMessage; + return this; + } + + public Builder statusUpdateTime(Date statusUpdateTime) { + this.statusUpdateTime = statusUpdateTime; + return this; + } + public SpotInstanceRequest build() { return new SpotInstanceRequest(region, availabilityZoneGroup, launchedAvailabilityZone, createTime, faultCode, faultMessage, instanceId, launchGroup, launchSpecification, productDescription, id, spotPrice, state, - rawState, type, validFrom, validUntil, tags); + rawState, type, validFrom, validUntil, statusCode, statusMessage, statusUpdateTime, tags); } } @@ -236,12 +256,16 @@ public static State fromValue(String state) { private final Type type; private final Date validFrom; private final Date validUntil; + private final String statusCode; + private final String statusMessage; + private final Date statusUpdateTime; private final Map tags; public SpotInstanceRequest(String region, String availabilityZoneGroup, @Nullable String launchedAvailabilityZone, Date createTime, String faultCode, String faultMessage, String instanceId, String launchGroup, LaunchSpecification launchSpecification, String productDescription, String id, float spotPrice, - State state, String rawState, Type type, Date validFrom, Date validUntil, Map tags) { + State state, String rawState, Type type, Date validFrom, Date validUntil, String statusCode, + String statusMessage, Date statusUpdateTime, Map tags) { this.region = checkNotNull(region, "region"); this.availabilityZoneGroup = availabilityZoneGroup; this.launchedAvailabilityZone = launchedAvailabilityZone; @@ -259,6 +283,9 @@ public SpotInstanceRequest(String region, String availabilityZoneGroup, @Nullabl this.type = checkNotNull(type, "type"); this.validFrom = validFrom; this.validUntil = validUntil; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.statusUpdateTime = statusUpdateTime; this.tags = ImmutableMap. copyOf(checkNotNull(tags, "tags")); } @@ -333,6 +360,18 @@ public Date getValidUntil() { return validUntil; } + public String getStatusCode() { + return statusCode; + } + + public String getStatusMessage() { + return statusMessage; + } + + public Date getStatusUpdateTime() { + return statusUpdateTime; + } + /** * tags that are present in the instance */ @@ -360,6 +399,9 @@ public int hashCode() { result = prime * result + ((type == null) ? 0 : type.hashCode()); result = prime * result + ((validFrom == null) ? 0 : validFrom.hashCode()); result = prime * result + ((validUntil == null) ? 0 : validUntil.hashCode()); + result = prime * result + ((statusCode == null) ? 0 : statusCode.hashCode()); + result = prime * result + ((statusMessage == null) ? 0 : statusMessage.hashCode()); + result = prime * result + ((statusUpdateTime == null) ? 0 : statusUpdateTime.hashCode()); result = prime * result + ((tags == null) ? 0 : tags.hashCode()); return result; } @@ -444,6 +486,21 @@ public boolean equals(Object obj) { return false; } else if (!validUntil.equals(other.validUntil)) return false; + if (statusCode == null) { + if (other.statusCode != null) + return false; + } else if (!statusCode.equals(other.statusCode)) + return false; + if (statusMessage == null) { + if (other.statusMessage != null) + return false; + } else if (!statusMessage.equals(other.statusMessage)) + return false; + if (statusUpdateTime == null) { + if (other.statusUpdateTime != null) + return false; + } else if (!statusUpdateTime.equals(other.statusUpdateTime)) + return false; if (tags == null) { if (other.tags != null) return false; @@ -459,7 +516,8 @@ public String toString() { + faultMessage + ", instanceId=" + instanceId + ", launchGroup=" + launchGroup + ", launchSpecification=" + launchSpecification + ", productDescription=" + productDescription + ", id=" + id + ", spotPrice=" + spotPrice + ", state=" + rawState + ", type=" + type + ", validFrom=" + validFrom + ", validUntil=" - + validUntil + ", tags=" + tags + "]"; + + validUntil + ", statusCode=" + statusCode + ", statusMessage=" + statusMessage + ", statusUpdateTime=" + + statusUpdateTime + ", tags=" + tags + "]"; } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java index 2c940ec4382..87ad3706d13 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/xml/SpotInstanceHandler.java @@ -40,6 +40,8 @@ public class SpotInstanceHandler extends ParseSax.HandlerForGeneratedRequestWith protected final DateCodec dateCodec; protected final Supplier defaultRegion; protected final Builder builder; + protected boolean inFault; + protected boolean inStatus; protected boolean inLaunchSpecification; protected final LaunchSpecificationHandler launchSpecificationHandler; protected boolean inTagSet; @@ -73,6 +75,10 @@ public void startElement(String uri, String name, String qName, Attributes attrs inLaunchSpecification = true; } else if (equalsOrSuffix(qName, "tagSet")) { inTagSet = true; + } else if (equalsOrSuffix(qName, "fault")) { + inFault = true; + } else if (equalsOrSuffix(qName, "status")) { + inStatus = true; } if (inLaunchSpecification) { launchSpecificationHandler.startElement(uri, name, qName, attrs); @@ -97,6 +103,14 @@ public void endElement(String uri, String name, String qName) throws SAXExceptio launchSpecificationHandler.endElement(uri, name, qName); } + if (qName.equals("fault")) { + inFault = false; + } + + if (qName.equals("status")) { + inStatus = false; + } + if (qName.equals("spotInstanceRequestId")) { builder.id(currentOrNull(currentText)); } else if (qName.equals("instanceId")) { @@ -107,10 +121,6 @@ public void endElement(String uri, String name, String qName) throws SAXExceptio builder.availabilityZoneGroup(currentOrNull(currentText)); } else if (qName.equals("launchGroup")) { builder.launchGroup(currentOrNull(currentText)); - } else if (qName.equals("code")) { - builder.faultCode(currentOrNull(currentText)); - } else if (qName.equals("message")) { - builder.faultMessage(currentOrNull(currentText)); } else if (qName.equals("spotPrice")) { String price = currentOrNull(currentText); if (price != null) @@ -131,6 +141,30 @@ public void endElement(String uri, String name, String qName) throws SAXExceptio builder.createTime(dateCodec.toDate(createTime)); } else if (qName.equals("productDescription")) { builder.productDescription(currentOrNull(currentText)); + } else if (inFault) { + if (qName.equals("code")) { + builder.faultCode(currentOrNull(currentText)); + } else if (qName.equals("message")) { + builder.faultMessage(currentOrNull(currentText)); + } + } else if (inStatus) { + if (qName.equals("code")) { + builder.statusCode(currentOrNull(currentText)); + } else if (qName.equals("message")) { + builder.statusMessage(currentOrNull(currentText)); + } else if (qName.equals("updateTime")) { + String updateTime = currentOrNull(currentText); + if (updateTime != null) + builder.statusUpdateTime(dateCodec.toDate(updateTime)); + } + } else if (qName.equals("validFrom")) { + String validFrom = currentOrNull(currentText); + if (validFrom != null) + builder.validFrom(dateCodec.toDate(validFrom)); + } else if (qName.equals("validUntil")) { + String validUntil = currentOrNull(currentText); + if (validUntil != null) + builder.validUntil(dateCodec.toDate(validUntil)); } currentText.setLength(0); } diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java index ff5f230c123..757b9f4a253 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/xml/SpotInstanceHandlerTest.java @@ -77,6 +77,11 @@ public void testApplyInputStream() { .type(Type.ONE_TIME) .state(State.OPEN) .rawState("open") + .statusCode("pending-fulfillment") + .statusMessage("Pending fulfillment") + .statusUpdateTime(new SimpleDateFormatDateService().iso8601DateParse("2011-03-08T03:30:36.000Z")) + .validFrom(new SimpleDateFormatDateService().iso8601DateParse("2011-03-08T03:30:36.000Z")) + .validUntil(new SimpleDateFormatDateService().iso8601DateParse("2011-03-08T03:30:36.000Z")) .launchSpecification( LaunchSpecification.builder().imageId("ami-595a0a1c").securityGroupIdToName("sg-83e1c4ea", "default") .instanceType("m1.large").mapNewVolumeToDevice("/dev/sda1", 1, true) @@ -110,6 +115,9 @@ public void testApplyInputStream1() { .type(Type.ONE_TIME) .state(State.ACTIVE) .rawState("active") + .statusCode("fulfilled") + .statusMessage("Fulfilled") + .statusUpdateTime(new SimpleDateFormatDateService().iso8601DateParse("2011-07-29T05:27:39.000Z")) .launchedAvailabilityZone("us-east-1b") .launchSpecification( LaunchSpecification.builder().imageId("ami-8e1fece7") @@ -117,6 +125,8 @@ public void testApplyInputStream1() { .instanceType("t1.micro").monitoringEnabled(false).keyName("jclouds#adriancole-ec2unssh") .build()) .createTime(new SimpleDateFormatDateService().iso8601DateParse("2011-07-29T05:27:39.000Z")) + .validFrom(new SimpleDateFormatDateService().iso8601DateParse("2011-07-29T05:27:39.000Z")) + .validUntil(new SimpleDateFormatDateService().iso8601DateParse("2011-07-29T05:27:39.000Z")) .productDescription("Linux/UNIX") .tag("Name", "ec2-o") .tag("Spot", "spot-value") diff --git a/providers/aws-ec2/src/test/resources/describe_spot_instance.xml b/providers/aws-ec2/src/test/resources/describe_spot_instance.xml index 964b246b818..c97f5657abe 100644 --- a/providers/aws-ec2/src/test/resources/describe_spot_instance.xml +++ b/providers/aws-ec2/src/test/resources/describe_spot_instance.xml @@ -23,7 +23,14 @@ i-ef308e8e + + fulfilled + Fulfilled + 2011-07-29T05:27:39.000Z + 2011-07-29T05:27:39.000Z + 2011-07-29T05:27:39.000Z + 2011-07-29T05:27:39.000Z Linux/UNIX diff --git a/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml b/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml index 7f646088238..5569f67c769 100644 --- a/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml +++ b/providers/aws-ec2/src/test/resources/request_spot_instances-ebs.xml @@ -45,7 +45,14 @@ Webserver + + pending-fulfillment + Pending fulfillment + 2011-03-08T03:30:36.000Z + 2011-03-08T03:30:36.000Z + 2011-03-08T03:30:36.000Z + 2011-03-08T03:30:36.000Z Linux/UNIX