You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2018/05/04 09:05:06 UTC
[1/2] jclouds git commit: Cleanup unused variables
Repository: jclouds
Updated Branches:
refs/heads/2.1.x a32690a2e -> b144d9f47
Cleanup unused variables
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/c96028a2
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/c96028a2
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/c96028a2
Branch: refs/heads/2.1.x
Commit: c96028a2ae717c7c5d10786bb3c8650a2cc9cbf8
Parents: a32690a
Author: Ignasi Barrera <na...@apache.org>
Authored: Fri May 4 09:27:39 2018 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri May 4 11:04:51 2018 +0200
----------------------------------------------------------------------
.../compute/config/AzurePredicatesModule.java | 6 -----
.../arm/features/VaultApiLiveTest.java | 23 +++++---------------
.../arm/features/VaultApiMockTest.java | 3 ---
3 files changed, 6 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/c96028a2/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzurePredicatesModule.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzurePredicatesModule.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzurePredicatesModule.java
index c8dfd6e..9885075 100644
--- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzurePredicatesModule.java
+++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/compute/config/AzurePredicatesModule.java
@@ -330,7 +330,6 @@ public class AzurePredicatesModule extends AbstractModule {
@Override
public boolean apply(final String name) {
checkNotNull(name, "name cannot be null");
- boolean present = false;
List<Vault.DeletedVault> vaults = api.getVaultApi(resourceGroup).listDeletedVaults();
return shouldBePresent == Iterables.any(vaults, new Predicate<Vault.DeletedVault>() {
@Override public boolean apply(Vault.DeletedVault input) {
@@ -380,7 +379,6 @@ public class AzurePredicatesModule extends AbstractModule {
@Override
public boolean apply(final String name) {
checkNotNull(name, "name cannot be null");
- boolean present = false;
DeletedKeyBundle key = api.getVaultApi(resourceGroup).getDeletedKey(vaultUri, name);
return shouldBePresent == (key != null);
}
@@ -408,7 +406,6 @@ public class AzurePredicatesModule extends AbstractModule {
@Override
public boolean apply(final String name) {
checkNotNull(name, "name cannot be null");
- boolean result = false;
KeyBundle key = api.getVaultApi(resourceGroup).getKey(vaultUri, name);
return key != null ? (isRecovered ? true : key.attributes().recoveryLevel().contains("Recoverable")) : false;
}
@@ -453,7 +450,6 @@ public class AzurePredicatesModule extends AbstractModule {
return retry(new Predicate<String>() {
@Override
public boolean apply(final String name) {
- boolean present = false;
checkNotNull(name, "name cannot be null");
DeletedSecretBundle secret = api.getVaultApi(resourceGroup).getDeletedSecret(vaultUri, name);
return shouldBePresent == (secret != null);
@@ -534,7 +530,6 @@ public class AzurePredicatesModule extends AbstractModule {
return retry(new Predicate<String>() {
@Override
public boolean apply(final String name) {
- boolean present = false;
checkNotNull(name, "name cannot be null");
DeletedCertificateBundle cert = api.getVaultApi(resourceGroup).getDeletedCertificate(vaultUri, name);
return shouldBePresent == (cert != null);
@@ -591,7 +586,6 @@ public class AzurePredicatesModule extends AbstractModule {
@Override
public boolean apply(final String name) {
checkNotNull(name, "name cannot be null");
- boolean result = false;
CertificateOperation certOp = api.getVaultApi(resourceGroup).getCertificateOperation(vaultUri, name);
return isCreate ? ((certOp != null) ? !certOp.status().equals("inProgress") : false) : (certOp == null);
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/c96028a2/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiLiveTest.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiLiveTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiLiveTest.java
index 026aa28..2c6b100 100644
--- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiLiveTest.java
+++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiLiveTest.java
@@ -318,17 +318,6 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = "testUpdateKey")
public void testListKeyVersions() {
- // Create a second version of the key
- KeyAttributes keyAttr = KeyAttributes.create(true, null, null, null, null, null);
- KeyBundle keyBundle = api().createKey(vaultUri,
- KEY_NAME,
- keyAttr,
- null,
- null,
- 3072,
- "RSA",
- null);
-
// List key versions
List<Key> keys = api().getKeyVersions(vaultUri, KEY_NAME);
assertNotNull(keys);
@@ -427,7 +416,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = {"testDeleteRecoverableKey", "testGetDeletedKey"})
public void testRecoverDeletedKey() {
- KeyBundle key = api().recoverDeletedKey(vaultUri, RECOVERABLE_KEY_NAME);
+ api().recoverDeletedKey(vaultUri, RECOVERABLE_KEY_NAME);
checkState(recoverableKeyStatus.create(resourceGroupName, vaultUri, true).apply(RECOVERABLE_KEY_NAME),
"key was not recovered before timeout");
}
@@ -435,7 +424,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = "testRecoverDeletedKey")
public void testPurgeDeletedKey() {
// delete the key
- DeletedKeyBundle dkb = api().deleteKey(vaultUri, RECOVERABLE_KEY_NAME);
+ api().deleteKey(vaultUri, RECOVERABLE_KEY_NAME);
checkState(deletedKeyStatus.create(resourceGroupName, vaultUri, true).apply(RECOVERABLE_KEY_NAME),
"key was not deleted before timeout");
@@ -559,7 +548,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
public void testListSecretVersions() {
// Create a second version of the secret
SecretAttributes attributes = SecretAttributes.create(true, null, null, null, null, null);
- SecretBundle secretBundle = api().setSecret(vaultUri,
+ api().setSecret(vaultUri,
SECRET_NAME,
attributes,
"aNewSecretKey",
@@ -684,7 +673,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = {"testDeleteRecoverableSecret", "testGetDeletedSecret"})
public void testRecoverDeletedSecret() {
- SecretBundle secret = api().recoverDeletedSecret(vaultUri, RECOVERABLE_SECRET_NAME);
+ api().recoverDeletedSecret(vaultUri, RECOVERABLE_SECRET_NAME);
checkState(recoverableSecretStatus.create(resourceGroupName, vaultUri, true).apply(RECOVERABLE_SECRET_NAME),
"secret was not created before timeout");
}
@@ -692,7 +681,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = "testRecoverDeletedSecret")
public void testPurgeDeletedSecret() {
// delete the secret
- DeletedSecretBundle dsb = api().deleteSecret(vaultUri, RECOVERABLE_SECRET_NAME);
+ api().deleteSecret(vaultUri, RECOVERABLE_SECRET_NAME);
checkState(deletedSecretStatus.create(resourceGroupName, vaultUri, true).apply(RECOVERABLE_SECRET_NAME),
"secret was not deleted before timeout");
@@ -1041,7 +1030,7 @@ public class VaultApiLiveTest extends BaseAzureComputeApiLiveTest {
@Test(dependsOnMethods = "testRecoverDeletedCertificate")
public void testPurgeDeletedCertificate() {
// delete the certificate
- DeletedCertificateBundle dcb = api().deleteCertificate(vaultUri, RECOVERABLE_CERTIFICATE_NAME);
+ api().deleteCertificate(vaultUri, RECOVERABLE_CERTIFICATE_NAME);
checkState(deletedCertificateStatus.create(resourceGroupName, vaultUri, true).apply(RECOVERABLE_CERTIFICATE_NAME),
"certificate was not deleted before timeout");
http://git-wip-us.apache.org/repos/asf/jclouds/blob/c96028a2/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiMockTest.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiMockTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiMockTest.java
index 7459f48..be3ae74 100644
--- a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiMockTest.java
+++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/features/VaultApiMockTest.java
@@ -84,15 +84,12 @@ public class VaultApiMockTest extends BaseAzureComputeApiMockTest {
private URI vaultUri;
private static String KEY_NAME = "myKey";
- private static String TEMP_KEY_NAME = "myTempKey";
private static String IMPORT_KEY_NAME = "myImportKey";
private static String RECOVERABLE_KEY_NAME = "myRecoverableKey";
private static String SECRET_NAME = "mySecret";
private static String RECOVERABLE_SECRET_NAME = "myRecoverableSecret";
private static String CERTIFICATE_NAME = "myCertificate";
- private static String TEMP_CERTIFICATE_NAME = "myTempCertificate";
private static String RECOVERABLE_CERTIFICATE_NAME = "myRecoverableCertificate";
- private static String IMPORTABLE_CERTIFICATE_NAME = "myImportableCertificate";
private static String CERTIFICATE_ISSUER_NAME = "globalsign01";
private String IMPORTABLE_CERTIFICATE_PEM = stringFromResource("/vaultimportablecert.txt");
private String sampleSecret = stringFromResource("/vaultsamplesecret.txt");
[2/2] jclouds git commit: JCLOUDS-1294: Attempt to retry
RetryableErrors in Azure ARM
Posted by na...@apache.org.
JCLOUDS-1294: Attempt to retry RetryableErrors in Azure ARM
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/b144d9f4
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/b144d9f4
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/b144d9f4
Branch: refs/heads/2.1.x
Commit: b144d9f47310e863536ec94b2c1eb53398129dfd
Parents: c96028a
Author: Ignasi Barrera <na...@apache.org>
Authored: Fri May 4 09:27:25 2018 +0200
Committer: Ignasi Barrera <na...@apache.org>
Committed: Fri May 4 11:04:55 2018 +0200
----------------------------------------------------------------------
.../http/handlers/RateLimitRetryHandler.java | 2 +-
.../jclouds/azurecompute/arm/domain/Error.java | 58 +++++++++++++
.../arm/handlers/AzureComputeErrorHandler.java | 13 ++-
.../handlers/AzureRateLimitRetryHandler.java | 28 +++++++
.../handlers/AzureRetryableErrorHandler.java | 81 ++++++++++++++++++
.../AzureRetryableErrorHandlerTest.java | 88 ++++++++++++++++++++
6 files changed, 267 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/core/src/main/java/org/jclouds/http/handlers/RateLimitRetryHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/http/handlers/RateLimitRetryHandler.java b/core/src/main/java/org/jclouds/http/handlers/RateLimitRetryHandler.java
index 862ffd4..e5fda77 100644
--- a/core/src/main/java/org/jclouds/http/handlers/RateLimitRetryHandler.java
+++ b/core/src/main/java/org/jclouds/http/handlers/RateLimitRetryHandler.java
@@ -87,7 +87,7 @@ public abstract class RateLimitRetryHandler implements HttpRetryHandler {
}
}
- private boolean delayRequestUntilAllowed(final HttpCommand command, final HttpResponse response) {
+ protected boolean delayRequestUntilAllowed(final HttpCommand command, final HttpResponse response) {
Optional<Long> millisToNextAvailableRequest = millisToNextAvailableRequest(command, response);
if (!millisToNextAvailableRequest.isPresent()) {
logger.error("Cannot retry after rate limit error, no retry information provided in the response");
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Error.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Error.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Error.java
new file mode 100644
index 0000000..19a045f
--- /dev/null
+++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/domain/Error.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package org.jclouds.azurecompute.arm.domain;
+
+import java.util.List;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+@AutoValue
+public abstract class Error {
+
+ public abstract Details details();
+
+ @SerializedNames({ "error" })
+ public static Error create(Details details) {
+ return new AutoValue_Error(details);
+ }
+
+ Error() {
+
+ }
+
+ @AutoValue
+ public abstract static class Details {
+ public abstract String code();
+ public abstract String message();
+ public abstract List<Details> details();
+
+ @SerializedNames({ "code", "message", "details" })
+ public static Details create(String code, String message, @Nullable List<Details> details) {
+ return new AutoValue_Error_Details(code, message, details == null ? ImmutableList.<Details> of()
+ : ImmutableList.copyOf(details));
+ }
+
+ Details() {
+
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
index 8492d51..6673001 100644
--- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
+++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureComputeErrorHandler.java
@@ -16,6 +16,8 @@
*/
package org.jclouds.azurecompute.arm.handlers;
+import static org.jclouds.azurecompute.arm.handlers.AzureRateLimitRetryHandler.isRateLimitError;
+
import java.io.IOException;
import javax.inject.Singleton;
@@ -38,7 +40,10 @@ public class AzureComputeErrorHandler implements HttpErrorHandler {
@Override
public void handleError(final HttpCommand command, final HttpResponse response) {
- // it is important to always read fully and close streams
+ // It is important to always read fully and close streams
+ // For 429 errors the response body might have already been consumed as
+ // some errors report information in the response body that needs to be
+ // handled by the retry handlers.
String message = parseMessage(response);
Exception exception = message == null
? new HttpResponseException(command, response)
@@ -70,7 +75,11 @@ public class AzureComputeErrorHandler implements HttpErrorHandler {
exception = new IllegalStateException(message, exception);
break;
case 429:
- exception = new AzureComputeRateLimitExceededException(response, exception);
+ if (isRateLimitError(response)) {
+ exception = new AzureComputeRateLimitExceededException(response, exception);
+ } else {
+ exception = new IllegalStateException(message, exception);
+ }
break;
default:
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRateLimitRetryHandler.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRateLimitRetryHandler.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRateLimitRetryHandler.java
index ee5f5e5..e2c6270 100644
--- a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRateLimitRetryHandler.java
+++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRateLimitRetryHandler.java
@@ -16,6 +16,7 @@
*/
package org.jclouds.azurecompute.arm.handlers;
+import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
@@ -26,14 +27,41 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.net.HttpHeaders;
+/**
+ * Handles 429 Too Many Requests responses.
+ * <p>
+ * The Azure ARM provider also returns this 429 HTTP status code for some errors
+ * when resources are busy or in a state where they cannot be modified. In this
+ * case this handler delegates to the {@link AzureRetryableErrorHandler} to
+ * determine if the requests can be retried.
+ */
@Beta
@Singleton
public class AzureRateLimitRetryHandler extends RateLimitRetryHandler {
+ private final AzureRetryableErrorHandler retryableErrorHandler;
+
+ @Inject
+ AzureRateLimitRetryHandler(AzureRetryableErrorHandler retryableErrorHandler) {
+ this.retryableErrorHandler = retryableErrorHandler;
+ }
+
+ @Override
+ protected boolean delayRequestUntilAllowed(HttpCommand command, HttpResponse response) {
+ if (!isRateLimitError(response)) {
+ return retryableErrorHandler.shouldRetryRequest(command, response);
+ }
+ return super.delayRequestUntilAllowed(command, response);
+ }
+
@Override
protected Optional<Long> millisToNextAvailableRequest(HttpCommand command, HttpResponse response) {
String secondsToNextAvailableRequest = response.getFirstHeaderOrNull(HttpHeaders.RETRY_AFTER);
return secondsToNextAvailableRequest != null ? Optional.of(Long.valueOf(secondsToNextAvailableRequest) * 1000)
: Optional.<Long> absent();
}
+
+ public static boolean isRateLimitError(HttpResponse response) {
+ return response.getFirstHeaderOrNull(HttpHeaders.RETRY_AFTER) != null;
+ }
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandler.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandler.java b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandler.java
new file mode 100644
index 0000000..07f7b5d
--- /dev/null
+++ b/providers/azurecompute-arm/src/main/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandler.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+package org.jclouds.azurecompute.arm.handlers;
+
+import static org.jclouds.azurecompute.arm.handlers.AzureRateLimitRetryHandler.isRateLimitError;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.azurecompute.arm.domain.Error;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.logging.Logger;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * This handles failed responses that return a <code>RetryableError</code>.
+ * <p>
+ * In order to determine if the error is retryable, the response body must be
+ * read, so this handler will have to buffer the response payload in memory so
+ * the response body can be read again in subsequent steps of the response
+ * processing flow.
+ */
+@Singleton
+@Beta
+public class AzureRetryableErrorHandler extends BackoffLimitedRetryHandler {
+
+ private static final String RETRYABLE_ERROR_CODE = "RetryableError";
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+ private final ParseJson<Error> parseError;
+
+ @Inject
+ AzureRetryableErrorHandler(ParseJson<Error> parseError) {
+ this.parseError = parseError;
+ }
+
+ @Override
+ public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
+ // Only consider retryable errors and discard rate limit ones
+ if (response.getStatusCode() != 429 || isRateLimitError(response)) {
+ return false;
+ }
+
+ try {
+ // Note that this will consume the response body. At this point,
+ // subsequent retry handlers or error handlers will not be able to read
+ // again the payload, but that should only be attempted when the
+ // command is not retryable and an exception should be thrown.
+ Error error = parseError.apply(response);
+ logger.debug("processing error: %s", error);
+
+ boolean isRetryable = RETRYABLE_ERROR_CODE.equals(error.details().code());
+ return isRetryable ? super.shouldRetryRequest(command, response) : false;
+ } catch (Exception ex) {
+ // If we can't parse the error, just assume it is not a retryable error
+ logger.warn("could not parse error. Request won't be retried: %s", ex.getMessage());
+ return false;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/b144d9f4/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandlerTest.java
----------------------------------------------------------------------
diff --git a/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandlerTest.java b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandlerTest.java
new file mode 100644
index 0000000..49103ae
--- /dev/null
+++ b/providers/azurecompute-arm/src/test/java/org/jclouds/azurecompute/arm/handlers/AzureRetryableErrorHandlerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package org.jclouds.azurecompute.arm.handlers;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.json.config.GsonModule;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+@Test(groups = "unit", testName = "AzureRetryableErrorHandlerTest")
+public class AzureRetryableErrorHandlerTest {
+
+ private HttpRetryHandler handler;
+
+ @BeforeClass
+ public void setup() {
+ // Initialize an injector with just the Json features to get all
+ // serialization stuff
+ Injector injector = Guice.createInjector(new GsonModule());
+ handler = injector.getInstance(AzureRetryableErrorHandler.class);
+ }
+
+ @Test
+ public void testDoesNotRetryWhenNot429() {
+ HttpCommand command = new HttpCommand(HttpRequest.builder().method("GET").endpoint("http://localhost").build());
+ HttpResponse response = HttpResponse.builder().statusCode(400).build();
+
+ assertFalse(handler.shouldRetryRequest(command, response));
+ }
+
+ @Test
+ public void testDoesNotRetryWhenRateLimitError() {
+ HttpCommand command = new HttpCommand(HttpRequest.builder().method("GET").endpoint("http://localhost").build());
+ HttpResponse response = HttpResponse.builder().statusCode(429).addHeader(HttpHeaders.RETRY_AFTER, "15").build();
+
+ assertFalse(handler.shouldRetryRequest(command, response));
+ }
+
+ @Test
+ public void testDoesNotRetryWhenCannotParseError() {
+ HttpCommand command = new HttpCommand(HttpRequest.builder().method("GET").endpoint("http://localhost").build());
+ HttpResponse response = HttpResponse.builder().statusCode(429).payload("foo").build();
+
+ assertFalse(handler.shouldRetryRequest(command, response));
+ }
+
+ @Test
+ public void testDoesNotRetryWhenErrorNotRetryable() {
+ String nonRetryable = "{\"error\":{\"code\":\"ReferencedResourceNotProvisioned\",\"message\":\"Not provisioned\"}}";
+ HttpCommand command = new HttpCommand(HttpRequest.builder().method("GET").endpoint("http://localhost").build());
+ HttpResponse response = HttpResponse.builder().statusCode(429).payload(nonRetryable).build();
+
+ assertFalse(handler.shouldRetryRequest(command, response));
+ }
+
+ @Test
+ public void testRetriesWhenRetryableError() {
+ String retryable = "{\"error\":{\"code\":\"RetryableError\",\"message\":\"Resource busy\"}}";
+ HttpCommand command = new HttpCommand(HttpRequest.builder().method("GET").endpoint("http://localhost").build());
+ HttpResponse response = HttpResponse.builder().statusCode(429).payload(retryable).build();
+
+ assertTrue(handler.shouldRetryRequest(command, response));
+ }
+}