Issue 27: portability of error handling in eucalyptus where errors are text/plain on 400; changed euc m1.small to accept 64bit

This commit is contained in:
Adrian Cole 2010-09-14 17:32:26 -07:00
parent 15c878c1bf
commit 0d7d2fe778
9 changed files with 139 additions and 54 deletions

View File

@ -21,6 +21,7 @@ package org.jclouds.aws.ec2.compute.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.not;
import static org.jclouds.compute.predicates.ImagePredicates.any;
import static org.jclouds.compute.predicates.ImagePredicates.idIn;
import static org.jclouds.compute.predicates.ImagePredicates.is64Bit;
@ -75,23 +76,24 @@ public class EC2Hardware extends HardwareImpl {
EC2Hardware(String instanceType, Iterable<? extends Processor> processors, Integer ram,
Iterable<? extends Volume> volumes, RootDeviceType rootDeviceType) {
super(instanceType, instanceType, instanceType, null, null, ImmutableMap.<String, String> of(), processors, ram,
volumes, hasRootDeviceType(rootDeviceType));
this(null, instanceType, processors, ram, volumes, hasRootDeviceType(rootDeviceType));
}
EC2Hardware(Location location, String instanceType, Iterable<? extends Processor> processors, Integer ram,
Iterable<? extends Volume> volumes, Predicate<Image> supportsImage) {
super(instanceType, instanceType, instanceType, location, null, ImmutableMap.<String, String> of(), processors,
ram, volumes, supportsImage);
this.instanceType = instanceType;
}
EC2Hardware(String instanceType, Iterable<? extends Processor> processors, Integer ram,
Iterable<? extends Volume> volumes, boolean is64Bit) {
super(instanceType, instanceType, instanceType, null, null, ImmutableMap.<String, String> of(), processors, ram,
volumes, is64Bit ? is64Bit() : not(is64Bit()));
this.instanceType = instanceType;
this(null, instanceType, processors, ram, volumes, is64Bit ? is64Bit() : not(is64Bit()));
}
public EC2Hardware(Location location, String instanceType, Iterable<? extends Processor> processors, Integer ram,
Iterable<? extends Volume> volumes, String[] ids) {
super(instanceType, instanceType, instanceType, location, null, ImmutableMap.<String, String> of(), processors,
ram, volumes, (ids.length == 0 ? is64Bit() : idIn(Arrays.asList(ids))));
this.instanceType = instanceType;
this(location, instanceType, processors, ram, volumes, (ids.length == 0 ? is64Bit() : idIn(Arrays.asList(ids))));
}
/**
@ -107,6 +109,14 @@ public class EC2Hardware extends HardwareImpl {
public static final EC2Hardware M1_SMALL = new EC2Hardware(InstanceType.M1_SMALL, ImmutableList.of(new Processor(
1.0, 1.0)), 1740, ImmutableList.of(new VolumeImpl(10.0f, "/dev/sda1", true, false), new VolumeImpl(150.0f,
"/dev/sda2", false, false)), false);
/**
* In Eucalyptus, m1.small can run 64bit images.
*
* @see InstanceType#M1_SMALL
*/
public static final EC2Hardware M1_SMALL_EUCALYPTUS = new EC2Hardware(null, InstanceType.M1_SMALL, ImmutableList
.of(new Processor(1.0, 1.0)), 1740, ImmutableList.of(new VolumeImpl(10.0f, "/dev/sda1", true, false),
new VolumeImpl(150.0f, "/dev/sda2", false, false)), any());
/**
* @see InstanceType#T1_MICRO

View File

@ -26,7 +26,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.ec2.EC2Client;
import org.jclouds.aws.ec2.compute.domain.RegionNameAndIngressRules;
import org.jclouds.aws.ec2.domain.IpProtocol;
@ -72,13 +71,8 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngres
if (ports.length > 0) {
authorizeGroupToItself(region, name);
}
} catch (AWSResponseException e) {
if (e.getError().getCode().equals("InvalidGroup.Duplicate")
|| e.getError().getMessage().endsWith("already exists")) {
logger.debug("<< reused securityGroup(%s)", name);
} else {
throw e;
}
} catch (IllegalStateException e) {
logger.debug("<< reused securityGroup(%s)", name);
}
}

View File

@ -39,6 +39,7 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationScope;
import org.jclouds.logging.Logger;
import org.jclouds.rest.annotations.Provider;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
@ -56,11 +57,14 @@ public class EC2HardwareSupplier implements Supplier<Set<? extends Hardware>> {
protected Logger logger = Logger.NULL;
private final Supplier<Set<? extends Location>> locations;
private final String[] ccAmis;
private final String providerName;
@Inject
EC2HardwareSupplier(Supplier<Set<? extends Location>> locations, @Named(PROPERTY_EC2_CC_AMIs) String[] ccAmis) {
EC2HardwareSupplier(Supplier<Set<? extends Location>> locations, @Provider String providerName,
@Named(PROPERTY_EC2_CC_AMIs) String[] ccAmis) {
this.locations = locations;
this.ccAmis = ccAmis;
this.providerName=providerName;
}
@Override
@ -82,8 +86,9 @@ public class EC2HardwareSupplier implements Supplier<Set<? extends Hardware>> {
new VolumeImpl(840.0f, "/dev/sdc", false, false)), ccAmis));
}
sizes.addAll(ImmutableSet.<Hardware> of(EC2Hardware.T1_MICRO, EC2Hardware.C1_MEDIUM, EC2Hardware.C1_XLARGE,
EC2Hardware.M1_LARGE, EC2Hardware.M1_SMALL, EC2Hardware.M1_XLARGE, EC2Hardware.M2_XLARGE,
EC2Hardware.M2_2XLARGE, EC2Hardware.M2_4XLARGE));
EC2Hardware.M1_LARGE, "eucalyptus".equals(providerName) ? EC2Hardware.M1_SMALL_EUCALYPTUS
: EC2Hardware.M1_SMALL, EC2Hardware.M1_XLARGE, EC2Hardware.M2_XLARGE, EC2Hardware.M2_2XLARGE,
EC2Hardware.M2_4XLARGE));
return sizes;
}
}

View File

@ -21,6 +21,8 @@ package org.jclouds.aws.handlers;
import static org.jclouds.http.HttpUtils.releasePayload;
import java.io.IOException;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -38,6 +40,7 @@ import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Utils;
import com.google.common.annotations.VisibleForTesting;
@ -65,33 +68,49 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
HttpRequest request = command.getRequest();
Exception exception = new HttpResponseException(command, response);
try {
AWSError error = utils.parseAWSErrorFromContent(request, response);
exception = error != null ? new AWSResponseException(command, response, error) : exception;
String notFoundMessage = error != null ? error.getMessage() : String.format("%s -> %s", request
.getRequestLine(), response.getStatusLine());
AWSError error = null;
String message = null;
if (response.getPayload().getContentType() != null
&& response.getPayload().getContentType().indexOf("xml") != -1) {
error = utils.parseAWSErrorFromContent(request, response);
if (error != null) {
message = error.getMessage();
exception = new AWSResponseException(command, response, error);
}
} else {
try {
message = Utils.toStringAndClose(response.getPayload().getInput());
} catch (IOException e) {
}
}
message = message != null ? message : String.format("%s -> %s", request.getRequestLine(), response
.getStatusLine());
switch (response.getStatusCode()) {
case 400:
if (error != null && error.getCode().endsWith(".NotFound") || error.getCode().endsWith(".Unknown"))
exception = new ResourceNotFoundException(notFoundMessage, exception);
else if (error != null && error.getCode().equals("IncorrectState"))
exception = new IllegalStateException(error.getMessage(), exception);
else if (error != null && error.getCode().equals("AuthFailure"))
exception = new AuthorizationException(command.getRequest(), error != null ? error.getMessage()
: response.getStatusLine());
if (error != null && error.getCode() != null
&& (error.getCode().endsWith(".NotFound") || error.getCode().endsWith(".Unknown")))
exception = new ResourceNotFoundException(message, exception);
else if ((error != null && error.getCode() != null && (error.getCode().equals("IncorrectState") || error
.getCode().equals("InvalidGroup.Duplicate")))
|| (message != null && message.indexOf("already exists") != -1))
exception = new IllegalStateException(message, exception);
else if (error != null && error.getCode() != null && error.getCode().equals("AuthFailure"))
exception = new AuthorizationException(command.getRequest(), message);
else if (message != null && message.indexOf("Failed to bind the following fields") != -1)// Eucalyptus
exception = new IllegalArgumentException(message, exception);
break;
case 401:
case 403:
exception = new AuthorizationException(command.getRequest(), error != null ? error.getMessage()
: response.getStatusLine());
exception = new AuthorizationException(command.getRequest(), message);
break;
case 404:
if (!command.getRequest().getMethod().equals("DELETE")) {
String container = request.getEndpoint().getHost();
String key = request.getEndpoint().getPath();
if (key == null || key.equals("/"))
exception = new ContainerNotFoundException(container, notFoundMessage);
exception = new ContainerNotFoundException(container, message);
else
exception = new KeyNotFoundException(container, key, notFoundMessage);
exception = new KeyNotFoundException(container, key, message);
}
break;
}

View File

@ -303,12 +303,24 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
private void cleanupExtendedStuff(SecurityGroupClient securityGroupClient, KeyPairClient keyPairClient, String tag)
throws InterruptedException {
try {
securityGroupClient.deleteSecurityGroupInRegion(null, tag);
for (SecurityGroup group : securityGroupClient.describeSecurityGroupsInRegion(null))
if (group.getName().startsWith("jclouds#" + tag) || group.getName().equals(tag)) {
System.err.printf("deleting group %s%n", group.getName());
securityGroupClient.deleteSecurityGroupInRegion(null, group.getName());
} else {
System.err.printf("group %s didn't match %s%n", group.getName(), tag);
}
} catch (Exception e) {
}
try {
keyPairClient.deleteKeyPairInRegion(null, tag);
for (KeyPair pair : keyPairClient.describeKeyPairsInRegion(null))
if (pair.getKeyName().startsWith("jclouds#" + tag) || pair.getKeyName().equals(tag)) {
System.err.printf("deleting key %s%n", pair.getKeyName());
keyPairClient.deleteKeyPairInRegion(null, pair.getKeyName());
} else {
System.err.printf("key %s didn't match %s%n", pair.getKeyName(), tag);
}
} catch (Exception e) {
}

View File

@ -46,8 +46,7 @@ public class EucalyptusComputeServiceLiveTest extends EC2ComputeServiceLiveTest
Template defaultTemplate = client.templateBuilder().build();
assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.CENTOS);
// 64 bit implied 4 ecus
assertEquals(getCores(template.getHardware()), 4.0d);
assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
}
}

View File

@ -69,6 +69,30 @@ public class ParseAWSErrorFromXmlContentTest {
"<Error><Code>IncorrectState</Code></Error>", IllegalStateException.class);
}
@Test
public void test400WithInvalidGroupDuplicateIllegalStateException() {
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
"<Error><Code>InvalidGroup.Duplicate</Code></Error>", IllegalStateException.class);
}
@Test
public void test400WithTextPlainIllegalArgumentException() {
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "Bad Request", "text/plain",
"Failure: 400 Bad Request\nFailed to bind the following fields\nMonitoring.Enabled = true\n\n\n",
IllegalArgumentException.class);
}
@Test
public void test400WithGroupAlreadyExistsEucalyptusIllegalStateException() {
assertCodeMakes(
"GET",
URI.create("https://amazonaws.com/foo"),
400,
"",
"<?xml version=\"1.0\"?><Response><Errors><Error><Code>Groups</Code><Message>\nError adding network group: group named jclouds#eucrun#Eucalyptus already exists\nError adding network group: group named jclouds#eucrun#Eucalyptus already exists</Message></Error></Errors><RequestID>e0133975-3bc5-456d-9753-1d61b27e07e9</RequestID></Response>",
IllegalStateException.class);
}
@Test
public void test400WithAuthFailureSetsAuthorizationException() {
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
@ -77,6 +101,11 @@ public class ParseAWSErrorFromXmlContentTest {
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String content,
Class<? extends Exception> expected) {
assertCodeMakes(method, uri, statusCode, message, "text/xml", content, expected);
}
private void assertCodeMakes(String method, URI uri, int statusCode, String message, String contentType,
String content, Class<? extends Exception> expected) {
ParseAWSErrorFromXmlContent function = Guice.createInjector(new SaxParserModule(), new AbstractModule() {
@ -92,8 +121,7 @@ public class ParseAWSErrorFromXmlContentTest {
HttpRequest request = new HttpRequest(method, uri);
HttpResponse response = new HttpResponse(statusCode, message, Payloads.newInputStreamPayload(Utils
.toInputStream(content)));
response.getPayload().setContentType("text/xml");
//TODO also check application/unknown
response.getPayload().setContentType(contentType);
expect(command.getRequest()).andReturn(request).atLeastOnce();
command.setException(classEq(expected));

View File

@ -33,23 +33,30 @@ public class ErrorHandlerTest extends BaseHandlerTest {
public static final String errorFromAmazonIfYouDontRemoveTransferEncodingHeader = "<Error><Code>NotImplemented</Code><Message>A header you provided implies functionality that is not implemented</Message><Header>Transfer-Encoding</Header><RequestId>7C59925D75D15561</RequestId><HostId>fbskVU51OZJg2yZS/wNIxoE2PmCf0ZqFd0iH6Vrzw0uKG3KmokswBytL/Bfp/GWb</HostId></Error>";
ParseSax<AWSError> createParser() {
ParseSax<AWSError> parser = (ParseSax<AWSError>) factory.create(injector
.getInstance(ErrorHandler.class));
ParseSax<AWSError> parser = (ParseSax<AWSError>) factory.create(injector.getInstance(ErrorHandler.class));
return parser;
}
@Test
public void testErrorFromAmazonIfYouDontRemoveTransferEncodingHeader() throws HttpException {
ParseSax<AWSError> parser = createParser();
AWSError error = parser.parse(Utils
.toInputStream(errorFromAmazonIfYouDontRemoveTransferEncodingHeader));
AWSError error = parser.parse(Utils.toInputStream(errorFromAmazonIfYouDontRemoveTransferEncodingHeader));
assertEquals(error.getCode(), "NotImplemented");
assertEquals(error.getMessage(),
"A header you provided implies functionality that is not implemented");
assertEquals(error.getMessage(), "A header you provided implies functionality that is not implemented");
assertEquals(error.getDetails().get("Header"), "Transfer-Encoding");
assertEquals(error.getRequestId(), "7C59925D75D15561");
assertEquals(error.getDetails().get("HostId"),
"fbskVU51OZJg2yZS/wNIxoE2PmCf0ZqFd0iH6Vrzw0uKG3KmokswBytL/Bfp/GWb");
assertEquals(error.getDetails().get("HostId"), "fbskVU51OZJg2yZS/wNIxoE2PmCf0ZqFd0iH6Vrzw0uKG3KmokswBytL/Bfp/GWb");
}
@Test
public void testErrorFromEucalyptusWhenGroupAlreadyExists() throws HttpException {
ParseSax<AWSError> parser = createParser();
AWSError error = parser
.parse(Utils
.toInputStream("<?xml version=\"1.0\"?><Response><Errors><Error><Code>Groups</Code><Message>\nError adding network group: group named jclouds#eucrun#Eucalyptus already exists\nError adding network group: group named jclouds#eucrun#Eucalyptus already exists</Message></Error></Errors><RequestID>e0133975-3bc5-456d-9753-1d61b27e07e9</RequestID></Response>"));
assertEquals(error.getCode(), "Groups");
assertEquals(
error.getMessage(),
"Error adding network group: group named jclouds#eucrun#Eucalyptus already exists\nError adding network group: group named jclouds#eucrun#Eucalyptus already exists");
}
public static final String badRequestWhenSourceIsDestBucketOnCopy400 = "<Error><Code>InvalidRequest</Code><Message>The Source and Destination may not be the same when the MetadataDirective is Copy.</Message><RequestId>54C77CAF4D42474B</RequestId><HostId>SJecknEUUUx88/65VAKbCdKSOCkpuVTeu7ZG9in9x9NTNglGnoxdbALCfS4k/DUZ</HostId></Error>";

View File

@ -19,11 +19,13 @@
package org.jclouds.compute.predicates;
import java.util.Set;
import org.jclouds.compute.domain.OperatingSystem;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
/**
* Container for operating system filters (predicates).
@ -48,7 +50,7 @@ public class OperatingSystemPredicates {
return false;
}
}
for (String toMatch : ImmutableSet.of(os.getName(), os.getDescription()))
for (String toMatch : searchStrings(os))
if (toMatch != null && toMatch.toLowerCase().indexOf("windows") != -1)
return false;
return true;
@ -76,7 +78,7 @@ public class OperatingSystemPredicates {
return true;
}
}
for (String toMatch : ImmutableSet.of(os.getName(), os.getDescription()))
for (String toMatch : searchStrings(os))
if (toMatch != null && toMatch.toLowerCase().indexOf("ubuntu") != -1
|| toMatch.toLowerCase().indexOf("debian") != -1)
return true;
@ -107,7 +109,7 @@ public class OperatingSystemPredicates {
}
}
for (String toMatch : ImmutableSet.of(os.getName(), os.getDescription()))
for (String toMatch : searchStrings(os))
if (toMatch.toLowerCase().indexOf("centos") != -1 || toMatch.toLowerCase().indexOf("rhel") != -1
|| toMatch.toLowerCase().replace(" ", "").indexOf("redhate") != -1
|| toMatch.toLowerCase().indexOf("fedora") != -1)
@ -136,7 +138,7 @@ public class OperatingSystemPredicates {
return true;
}
}
for (String toMatch : ImmutableSet.of(os.getName(), os.getDescription()))
for (String toMatch : searchStrings(os))
if (toMatch != null && toMatch.toLowerCase().indexOf("suse") != -1)
return true;
return false;
@ -173,4 +175,13 @@ public class OperatingSystemPredicates {
};
}
static Iterable<String> searchStrings(OperatingSystem os) {
Set<String> search = Sets.newLinkedHashSet();
if (os.getName() != null)
search.add(os.getName());
if (os.getDescription() != null)
search.add(os.getDescription());
return search;
}
}