mirror of https://github.com/apache/jclouds.git
Issue 299: converted 409 -> IllegalStateException and mapped AWS and vCloud exceptions
This commit is contained in:
parent
85b5bd48f1
commit
f29d003914
|
@ -64,8 +64,8 @@ public class AWSClientErrorRetryHandler implements HttpRetryHandler {
|
|||
// Content can be null in the case of HEAD requests
|
||||
if (content != null) {
|
||||
try {
|
||||
AWSError error = utils.parseAWSErrorFromContent(command, response, new String(
|
||||
content));
|
||||
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response,
|
||||
new String(content));
|
||||
if ("RequestTimeout".equals(error.getCode())
|
||||
|| "OperationAborted".equals(error.getCode())
|
||||
|| "SignatureDoesNotMatch".equals(error.getCode())) {
|
||||
|
|
|
@ -64,8 +64,8 @@ public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
|
|||
} else {
|
||||
command.incrementRedirectCount();
|
||||
try {
|
||||
AWSError error = utils.parseAWSErrorFromContent(command, response, new String(
|
||||
content));
|
||||
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response,
|
||||
new String(content));
|
||||
String host = error.getDetails().get("Endpoint");
|
||||
if (host != null) {
|
||||
if (host.equals(command.getRequest().getEndpoint().getHost())) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.jclouds.blobstore.ContainerNotFoundException;
|
|||
import org.jclouds.blobstore.KeyNotFoundException;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.logging.Logger;
|
||||
|
@ -38,6 +39,7 @@ import org.jclouds.rest.AuthorizationException;
|
|||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.io.Closeables;
|
||||
|
||||
/**
|
||||
|
@ -52,43 +54,47 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
|
|||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
private final AWSUtils utils;
|
||||
@VisibleForTesting
|
||||
final AWSUtils utils;
|
||||
|
||||
@Inject
|
||||
public ParseAWSErrorFromXmlContent(AWSUtils utils) {
|
||||
ParseAWSErrorFromXmlContent(AWSUtils utils) {
|
||||
this.utils = utils;
|
||||
}
|
||||
|
||||
public void handleError(HttpCommand command, HttpResponse response) {
|
||||
HttpRequest request = command.getRequest();
|
||||
Exception exception = new HttpResponseException(command, response);
|
||||
try {
|
||||
AWSError error = parseErrorFromContentOrNull(command, response);
|
||||
exception = error != null ? new AWSResponseException(command,
|
||||
response, error) : exception;
|
||||
AWSError error = parseErrorFromContentOrNull(request, response);
|
||||
exception = error != null ? new AWSResponseException(command, response, error) : exception;
|
||||
switch (response.getStatusCode()) {
|
||||
case 400:
|
||||
if (error.getCode().endsWith(".NotFound"))
|
||||
exception = new ResourceNotFoundException(error.getMessage(),
|
||||
exception);
|
||||
break;
|
||||
case 401:
|
||||
case 403:
|
||||
exception = new AuthorizationException(command.getRequest(),
|
||||
error != null ? error.getMessage() : response.getStatusLine());
|
||||
break;
|
||||
case 404:
|
||||
if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
String message = error != null ? error.getMessage() : String
|
||||
.format("%s -> %s", command.getRequest().getRequestLine(),
|
||||
response.getStatusLine());
|
||||
String container = command.getRequest().getEndpoint().getHost();
|
||||
String key = command.getRequest().getEndpoint().getPath();
|
||||
if (key == null || key.equals("/"))
|
||||
exception = new ContainerNotFoundException(container, message);
|
||||
else
|
||||
exception = new KeyNotFoundException(container, key, message);
|
||||
}
|
||||
break;
|
||||
case 400:
|
||||
if (error.getCode().endsWith(".NotFound"))
|
||||
exception = new ResourceNotFoundException(error.getMessage(), exception);
|
||||
else if (error.getCode().equals("IncorrectState"))
|
||||
exception = new IllegalStateException(error.getMessage(), exception);
|
||||
else if (error.getCode().equals("AuthFailure"))
|
||||
exception = new AuthorizationException(command.getRequest(),
|
||||
error != null ? error.getMessage() : response.getStatusLine());
|
||||
break;
|
||||
case 401:
|
||||
case 403:
|
||||
exception = new AuthorizationException(command.getRequest(), error != null ? error
|
||||
.getMessage() : response.getStatusLine());
|
||||
break;
|
||||
case 404:
|
||||
if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
String message = error != null ? error.getMessage() : String.format("%s -> %s",
|
||||
request.getRequestLine(), response.getStatusLine());
|
||||
String container = request.getEndpoint().getHost();
|
||||
String key = request.getEndpoint().getPath();
|
||||
if (key == null || key.equals("/"))
|
||||
exception = new ContainerNotFoundException(container, message);
|
||||
else
|
||||
exception = new KeyNotFoundException(container, key, message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
Closeables.closeQuietly(response.getContent());
|
||||
|
@ -96,14 +102,12 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
|
|||
}
|
||||
}
|
||||
|
||||
AWSError parseErrorFromContentOrNull(HttpCommand command,
|
||||
HttpResponse response) {
|
||||
AWSError parseErrorFromContentOrNull(HttpRequest request, HttpResponse response) {
|
||||
if (response.getContent() != null) {
|
||||
try {
|
||||
String content = Utils.toStringAndClose(response.getContent());
|
||||
if (content != null && content.indexOf('<') >= 0)
|
||||
return utils
|
||||
.parseAWSErrorFromContent(command, response, content);
|
||||
return utils.parseAWSErrorFromContent(request, response, content);
|
||||
} catch (IOException e) {
|
||||
logger.warn(e, "exception reading error from response", response);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public class S3Utils {
|
|||
|
||||
public AWSError parseAWSErrorFromContent(HttpCommand command, HttpResponse response,
|
||||
InputStream content) throws HttpException {
|
||||
AWSError error = util.parseAWSErrorFromContent(command, response, content);
|
||||
AWSError error = util.parseAWSErrorFromContent(command.getRequest(), response, content);
|
||||
if (error.getRequestId() == null)
|
||||
error.setRequestId(response.getFirstHeaderOrNull(S3Headers.REQUEST_ID));
|
||||
error.setRequestToken(response.getFirstHeaderOrNull(S3Headers.REQUEST_TOKEN));
|
||||
|
|
|
@ -23,12 +23,14 @@ import java.io.InputStream;
|
|||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.aws.domain.AWSError;
|
||||
import org.jclouds.aws.xml.ErrorHandler;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.http.functions.ParseSax.Factory;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
|
||||
/**
|
||||
|
@ -36,30 +38,32 @@ import org.jclouds.rest.RequestSigner;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class AWSUtils {
|
||||
private final RequestSigner signer;
|
||||
private final ParseSax.Factory factory;
|
||||
private final Provider<ErrorHandler> errorHandlerProvider;
|
||||
|
||||
@Inject
|
||||
RequestSigner signer;
|
||||
AWSUtils(RequestSigner signer, Factory factory, Provider<ErrorHandler> errorHandlerProvider) {
|
||||
this.signer = signer;
|
||||
this.factory = factory;
|
||||
this.errorHandlerProvider = errorHandlerProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
ParseSax.Factory factory;
|
||||
|
||||
@Inject
|
||||
Provider<ErrorHandler> errorHandlerProvider;
|
||||
|
||||
public AWSError parseAWSErrorFromContent(HttpCommand command, HttpResponse response,
|
||||
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response,
|
||||
InputStream content) {
|
||||
AWSError error = (AWSError) factory.create(errorHandlerProvider.get()).parse(content);
|
||||
if ("SignatureDoesNotMatch".equals(error.getCode())) {
|
||||
error.setStringSigned(signer.createStringToSign(command.getRequest()));
|
||||
error.setStringSigned(signer.createStringToSign(request));
|
||||
error.setSignature(signer.sign(error.getStringSigned()));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
public AWSError parseAWSErrorFromContent(HttpCommand command, HttpResponse response,
|
||||
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response,
|
||||
String content) {
|
||||
return parseAWSErrorFromContent(command, response, new ByteArrayInputStream(content
|
||||
return parseAWSErrorFromContent(request, response, new ByteArrayInputStream(content
|
||||
.getBytes()));
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
package org.jclouds.aws.xml;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.aws.domain.AWSError;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
|
||||
|
@ -29,6 +31,7 @@ import org.jclouds.http.functions.ParseSax;
|
|||
* />
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ErrorHandler extends ParseSax.HandlerWithResult<AWSError> {
|
||||
|
||||
private AWSError error = new AWSError();
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package org.jclouds.aws.handlers;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.reportMatcher;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.config.ParserModule;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.RequestSigner;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.jclouds.util.Utils;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" })
|
||||
public class ParseAWSErrorFromXmlContentTest {
|
||||
|
||||
@Test
|
||||
public void test400WithNotFoundSetsResourceNotFoundException() {
|
||||
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
|
||||
"<Error><Code>Monster.NotFound</Code></Error>", ResourceNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test400WithIncorrectStateSetsIllegalStateException() {
|
||||
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
|
||||
"<Error><Code>IncorrectState</Code></Error>", IllegalStateException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test400WithAuthFailureSetsAuthorizationException() {
|
||||
assertCodeMakes("GET", URI.create("https://amazonaws.com/foo"), 400, "",
|
||||
"<Error><Code>AuthFailure</Code></Error>", AuthorizationException.class);
|
||||
}
|
||||
|
||||
private void assertCodeMakes(String method, URI uri, int statusCode, String message,
|
||||
String content, Class<? extends Exception> expected) {
|
||||
|
||||
ParseAWSErrorFromXmlContent function = Guice.createInjector(new ParserModule(),
|
||||
new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(RequestSigner.class).toInstance(createMock(RequestSigner.class));
|
||||
|
||||
}
|
||||
|
||||
}).getInstance(ParseAWSErrorFromXmlContent.class);
|
||||
|
||||
HttpCommand command = createMock(HttpCommand.class);
|
||||
HttpRequest request = new HttpRequest(method, uri);
|
||||
HttpResponse response = new HttpResponse(Utils.toInputStream(content));
|
||||
response.setStatusCode(statusCode);
|
||||
response.setMessage(message);
|
||||
|
||||
expect(command.getRequest()).andReturn(request).atLeastOnce();
|
||||
command.setException(classEq(expected));
|
||||
|
||||
replay(command);
|
||||
|
||||
function.handleError(command, response);
|
||||
|
||||
verify(command);
|
||||
}
|
||||
|
||||
public static Exception classEq(final Class<? extends Exception> in) {
|
||||
reportMatcher(new IArgumentMatcher() {
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("classEq(");
|
||||
buffer.append(in);
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object arg) {
|
||||
return arg.getClass() == in;
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -18,7 +18,9 @@
|
|||
*/
|
||||
package org.jclouds.aws.s3.util;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -29,6 +31,7 @@ import org.jclouds.aws.domain.AWSError;
|
|||
import org.jclouds.aws.s3.reference.S3Headers;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.logging.config.NullLoggingModule;
|
||||
import org.jclouds.rest.RestContextFactory;
|
||||
|
@ -46,7 +49,7 @@ import com.google.inject.Injector;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" }, testName = "s3.S3UtilsTest")
|
||||
@Test(sequential = true, groups = { "unit" }, testName = "s3.S3UtilsTest")
|
||||
public class S3UtilsTest {
|
||||
S3Utils utils = null;
|
||||
private HttpResponse response;
|
||||
|
@ -65,7 +68,8 @@ public class S3UtilsTest {
|
|||
response.getHeaders().put(S3Headers.REQUEST_ID, "requestid");
|
||||
response.getHeaders().put(S3Headers.REQUEST_TOKEN, "requesttoken");
|
||||
command = createMock(HttpCommand.class);
|
||||
|
||||
expect(command.getRequest()).andReturn(createMock(HttpRequest.class)).atLeastOnce();
|
||||
replay(command);
|
||||
}
|
||||
|
||||
@AfterTest
|
||||
|
|
|
@ -1,29 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
|
||||
Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
|
||||
====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF 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.
|
||||
====================================================================
|
||||
|
||||
-->
|
||||
<Error>
|
||||
<Code>NoSuchKey</Code>
|
||||
<Message>The resource you requested does not exist</Message>
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
package org.jclouds.http.internal;
|
||||
|
||||
import static org.jclouds.concurrent.ConcurrentUtils.makeListenable;
|
||||
import static org.jclouds.http.HttpUtils.logRequest;
|
||||
import static org.jclouds.http.HttpUtils.logResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Callable;
|
||||
|
@ -35,7 +37,6 @@ import org.jclouds.http.HttpRequest;
|
|||
import org.jclouds.http.HttpRequestFilter;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.http.HttpUtils;
|
||||
import org.jclouds.http.IOExceptionRetryHandler;
|
||||
import org.jclouds.http.Payloads;
|
||||
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||
|
@ -102,7 +103,7 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
|
|||
request.setPayload(Payloads.newPayload(wire.output(request.getPayload()
|
||||
.getRawContent())));
|
||||
nativeRequest = convert(request);
|
||||
HttpUtils.logRequest(headerLog, request, ">>");
|
||||
logRequest(headerLog, request, ">>");
|
||||
try {
|
||||
response = invoke(nativeRequest);
|
||||
} catch (IOException e) {
|
||||
|
@ -117,7 +118,7 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
|
|||
}
|
||||
logger.debug("Receiving response %s: %s", request.hashCode(), response
|
||||
.getStatusLine());
|
||||
HttpUtils.logResponse(headerLog, response, "<<");
|
||||
logResponse(headerLog, response, "<<");
|
||||
if (response.getContent() != null && wire.enabled())
|
||||
response.setContent(wire.input(response.getContent()));
|
||||
int statusCode = response.getStatusCode();
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.jclouds.rest.functions;
|
|||
|
||||
import static org.jclouds.util.Utils.propagateOrNull;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
|
@ -30,6 +32,7 @@ import com.google.common.base.Function;
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class MapHttp4xxCodesToExceptions implements Function<Exception, Object> {
|
||||
|
||||
public Object apply(Exception from) {
|
||||
|
@ -42,6 +45,8 @@ public class MapHttp4xxCodesToExceptions implements Function<Exception, Object>
|
|||
throw new AuthorizationException(from);
|
||||
case 404:
|
||||
throw new ResourceNotFoundException(from);
|
||||
case 409:
|
||||
throw new IllegalStateException(from);
|
||||
}
|
||||
}
|
||||
return propagateOrNull(from);
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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.http;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.reportMatcher;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.functions.config.ParserModule;
|
||||
import org.jclouds.util.Utils;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public abstract class BaseHttpErrorHandlerTest {
|
||||
|
||||
public static Exception classEq(final Class<? extends Exception> in) {
|
||||
reportMatcher(new IArgumentMatcher() {
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("classEq(");
|
||||
buffer.append(in);
|
||||
buffer.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object arg) {
|
||||
return arg.getClass() == in;
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract Class<? extends HttpErrorHandler> getHandlerClass();
|
||||
|
||||
protected void assertCodeMakes(String method, URI uri, int statusCode, String message,
|
||||
String content, Class<? extends Exception> expected) {
|
||||
|
||||
HttpErrorHandler function = Guice.createInjector(new ParserModule()).getInstance(
|
||||
getHandlerClass());
|
||||
|
||||
HttpCommand command = createMock(HttpCommand.class);
|
||||
HttpRequest request = new HttpRequest(method, uri);
|
||||
HttpResponse response = new HttpResponse(Utils.toInputStream(content));
|
||||
response.setStatusCode(statusCode);
|
||||
response.setMessage(message);
|
||||
|
||||
expect(command.getRequest()).andReturn(request).atLeastOnce();
|
||||
command.setException(classEq(expected));
|
||||
|
||||
replay(command);
|
||||
|
||||
function.handleError(command, response);
|
||||
|
||||
verify(command);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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.rest.functions;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.classextension.EasyMock.createMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
import static org.easymock.classextension.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.http.HttpResponse;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" })
|
||||
public class MapHttp4xxCodesToExceptionsTest {
|
||||
|
||||
@Test
|
||||
public void test401And403ToAuthorizationException() {
|
||||
assertCodeMakes(401, AuthorizationException.class);
|
||||
assertCodeMakes(403, AuthorizationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test404ToResourceNotFoundException() {
|
||||
assertCodeMakes(404, ResourceNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test409ToIllegalStateException() {
|
||||
assertCodeMakes(409, IllegalStateException.class);
|
||||
}
|
||||
|
||||
private void assertCodeMakes(int statuscode, Class<?> expected) {
|
||||
Function<Exception, Object> function = new MapHttp4xxCodesToExceptions();
|
||||
HttpResponseException responseException = createMock(HttpResponseException.class);
|
||||
|
||||
HttpResponse response = createMock(HttpResponse.class);
|
||||
expect(response.getStatusCode()).andReturn(statuscode).atLeastOnce();
|
||||
expect(responseException.getResponse()).andReturn(response).atLeastOnce();
|
||||
|
||||
replay(responseException);
|
||||
replay(response);
|
||||
|
||||
try {
|
||||
function.apply(responseException);
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertEquals(e.getClass(), expected);
|
||||
}
|
||||
|
||||
verify(responseException);
|
||||
verify(response);
|
||||
}
|
||||
|
||||
}
|
|
@ -50,8 +50,8 @@ import com.google.common.collect.ImmutableMap;
|
|||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Configures the {@link VCloudComputeServiceContext}; requires
|
||||
* {@link BaseVCloudComputeClient} bound.
|
||||
* Configures the {@link VCloudComputeServiceContext}; requires {@link BaseVCloudComputeClient}
|
||||
* bound.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
|
@ -70,59 +70,53 @@ public class VCloudGetNodeMetadata {
|
|||
|
||||
// hex [][][] are templateId, last two are instanceId
|
||||
public static final Pattern TAG_PATTERN_WITH_TEMPLATE = Pattern
|
||||
.compile("([^-]+)-([0-9a-f][0-9a-f][0-9a-f])[0-9a-f]+");
|
||||
.compile("([^-]+)-([0-9a-f][0-9a-f][0-9a-f])[0-9a-f]+");
|
||||
|
||||
public static final Pattern TAG_PATTERN_WITHOUT_TEMPLATE = Pattern
|
||||
.compile("([^-]+)-[0-9]+");
|
||||
public static final Pattern TAG_PATTERN_WITHOUT_TEMPLATE = Pattern.compile("([^-]+)-[0-9]+");
|
||||
|
||||
@Inject
|
||||
VCloudGetNodeMetadata(VCloudClient client,
|
||||
VCloudComputeClient computeClient,
|
||||
Map<VAppStatus, NodeState> vAppStatusToNodeState, GetExtra getExtra,
|
||||
FindLocationForResourceInVDC findLocationForResourceInVDC,
|
||||
Provider<Set<? extends Image>> images) {
|
||||
VCloudGetNodeMetadata(VCloudClient client, VCloudComputeClient computeClient,
|
||||
Map<VAppStatus, NodeState> vAppStatusToNodeState, GetExtra getExtra,
|
||||
FindLocationForResourceInVDC findLocationForResourceInVDC,
|
||||
Provider<Set<? extends Image>> images) {
|
||||
this.client = checkNotNull(client, "client");
|
||||
this.images = checkNotNull(images, "images");
|
||||
this.getExtra = checkNotNull(getExtra, "getExtra");
|
||||
this.findLocationForResourceInVDC = checkNotNull(
|
||||
findLocationForResourceInVDC, "findLocationForResourceInVDC");
|
||||
this.findLocationForResourceInVDC = checkNotNull(findLocationForResourceInVDC,
|
||||
"findLocationForResourceInVDC");
|
||||
this.computeClient = checkNotNull(computeClient, "computeClient");
|
||||
this.vAppStatusToNodeState = checkNotNull(vAppStatusToNodeState,
|
||||
"vAppStatusToNodeState");
|
||||
this.vAppStatusToNodeState = checkNotNull(vAppStatusToNodeState, "vAppStatusToNodeState");
|
||||
}
|
||||
|
||||
public NodeMetadata execute(String id) {
|
||||
VApp vApp = client.getVApp(id);
|
||||
if (vApp == null)
|
||||
return null;
|
||||
|
||||
String tag = null;
|
||||
Image image = null;
|
||||
Matcher matcher = vApp.getName() != null ? TAG_PATTERN_WITH_TEMPLATE
|
||||
.matcher(vApp.getName()) : null;
|
||||
Matcher matcher = vApp.getName() != null ? TAG_PATTERN_WITH_TEMPLATE.matcher(vApp.getName())
|
||||
: null;
|
||||
|
||||
final Location location = findLocationForResourceInVDC.apply(vApp, vApp
|
||||
.getVDC().getId());
|
||||
final Location location = findLocationForResourceInVDC.apply(vApp, vApp.getVDC().getId());
|
||||
if (matcher != null && matcher.find()) {
|
||||
tag = matcher.group(1);
|
||||
String templateIdInHexWithoutLeadingZeros = matcher.group(2)
|
||||
.replaceAll("^[0]+", "");
|
||||
final String templateId = Integer.parseInt(
|
||||
templateIdInHexWithoutLeadingZeros, 16)
|
||||
+ "";
|
||||
String templateIdInHexWithoutLeadingZeros = matcher.group(2).replaceAll("^[0]+", "");
|
||||
final String templateId = Integer.parseInt(templateIdInHexWithoutLeadingZeros, 16) + "";
|
||||
try {
|
||||
image = Iterables.find(images.get(), new Predicate<Image>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(Image input) {
|
||||
return input.getProviderId().equals(templateId)
|
||||
&& input.getLocation().equals(location);
|
||||
&& input.getLocation().equals(location);
|
||||
}
|
||||
|
||||
});
|
||||
} catch (NoSuchElementException e) {
|
||||
logger
|
||||
.warn(
|
||||
"could not find a matching image for vapp %s; vapptemplate %s in location %s",
|
||||
vApp, templateId, location);
|
||||
logger.warn(
|
||||
"could not find a matching image for vapp %s; vapptemplate %s in location %s",
|
||||
vApp, templateId, location);
|
||||
}
|
||||
} else {
|
||||
matcher = TAG_PATTERN_WITHOUT_TEMPLATE.matcher(vApp.getName());
|
||||
|
@ -132,10 +126,9 @@ public class VCloudGetNodeMetadata {
|
|||
tag = "NOTAG-" + vApp.getName();
|
||||
}
|
||||
}
|
||||
return new NodeMetadataImpl(vApp.getId(), vApp.getName(), vApp.getId(),
|
||||
location, vApp.getLocation(), ImmutableMap.<String, String> of(),
|
||||
tag, image, vAppStatusToNodeState.get(vApp.getStatus()),
|
||||
computeClient.getPublicAddresses(id), computeClient
|
||||
.getPrivateAddresses(id), getExtra.apply(vApp), null);
|
||||
return new NodeMetadataImpl(vApp.getId(), vApp.getName(), vApp.getId(), location, vApp
|
||||
.getLocation(), ImmutableMap.<String, String> of(), tag, image,
|
||||
vAppStatusToNodeState.get(vApp.getStatus()), computeClient.getPublicAddresses(id),
|
||||
computeClient.getPrivateAddresses(id), getExtra.apply(vApp), null);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
|
@ -41,11 +42,11 @@ import com.google.common.io.Closeables;
|
|||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@Singleton
|
||||
public class ParseVCloudErrorFromHttpResponse implements HttpErrorHandler {
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
public static final Pattern RESOURCE_PATTERN = Pattern
|
||||
.compile(".*/v[^/]*/[0-9]+/([^/]+)/([0-9]+)");
|
||||
public static final Pattern RESOURCE_PATTERN = Pattern.compile(".*/v[^/]+/([^/]+)/([0-9]+)");
|
||||
|
||||
public void handleError(HttpCommand command, HttpResponse response) {
|
||||
Exception exception = new HttpResponseException(command, response);
|
||||
|
@ -56,7 +57,6 @@ public class ParseVCloudErrorFromHttpResponse implements HttpErrorHandler {
|
|||
case 401:
|
||||
exception = new AuthorizationException(command.getRequest(), content);
|
||||
break;
|
||||
case 403: // TODO temporary as terremark mistakenly uses this for vApp not found.
|
||||
case 404:
|
||||
if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
String path = command.getRequest().getEndpoint().getPath();
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* 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.vcloud.handlers;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.http.BaseHttpErrorHandlerTest;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" })
|
||||
public class ParseVCloudErrorFromHttpResponseTest extends BaseHttpErrorHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testGet404SetsResourceNotFoundException() {
|
||||
assertCodeMakes("GET", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
404, "", "", ResourceNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete404SetsHttpResponseException() {
|
||||
assertCodeMakes("DELETE", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
404, "", "", HttpResponseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test401SetsAuthorizationException() {
|
||||
assertCodeMakes("GET", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
401, "", "", AuthorizationException.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends HttpErrorHandler> getHandlerClass() {
|
||||
return ParseVCloudErrorFromHttpResponse.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -81,7 +81,7 @@ import org.jclouds.vcloud.terremark.domain.TerremarkOrganization;
|
|||
import org.jclouds.vcloud.terremark.domain.VAppConfiguration;
|
||||
import org.jclouds.vcloud.terremark.endpoints.KeysList;
|
||||
import org.jclouds.vcloud.terremark.functions.ParseTaskFromLocationHeader;
|
||||
import org.jclouds.vcloud.terremark.functions.ReturnEmptySetOnUnauthorized;
|
||||
import org.jclouds.vcloud.terremark.functions.ReturnEmptySetOnResourceNotFoundException;
|
||||
import org.jclouds.vcloud.terremark.functions.ReturnVoidOnDeleteDefaultIp;
|
||||
import org.jclouds.vcloud.terremark.options.AddInternetServiceOptions;
|
||||
import org.jclouds.vcloud.terremark.options.AddNodeOptions;
|
||||
|
@ -104,8 +104,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
* Provides access to VCloud resources via their REST API.
|
||||
* <p/>
|
||||
*
|
||||
* @see <a href="https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60.aspx"
|
||||
* />
|
||||
* @see <a href="https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60.aspx" />
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@RequestFilters(SetVCloudTokenCookie.class)
|
||||
|
@ -126,7 +125,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(ORG_XML)
|
||||
@Override
|
||||
ListenableFuture<? extends TerremarkOrganization> getOrganization(
|
||||
@PathParam("orgId") String orgId);
|
||||
@PathParam("orgId") String orgId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getDefaultVDC
|
||||
|
@ -172,11 +171,10 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@XMLResponseParser(VAppHandler.class)
|
||||
@MapBinder(TerremarkBindInstantiateVAppTemplateParamsToXmlPayload.class)
|
||||
@Override
|
||||
ListenableFuture<? extends VApp> instantiateVAppTemplateInVDC(
|
||||
@PathParam("vDCId") String vDCId,
|
||||
@MapPayloadParam("name") @ParamValidators(DnsNameValidator.class) String appName,
|
||||
@MapPayloadParam("template") @ParamParser(CatalogIdToUri.class) String templateId,
|
||||
InstantiateVAppTemplateOptions... options);
|
||||
ListenableFuture<? extends VApp> instantiateVAppTemplateInVDC(@PathParam("vDCId") String vDCId,
|
||||
@MapPayloadParam("name") @ParamValidators(DnsNameValidator.class) String appName,
|
||||
@MapPayloadParam("template") @ParamParser(CatalogIdToUri.class) String templateId,
|
||||
InstantiateVAppTemplateOptions... options);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#addInternetService
|
||||
|
@ -189,11 +187,9 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@XMLResponseParser(InternetServiceHandler.class)
|
||||
@MapBinder(AddInternetServiceOptions.class)
|
||||
ListenableFuture<? extends InternetService> addInternetServiceToVDC(
|
||||
@PathParam("vDCId") String vDCId,
|
||||
@MapPayloadParam("name") String serviceName,
|
||||
@MapPayloadParam("protocol") Protocol protocol,
|
||||
@MapPayloadParam("port") int port,
|
||||
AddInternetServiceOptions... options);
|
||||
@PathParam("vDCId") String vDCId, @MapPayloadParam("name") String serviceName,
|
||||
@MapPayloadParam("protocol") Protocol protocol, @MapPayloadParam("port") int port,
|
||||
AddInternetServiceOptions... options);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getAllInternetServices
|
||||
|
@ -204,7 +200,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(INTERNETSERVICESLIST_XML)
|
||||
@XMLResponseParser(InternetServicesHandler.class)
|
||||
ListenableFuture<? extends SortedSet<InternetService>> getAllInternetServicesInVDC(
|
||||
@PathParam("vDCId") String vDCId);
|
||||
@PathParam("vDCId") String vDCId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#addInternetServiceToExistingIp
|
||||
|
@ -217,11 +213,9 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@XMLResponseParser(InternetServiceHandler.class)
|
||||
@MapBinder(AddInternetServiceOptions.class)
|
||||
ListenableFuture<? extends InternetService> addInternetServiceToExistingIp(
|
||||
@PathParam("ipId") int existingIpId,
|
||||
@MapPayloadParam("name") String serviceName,
|
||||
@MapPayloadParam("protocol") Protocol protocol,
|
||||
@MapPayloadParam("port") int port,
|
||||
AddInternetServiceOptions... options);
|
||||
@PathParam("ipId") int existingIpId, @MapPayloadParam("name") String serviceName,
|
||||
@MapPayloadParam("protocol") Protocol protocol, @MapPayloadParam("port") int port,
|
||||
AddInternetServiceOptions... options);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#deletePublicIp
|
||||
|
@ -241,7 +235,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(INTERNETSERVICESLIST_XML)
|
||||
@XMLResponseParser(InternetServicesHandler.class)
|
||||
ListenableFuture<? extends SortedSet<InternetService>> getInternetServicesOnPublicIp(
|
||||
@PathParam("ipId") int ipId);
|
||||
@PathParam("ipId") int ipId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getPublicIp
|
||||
|
@ -252,8 +246,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(PUBLICIP_XML)
|
||||
@XMLResponseParser(InternetServicesHandler.class)
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends SortedSet<InternetService>> getPublicIp(
|
||||
@PathParam("ipId") int ipId);
|
||||
ListenableFuture<? extends SortedSet<InternetService>> getPublicIp(@PathParam("ipId") int ipId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getPublicIpsAssociatedWithVDC
|
||||
|
@ -264,7 +257,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(PUBLICIPSLIST_XML)
|
||||
@XMLResponseParser(PublicIpAddressesHandler.class)
|
||||
ListenableFuture<? extends SortedSet<PublicIpAddress>> getPublicIpsAssociatedWithVDC(
|
||||
@PathParam("vDCId") String vDCId);
|
||||
@PathParam("vDCId") String vDCId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#deleteInternetService
|
||||
|
@ -274,7 +267,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Path("/extensions/internetService/{internetServiceId}")
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
ListenableFuture<Void> deleteInternetService(
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getInternetService
|
||||
|
@ -286,7 +279,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@XMLResponseParser(InternetServiceHandler.class)
|
||||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
ListenableFuture<? extends InternetService> getInternetService(
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#addNode
|
||||
|
@ -298,11 +291,9 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(NODESERVICE_XML)
|
||||
@XMLResponseParser(NodeHandler.class)
|
||||
@MapBinder(AddNodeOptions.class)
|
||||
ListenableFuture<? extends Node> addNode(
|
||||
@PathParam("internetServiceId") int internetServiceId,
|
||||
@MapPayloadParam("ipAddress") String ipAddress,
|
||||
@MapPayloadParam("name") String name,
|
||||
@MapPayloadParam("port") int port, AddNodeOptions... options);
|
||||
ListenableFuture<? extends Node> addNode(@PathParam("internetServiceId") int internetServiceId,
|
||||
@MapPayloadParam("ipAddress") String ipAddress, @MapPayloadParam("name") String name,
|
||||
@MapPayloadParam("port") int port, AddNodeOptions... options);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getNodes
|
||||
|
@ -311,10 +302,10 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Endpoint(org.jclouds.vcloud.endpoints.VCloudApi.class)
|
||||
@Path("/extensions/internetService/{internetServiceId}/nodeServices")
|
||||
@XMLResponseParser(NodesHandler.class)
|
||||
@ExceptionParser(ReturnEmptySetOnUnauthorized.class)
|
||||
@ExceptionParser(ReturnEmptySetOnResourceNotFoundException.class)
|
||||
@Consumes(NODESERVICE_XML)
|
||||
ListenableFuture<? extends SortedSet<Node>> getNodes(
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
@PathParam("internetServiceId") int internetServiceId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getNode
|
||||
|
@ -337,8 +328,8 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(NODESERVICE_XML)
|
||||
@XMLResponseParser(NodeHandler.class)
|
||||
ListenableFuture<? extends Node> configureNode(
|
||||
@PathParam("nodeId") int nodeId,
|
||||
@BinderParam(BindNodeConfigurationToXmlPayload.class) NodeConfiguration nodeConfiguration);
|
||||
@PathParam("nodeId") int nodeId,
|
||||
@BinderParam(BindNodeConfigurationToXmlPayload.class) NodeConfiguration nodeConfiguration);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#deleteNode
|
||||
|
@ -360,8 +351,8 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@MapBinder(BindVAppConfigurationToXmlPayload.class)
|
||||
@ResponseParser(ParseTaskFromLocationHeader.class)
|
||||
ListenableFuture<? extends Task> configureVApp(
|
||||
@PathParam("vAppId") @ParamParser(VAppId.class) VApp vApp,
|
||||
VAppConfiguration configuration);
|
||||
@PathParam("vAppId") @ParamParser(VAppId.class) VApp vApp,
|
||||
VAppConfiguration configuration);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getCustomizationOptionsOfCatalogItem
|
||||
|
@ -372,7 +363,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@XMLResponseParser(CustomizationParametersHandler.class)
|
||||
@Consumes(CATALOGITEMCUSTOMIZATIONPARAMETERS_XML)
|
||||
ListenableFuture<? extends CustomizationParameters> getCustomizationOptionsOfCatalogItem(
|
||||
@PathParam("catalogItemId") String catalogItemId);
|
||||
@PathParam("catalogItemId") String catalogItemId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#listKeyPairs
|
||||
|
@ -391,8 +382,7 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Path("/extensions/org/{orgId}/keys")
|
||||
@Consumes(KEYSLIST_XML)
|
||||
@XMLResponseParser(KeyPairsHandler.class)
|
||||
ListenableFuture<? extends Set<KeyPair>> listKeyPairsInOrg(
|
||||
@PathParam("orgId") String orgId);
|
||||
ListenableFuture<? extends Set<KeyPair>> listKeyPairsInOrg(@PathParam("orgId") String orgId);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#generateKeyPairInOrg
|
||||
|
@ -404,10 +394,8 @@ public interface TerremarkVCloudAsyncClient extends VCloudAsyncClient {
|
|||
@Consumes(KEYSLIST_XML)
|
||||
@XMLResponseParser(KeyPairHandler.class)
|
||||
@MapBinder(BindCreateKeyToXmlPayload.class)
|
||||
ListenableFuture<? extends KeyPair> generateKeyPairInOrg(
|
||||
@PathParam("orgId") String orgId,
|
||||
@MapPayloadParam("name") String name,
|
||||
@MapPayloadParam("isDefault") boolean makeDefault);
|
||||
ListenableFuture<? extends KeyPair> generateKeyPairInOrg(@PathParam("orgId") String orgId,
|
||||
@MapPayloadParam("name") String name, @MapPayloadParam("isDefault") boolean makeDefault);
|
||||
|
||||
/**
|
||||
* @see TerremarkVCloudClient#getKeyPair
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.jclouds.vcloud.terremark;
|
|||
|
||||
import static org.jclouds.Constants.PROPERTY_API_VERSION;
|
||||
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
|
||||
import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
|
@ -38,6 +39,8 @@ public class TerremarkVCloudPropertiesBuilder extends VCloudPropertiesBuilder {
|
|||
properties.setProperty(PROPERTY_ENDPOINT, "https://services.vcloudexpress.terremark.com/api");
|
||||
properties.setProperty("jclouds.dns_name_length_min", "1");
|
||||
properties.setProperty("jclouds.dns_name_length_max", "15");
|
||||
// with ssh key injection comes another reboot. allowing more time
|
||||
properties.setProperty(PROPERTY_VCLOUD_TIMEOUT_TASK_COMPLETED, 360l * 1000l + "");
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
*/
|
||||
package org.jclouds.vcloud.terremark.compute;
|
||||
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static org.jclouds.vcloud.terremark.options.AddInternetServiceOptions.Builder.withDescription;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
|
@ -35,6 +38,8 @@ import org.jclouds.compute.strategy.PopulateDefaultLoginCredentialsForImageStrat
|
|||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.vcloud.compute.BaseVCloudComputeClient;
|
||||
import org.jclouds.vcloud.domain.Task;
|
||||
import org.jclouds.vcloud.domain.TaskStatus;
|
||||
import org.jclouds.vcloud.domain.TasksList;
|
||||
import org.jclouds.vcloud.domain.VApp;
|
||||
import org.jclouds.vcloud.domain.VAppStatus;
|
||||
import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions;
|
||||
|
@ -45,8 +50,8 @@ import org.jclouds.vcloud.terremark.domain.Protocol;
|
|||
import org.jclouds.vcloud.terremark.domain.PublicIpAddress;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.internal.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
@ -60,10 +65,9 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
|
||||
@Inject
|
||||
protected TerremarkVCloudComputeClient(TerremarkVCloudClient client,
|
||||
PopulateDefaultLoginCredentialsForImageStrategy credentialsProvider,
|
||||
@Named("PASSWORD") Provider<String> passwordGenerator,
|
||||
Predicate<String> successTester,
|
||||
Map<VAppStatus, NodeState> vAppStatusToNodeState) {
|
||||
PopulateDefaultLoginCredentialsForImageStrategy credentialsProvider,
|
||||
@Named("PASSWORD") Provider<String> passwordGenerator, Predicate<String> successTester,
|
||||
Map<VAppStatus, NodeState> vAppStatusToNodeState) {
|
||||
super(client, successTester, vAppStatusToNodeState);
|
||||
this.client = client;
|
||||
this.credentialsProvider = credentialsProvider;
|
||||
|
@ -71,37 +75,30 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> parseAndValidateResponse(String templateId,
|
||||
VApp vAppResponse) {
|
||||
Credentials credentials = credentialsProvider.execute(client
|
||||
.getVAppTemplate(templateId));
|
||||
Map<String, String> toReturn = super.parseResponse(templateId,
|
||||
vAppResponse);
|
||||
protected Map<String, String> parseAndValidateResponse(String templateId, VApp vAppResponse) {
|
||||
Credentials credentials = credentialsProvider.execute(client.getVAppTemplate(templateId));
|
||||
Map<String, String> toReturn = super.parseResponse(templateId, vAppResponse);
|
||||
toReturn.put("username", credentials.identity);
|
||||
toReturn.put("password", credentials.credential);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> start(String vDCId, String name,
|
||||
String templateId, InstantiateVAppTemplateOptions options,
|
||||
int... portsToOpen) {
|
||||
public Map<String, String> start(String vDCId, String name, String templateId,
|
||||
InstantiateVAppTemplateOptions options, int... portsToOpen) {
|
||||
if (options.getDiskSizeKilobytes() != null) {
|
||||
logger
|
||||
.warn("trmk does not support resizing the primary disk; unsetting disk size");
|
||||
logger.warn("trmk does not support resizing the primary disk; unsetting disk size");
|
||||
}
|
||||
// we only get IP addresses after "deploy"
|
||||
if (portsToOpen.length > 0 && !options.shouldBlockOnDeploy())
|
||||
throw new IllegalArgumentException(
|
||||
"We cannot open ports on terremark unless we can deploy the vapp");
|
||||
"We cannot open ports on terremark unless we can deploy the vapp");
|
||||
String password = null;
|
||||
if (client.getVAppTemplate(templateId).getDescription()
|
||||
.indexOf("Windows") != -1) {
|
||||
if (client.getVAppTemplate(templateId).getDescription().indexOf("Windows") != -1) {
|
||||
password = passwordGenerator.get();
|
||||
options.getProperties().put("password", password);
|
||||
}
|
||||
Map<String, String> response = super.start(vDCId, name, templateId,
|
||||
options, portsToOpen);
|
||||
Map<String, String> response = super.start(vDCId, name, templateId, options, portsToOpen);
|
||||
if (password != null) {
|
||||
response = new LinkedHashMap<String, String>(response);
|
||||
response.put("password", password);
|
||||
|
@ -114,77 +111,68 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
public String createPublicAddressMappedToPorts(String vAppId, int... ports) {
|
||||
VApp vApp = client.getVApp(vAppId);
|
||||
PublicIpAddress ip = null;
|
||||
String privateAddress = Iterables.getLast(vApp.getNetworkToAddresses()
|
||||
.values());
|
||||
String privateAddress = getLast(vApp.getNetworkToAddresses().values());
|
||||
for (int port : ports) {
|
||||
InternetService is = null;
|
||||
Protocol protocol;
|
||||
switch (port) {
|
||||
case 22:
|
||||
protocol = Protocol.TCP;
|
||||
break;
|
||||
case 80:
|
||||
case 8080:
|
||||
protocol = Protocol.HTTP;
|
||||
break;
|
||||
case 443:
|
||||
protocol = Protocol.HTTPS;
|
||||
break;
|
||||
default:
|
||||
protocol = Protocol.HTTP;
|
||||
break;
|
||||
case 22:
|
||||
protocol = Protocol.TCP;
|
||||
break;
|
||||
case 80:
|
||||
case 8080:
|
||||
protocol = Protocol.HTTP;
|
||||
break;
|
||||
case 443:
|
||||
protocol = Protocol.HTTPS;
|
||||
break;
|
||||
default:
|
||||
protocol = Protocol.HTTP;
|
||||
break;
|
||||
}
|
||||
if (ip == null) {
|
||||
logger.debug(">> creating InternetService in vDC %s:%s:%d", vApp
|
||||
.getVDC().getId(), protocol, port);
|
||||
is = client.addInternetServiceToVDC(vApp.getVDC().getId(), vApp
|
||||
.getName()
|
||||
+ "-" + port, protocol, port, withDescription(String.format(
|
||||
"port %d access to serverId: %s name: %s", port,
|
||||
vApp.getId(), vApp.getName())));
|
||||
logger.debug(">> creating InternetService in vDC %s:%s:%d", vApp.getVDC().getId(),
|
||||
protocol, port);
|
||||
is = client.addInternetServiceToVDC(vApp.getVDC().getId(), vApp.getName() + "-" + port,
|
||||
protocol, port, withDescription(String.format(
|
||||
"port %d access to serverId: %s name: %s", port, vApp.getId(), vApp
|
||||
.getName())));
|
||||
ip = is.getPublicIpAddress();
|
||||
} else {
|
||||
logger.debug(">> adding InternetService %s:%s:%d", ip.getAddress(),
|
||||
protocol, port);
|
||||
is = client.addInternetServiceToExistingIp(ip.getId(), vApp
|
||||
.getName()
|
||||
+ "-" + port, protocol, port, withDescription(String.format(
|
||||
"port %d access to serverId: %s name: %s", port,
|
||||
vApp.getId(), vApp.getName())));
|
||||
logger.debug(">> adding InternetService %s:%s:%d", ip.getAddress(), protocol, port);
|
||||
is = client.addInternetServiceToExistingIp(ip.getId(), vApp.getName() + "-" + port,
|
||||
protocol, port, withDescription(String.format(
|
||||
"port %d access to serverId: %s name: %s", port, vApp.getId(), vApp
|
||||
.getName())));
|
||||
}
|
||||
logger.debug("<< created InternetService(%s) %s:%s:%d", is.getId(), is
|
||||
.getPublicIpAddress().getAddress(), is.getProtocol(), is
|
||||
.getPort());
|
||||
logger.debug(">> adding Node %s:%d -> %s:%d", is.getPublicIpAddress()
|
||||
.getAddress(), is.getPort(), privateAddress, port);
|
||||
Node node = client.addNode(is.getId(), privateAddress, vApp.getName()
|
||||
+ "-" + port, port);
|
||||
.getPublicIpAddress().getAddress(), is.getProtocol(), is.getPort());
|
||||
logger.debug(">> adding Node %s:%d -> %s:%d", is.getPublicIpAddress().getAddress(), is
|
||||
.getPort(), privateAddress, port);
|
||||
Node node = client.addNode(is.getId(), privateAddress, vApp.getName() + "-" + port, port);
|
||||
logger.debug("<< added Node(%s)", node.getId());
|
||||
}
|
||||
return ip != null ? ip.getAddress() : null;
|
||||
}
|
||||
|
||||
private Set<PublicIpAddress> deleteInternetServicesAndNodesAssociatedWithVApp(
|
||||
VApp vApp) {
|
||||
private Set<PublicIpAddress> deleteInternetServicesAndNodesAssociatedWithVApp(VApp vApp) {
|
||||
Set<PublicIpAddress> ipAddresses = Sets.newHashSet();
|
||||
SERVICE: for (InternetService service : client
|
||||
.getAllInternetServicesInVDC(vApp.getVDC().getId())) {
|
||||
SERVICE: for (InternetService service : client.getAllInternetServicesInVDC(vApp.getVDC()
|
||||
.getId())) {
|
||||
for (Node node : client.getNodes(service.getId())) {
|
||||
if (vApp.getNetworkToAddresses().containsValue(node.getIpAddress())) {
|
||||
ipAddresses.add(service.getPublicIpAddress());
|
||||
logger.debug(">> deleting Node(%s) %s:%d -> %s:%d",
|
||||
node.getId(), service.getPublicIpAddress().getAddress(),
|
||||
service.getPort(), node.getIpAddress(), node.getPort());
|
||||
logger.debug(">> deleting Node(%s) %s:%d -> %s:%d", node.getId(), service
|
||||
.getPublicIpAddress().getAddress(), service.getPort(), node.getIpAddress(),
|
||||
node.getPort());
|
||||
client.deleteNode(node.getId());
|
||||
logger.debug("<< deleted Node(%s)", node.getId());
|
||||
SortedSet<Node> nodes = client.getNodes(service.getId());
|
||||
if (nodes.size() == 0) {
|
||||
logger.debug(">> deleting InternetService(%s) %s:%d", service
|
||||
.getId(), service.getPublicIpAddress().getAddress(),
|
||||
service.getPort());
|
||||
logger.debug(">> deleting InternetService(%s) %s:%d", service.getId(), service
|
||||
.getPublicIpAddress().getAddress(), service.getPort());
|
||||
client.deleteInternetService(service.getId());
|
||||
logger.debug("<< deleted InternetService(%s)", service
|
||||
.getId());
|
||||
logger.debug("<< deleted InternetService(%s)", service.getId());
|
||||
continue SERVICE;
|
||||
}
|
||||
}
|
||||
|
@ -193,14 +181,13 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
return ipAddresses;
|
||||
}
|
||||
|
||||
private void deletePublicIpAddressesWithNoServicesAttached(
|
||||
Set<PublicIpAddress> ipAddresses) {
|
||||
private void deletePublicIpAddressesWithNoServicesAttached(Set<PublicIpAddress> ipAddresses) {
|
||||
IPADDRESS: for (PublicIpAddress address : ipAddresses) {
|
||||
SortedSet<InternetService> services = client
|
||||
.getInternetServicesOnPublicIp(address.getId());
|
||||
.getInternetServicesOnPublicIp(address.getId());
|
||||
if (services.size() == 0) {
|
||||
logger.debug(">> deleting PublicIpAddress(%s) %s", address.getId(),
|
||||
address.getAddress());
|
||||
logger.debug(">> deleting PublicIpAddress(%s) %s", address.getId(), address
|
||||
.getAddress());
|
||||
client.deletePublicIp(address.getId());
|
||||
logger.debug("<< deleted PublicIpAddress(%s)", address.getId());
|
||||
continue IPADDRESS;
|
||||
|
@ -209,10 +196,9 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* deletes the internet service and nodes associated with the vapp. Deletes
|
||||
* the IP address, if there are no others using it. Finally, it powers off
|
||||
* and deletes the vapp. Note that we do not call undeploy, as terremark does
|
||||
* not support the command.
|
||||
* deletes the internet service and nodes associated with the vapp. Deletes the IP address, if
|
||||
* there are no others using it. Finally, it powers off and deletes the vapp. Note that we do not
|
||||
* call undeploy, as terremark does not support the command.
|
||||
*/
|
||||
@Override
|
||||
public void stop(String id) {
|
||||
|
@ -220,11 +206,12 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
Set<PublicIpAddress> ipAddresses = deleteInternetServicesAndNodesAssociatedWithVApp(vApp);
|
||||
deletePublicIpAddressesWithNoServicesAttached(ipAddresses);
|
||||
if (vApp.getStatus() != VAppStatus.OFF) {
|
||||
logger.debug(">> powering off vApp(%s), current status: %s", vApp
|
||||
.getId(), vApp.getStatus());
|
||||
Task task = client.powerOffVApp(vApp.getId());
|
||||
if (!taskTester.apply(task.getId())) {
|
||||
throw new TaskException("powerOff", vApp, task);
|
||||
try {
|
||||
powerOffAndWait(vApp);
|
||||
} catch (IllegalStateException e) {
|
||||
logger.warn("<< %s vApp(%s)", e.getMessage(), vApp.getId());
|
||||
blockOnLastTask(vApp);
|
||||
powerOffAndWait(vApp);
|
||||
}
|
||||
vApp = client.getVApp(id);
|
||||
logger.debug("<< %s vApp(%s)", vApp.getStatus(), vApp.getId());
|
||||
|
@ -234,32 +221,62 @@ public class TerremarkVCloudComputeClient extends BaseVCloudComputeClient {
|
|||
logger.debug("<< deleted vApp(%s))", vApp.getId());
|
||||
}
|
||||
|
||||
private void powerOffAndWait(VApp vApp) {
|
||||
logger.debug(">> powering off vApp(%s), current status: %s", vApp.getId(), vApp.getStatus());
|
||||
Task task = client.powerOffVApp(vApp.getId());
|
||||
if (!taskTester.apply(task.getId()))
|
||||
throw new TaskException("powerOff", vApp, task);
|
||||
}
|
||||
|
||||
void blockOnLastTask(VApp vApp) {
|
||||
TasksList list = client.getDefaultTasksList();
|
||||
try {
|
||||
Task lastTask = getLast(filter(list.getTasks(), new Predicate<Task>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(Task input) {
|
||||
return input.getStatus() == TaskStatus.QUEUED
|
||||
|| input.getStatus() == TaskStatus.RUNNING;
|
||||
}
|
||||
|
||||
}));
|
||||
if (!taskTester.apply(lastTask.getId()))
|
||||
throw new TaskException("powerOff", vApp, lastTask);
|
||||
} catch (NoSuchElementException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NullPointerException
|
||||
* if the node is not found
|
||||
* @returns empty set if the node is not found
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getPrivateAddresses(String id) {
|
||||
VApp vApp = client.getVApp(id);
|
||||
return Sets.newHashSet(vApp.getNetworkToAddresses().values());
|
||||
if (vApp != null)
|
||||
return Sets.newHashSet(vApp.getNetworkToAddresses().values());
|
||||
else
|
||||
return ImmutableSet.<String> of();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NullPointerException
|
||||
* if the node is not found
|
||||
* @returns empty set if the node is not found
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getPublicAddresses(String id) {
|
||||
VApp vApp = client.getVApp(id);
|
||||
Set<String> ipAddresses = Sets.newHashSet();
|
||||
for (InternetService service : client.getAllInternetServicesInVDC(vApp
|
||||
.getVDC().getId())) {
|
||||
for (Node node : client.getNodes(service.getId())) {
|
||||
if (vApp.getNetworkToAddresses().containsValue(node.getIpAddress())) {
|
||||
ipAddresses.add(service.getPublicIpAddress().getAddress());
|
||||
if (vApp != null) {
|
||||
Set<String> ipAddresses = Sets.newHashSet();
|
||||
for (InternetService service : client.getAllInternetServicesInVDC(vApp.getVDC().getId())) {
|
||||
for (Node node : client.getNodes(service.getId())) {
|
||||
if (vApp.getNetworkToAddresses().containsValue(node.getIpAddress())) {
|
||||
ipAddresses.add(service.getPublicIpAddress().getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ipAddresses;
|
||||
} else {
|
||||
return ImmutableSet.<String> of();
|
||||
}
|
||||
return ipAddresses;
|
||||
}
|
||||
}
|
|
@ -57,20 +57,15 @@ public class TerremarkVCloudGetNodeMetadataStrategy extends VCloudGetNodeMetadat
|
|||
|
||||
@Override
|
||||
public NodeMetadata execute(String id) {
|
||||
try {
|
||||
NodeMetadata node = checkNotNull(getNodeMetadata.execute(checkNotNull(id, "node.id")),
|
||||
"node: " + id);
|
||||
if (node.getTag() != null) {
|
||||
node = installCredentialsFromCache(node);
|
||||
}
|
||||
if (node.getCredentials() == null)
|
||||
node = installDefaultCredentialsFromImage(node);
|
||||
return node;
|
||||
} catch (NullPointerException e) {
|
||||
if (logger.isTraceEnabled())
|
||||
logger.warn(e, "node %s not found during execution", id);
|
||||
NodeMetadata node = getNodeMetadata.execute(checkNotNull(id, "node.id"));
|
||||
if (node == null)
|
||||
return null;
|
||||
if (node.getTag() != null) {
|
||||
node = installCredentialsFromCache(node);
|
||||
}
|
||||
if (node.getCredentials() == null)
|
||||
node = installDefaultCredentialsFromImage(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
NodeMetadata installCredentialsFromCache(NodeMetadata node) {
|
||||
|
|
|
@ -24,7 +24,11 @@ import java.net.URI;
|
|||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.RequiresHttp;
|
||||
import org.jclouds.http.annotation.ClientError;
|
||||
import org.jclouds.http.annotation.Redirection;
|
||||
import org.jclouds.http.annotation.ServerError;
|
||||
import org.jclouds.rest.ConfiguresRestClient;
|
||||
import org.jclouds.util.Utils;
|
||||
import org.jclouds.vcloud.VCloudAsyncClient;
|
||||
|
@ -35,6 +39,7 @@ import org.jclouds.vcloud.terremark.TerremarkVCloudAsyncClient;
|
|||
import org.jclouds.vcloud.terremark.TerremarkVCloudClient;
|
||||
import org.jclouds.vcloud.terremark.domain.TerremarkOrganization;
|
||||
import org.jclouds.vcloud.terremark.endpoints.KeysList;
|
||||
import org.jclouds.vcloud.terremark.handlers.ParseTerremarkVCloudErrorFromHttpResponse;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
|
||||
|
@ -97,4 +102,15 @@ public class TerremarkVCloudRestClientModule
|
|||
return Utils.toStringAndClose(getClass().getResourceAsStream(
|
||||
"/terremark/CreateKey.xml"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindErrorHandlers() {
|
||||
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
|
||||
ParseTerremarkVCloudErrorFromHttpResponse.class);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(
|
||||
ParseTerremarkVCloudErrorFromHttpResponse.class);
|
||||
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(
|
||||
ParseTerremarkVCloudErrorFromHttpResponse.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,24 +24,23 @@ import java.util.SortedSet;
|
|||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.jclouds.vcloud.terremark.domain.Node;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
|
||||
/**
|
||||
* There's a bug where calling get after delete throws an unauthorized exception.
|
||||
* <p/>
|
||||
* https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60/p/264/876.aspx#876
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Singleton
|
||||
public class ReturnEmptySetOnUnauthorized implements Function<Exception, SortedSet<Node>> {
|
||||
public class ReturnEmptySetOnResourceNotFoundException implements
|
||||
Function<Exception, SortedSet<Node>> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public SortedSet<Node> apply(Exception from) {
|
||||
if (from instanceof AuthorizationException) {
|
||||
if (from instanceof ResourceNotFoundException) {
|
||||
return ImmutableSortedSet.<Node> of();
|
||||
}
|
||||
return SortedSet.class.cast(propagateOrNull(from));
|
|
@ -0,0 +1,79 @@
|
|||
package org.jclouds.vcloud.terremark.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.http.HttpCommand;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
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.io.Closeables;
|
||||
|
||||
/**
|
||||
* This will parse and set an appropriate exception on the command object.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
@Singleton
|
||||
public class ParseTerremarkVCloudErrorFromHttpResponse implements HttpErrorHandler {
|
||||
@Resource
|
||||
protected Logger logger = Logger.NULL;
|
||||
public static final Pattern RESOURCE_PATTERN = Pattern.compile(".*/v[^/]+/([^/]+)/([0-9]+)");
|
||||
|
||||
public void handleError(HttpCommand command, HttpResponse response) {
|
||||
Exception exception = new HttpResponseException(command, response);
|
||||
|
||||
try {
|
||||
String content = parseErrorFromContentOrNull(command, response);
|
||||
switch (response.getStatusCode()) {
|
||||
case 401:
|
||||
exception = new AuthorizationException(command.getRequest(), content);
|
||||
break;
|
||||
case 403: // TODO temporary as terremark mistakenly uses this for vApp not found.
|
||||
case 404:
|
||||
if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
String path = command.getRequest().getEndpoint().getPath();
|
||||
Matcher matcher = RESOURCE_PATTERN.matcher(path);
|
||||
String message;
|
||||
if (matcher.find()) {
|
||||
message = String.format("%s %s not found", matcher.group(1), matcher.group(2));
|
||||
} else {
|
||||
message = path;
|
||||
}
|
||||
exception = new ResourceNotFoundException(message);
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
if (response.getMessage().indexOf("because there is a pending task running") != -1)
|
||||
exception = new IllegalStateException(response.getMessage(), exception);
|
||||
break;
|
||||
default:
|
||||
exception = new HttpResponseException(command, response, content);
|
||||
}
|
||||
} finally {
|
||||
Closeables.closeQuietly(response.getContent());
|
||||
command.setException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
String parseErrorFromContentOrNull(HttpCommand command, HttpResponse response) {
|
||||
if (response.getContent() != null) {
|
||||
try {
|
||||
return Utils.toStringAndClose(response.getContent());
|
||||
} catch (IOException e) {
|
||||
logger.warn(e, "exception reading error from response", response);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions;
|
|||
import org.jclouds.vcloud.terremark.config.TerremarkVCloudRestClientModule;
|
||||
import org.jclouds.vcloud.terremark.domain.NodeConfiguration;
|
||||
import org.jclouds.vcloud.terremark.domain.Protocol;
|
||||
import org.jclouds.vcloud.terremark.functions.ReturnEmptySetOnUnauthorized;
|
||||
import org.jclouds.vcloud.terremark.functions.ReturnEmptySetOnResourceNotFoundException;
|
||||
import org.jclouds.vcloud.terremark.options.AddInternetServiceOptions;
|
||||
import org.jclouds.vcloud.terremark.options.AddNodeOptions;
|
||||
import org.jclouds.vcloud.terremark.options.TerremarkInstantiateVAppTemplateOptions;
|
||||
|
@ -404,7 +404,7 @@ public class TerremarkVCloudAsyncClientTest extends RestClientTest<TerremarkVClo
|
|||
|
||||
assertResponseParserClassEquals(method, request, ParseSax.class);
|
||||
assertSaxResponseParserClassEquals(method, NodesHandler.class);
|
||||
assertExceptionParserClassEquals(method, ReturnEmptySetOnUnauthorized.class);
|
||||
assertExceptionParserClassEquals(method, ReturnEmptySetOnResourceNotFoundException.class);
|
||||
|
||||
checkFilters(request);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
package org.jclouds.vcloud.terremark.handlers;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jclouds.http.BaseHttpErrorHandlerTest;
|
||||
import org.jclouds.http.HttpErrorHandler;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = { "unit" })
|
||||
public class ParseTerremarkVCloudErrorFromHttpResponseTest extends BaseHttpErrorHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testGet403SetsResourceNotFoundException() {
|
||||
assertCodeMakes(
|
||||
"GET",
|
||||
URI.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
403,
|
||||
"HTTP/1.1 403 Internet Service does not exist in the system. Internet Service was probably deleted by another user. Please refresh and retry the operation",
|
||||
"", ResourceNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet404SetsResourceNotFoundException() {
|
||||
assertCodeMakes("GET", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
404, "", "", ResourceNotFoundException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete404SetsHttpResponseException() {
|
||||
assertCodeMakes("DELETE", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
404, "", "", HttpResponseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test401SetsAuthorizationException() {
|
||||
assertCodeMakes("GET", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
401, "", "", AuthorizationException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testbecause_there_is_a_pending_task_runningSetsIllegalStateException() {
|
||||
assertCodeMakes("GET", URI
|
||||
.create("https://services.vcloudexpress.terremark.com/api/v0.8a-ext1.6/vdc/32"),
|
||||
500, "because there is a pending task running",
|
||||
"because there is a pending task running", IllegalStateException.class);
|
||||
}
|
||||
|
||||
// case 401:
|
||||
// exception = new AuthorizationException(command.getRequest(), content);
|
||||
// break;
|
||||
// case 403: // TODO temporary as terremark mistakenly uses this for vApp not found.
|
||||
// case 404:
|
||||
// if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
// String path = command.getRequest().getEndpoint().getPath();
|
||||
// Matcher matcher = RESOURCE_PATTERN.matcher(path);
|
||||
// String message;
|
||||
// if (matcher.find()) {
|
||||
// message = String.format("%s %s not found", matcher.group(1), matcher.group(2));
|
||||
// } else {
|
||||
// message = path;
|
||||
// }
|
||||
// exception = new ResourceNotFoundException(message);
|
||||
// }
|
||||
// break;
|
||||
// case 401:
|
||||
// exception = new AuthorizationException(command.getRequest(), content);
|
||||
// break;
|
||||
// case 403: // TODO temporary as terremark mistakenly uses this for vApp not found.
|
||||
// case 404:
|
||||
// if (!command.getRequest().getMethod().equals("DELETE")) {
|
||||
// String path = command.getRequest().getEndpoint().getPath();
|
||||
// Matcher matcher = RESOURCE_PATTERN.matcher(path);
|
||||
// String message;
|
||||
// if (matcher.find()) {
|
||||
// message = String.format("%s %s not found", matcher.group(1), matcher.group(2));
|
||||
// } else {
|
||||
// message = path;
|
||||
// }
|
||||
// exception = new ResourceNotFoundException(message);
|
||||
// }
|
||||
// break;
|
||||
@Override
|
||||
protected Class<? extends HttpErrorHandler> getHandlerClass() {
|
||||
return ParseTerremarkVCloudErrorFromHttpResponse.class;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue