Repository: jclouds
Updated Branches:
  refs/heads/master c83a08a8d -> 5113be22d


JCLOUDS-1293 Add custom IOException retry handler for AWS-EC2

As all methods use POST we can not use the method to determine if funciton is 
idempotent.
We therefore set all as idempotent to nullify that check and add a custom
IOException retry handler to determine if commands should be retried on 
IOException.

The custom hander extends the BackoffLimitedRetryHandler, the current handler, 
so all other behaviour is not affected.

This does not retry any POST methods unless it's ACTION starts with 'Describe'. 
These functions are idempotent, and therefore safe to retry.

See JCLOUDS-1293


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/5113be22
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/5113be22
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/5113be22

Branch: refs/heads/master
Commit: 5113be22d888ba2bd403ef7549ea692163f5ff10
Parents: c83a08a
Author: Stuart Hendren <[email protected]>
Authored: Wed Jun 7 10:46:42 2017 +0100
Committer: Ignasi Barrera <[email protected]>
Committed: Wed Jun 7 12:00:06 2017 +0200

----------------------------------------------------------------------
 .../org/jclouds/aws/ec2/AWSEC2ApiMetadata.java  |  3 ++
 .../AWSEC2ComputeServiceContextModule.java      |  3 ++
 .../strategy/AWSEC2IOExceptionRetryHandler.java | 45 ++++++++++++++++
 .../AWSEC2IOExceptionRetryHandlerTest.java      | 56 ++++++++++++++++++++
 4 files changed, 107 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/5113be22/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java 
b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
index 7cb4c83..fa5e017 100644
--- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
+++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2ApiMetadata.java
@@ -21,6 +21,7 @@ import static 
org.jclouds.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
 import java.net.URI;
 import java.util.Properties;
 
+import org.jclouds.Constants;
 import org.jclouds.aws.ec2.compute.AWSEC2ComputeServiceContext;
 import org.jclouds.aws.ec2.compute.config.AWSEC2ComputeServiceContextModule;
 import org.jclouds.aws.ec2.config.AWSEC2HttpApiModule;
@@ -53,6 +54,8 @@ public final class AWSEC2ApiMetadata extends 
BaseHttpApiMetadata<AWSEC2Api> {
       // authorized key executes after ssh has started.  
       properties.setProperty("jclouds.ssh.max-retries", "7");
       properties.setProperty("jclouds.ssh.retry-auth", "true");
+      // required for custom retry handler 
+      properties.setProperty(Constants.PROPERTY_IDEMPOTENT_METHODS, 
"DELETE,GET,HEAD,OPTIONS,PUT,POST");
       return properties;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds/blob/5113be22/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
 
b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
index e1a184f..6b6ab6a 100644
--- 
a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
+++ 
b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/config/AWSEC2ComputeServiceContextModule.java
@@ -29,6 +29,7 @@ import 
org.jclouds.aws.ec2.compute.functions.PresentSpotRequestsAndInstances;
 import 
org.jclouds.aws.ec2.compute.strategy.AWSEC2CreateNodesInGroupThenAddToSet;
 import org.jclouds.aws.ec2.compute.strategy.AWSEC2DestroyNodeStrategy;
 import org.jclouds.aws.ec2.compute.strategy.AWSEC2GetNodeMetadataStrategy;
+import org.jclouds.aws.ec2.compute.strategy.AWSEC2IOExceptionRetryHandler;
 import org.jclouds.aws.ec2.compute.strategy.AWSEC2ListNodesStrategy;
 import org.jclouds.aws.ec2.compute.strategy.AWSEC2ReviseParsedImage;
 import 
org.jclouds.aws.ec2.compute.strategy.CreateKeyPairPlacementAndSecurityGroupsAsNeededAndReturnRunOptions;
@@ -51,6 +52,7 @@ import org.jclouds.ec2.compute.strategy.EC2ListNodesStrategy;
 import org.jclouds.ec2.compute.strategy.ReviseParsedImage;
 import org.jclouds.ec2.compute.suppliers.EC2HardwareSupplier;
 import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier;
+import org.jclouds.http.IOExceptionRetryHandler;
 import org.jclouds.rest.AuthorizationException;
 import org.jclouds.rest.suppliers.SetAndThrowAuthorizationExceptionSupplier;
 
@@ -84,6 +86,7 @@ public class AWSEC2ComputeServiceContextModule extends 
BaseComputeServiceContext
       bind(PresentInstances.class).to(PresentSpotRequestsAndInstances.class);
       
bind(EC2CreateNodesInGroupThenAddToSet.class).to(AWSEC2CreateNodesInGroupThenAddToSet.class);
       
bind(RunningInstanceToNodeMetadata.class).to(AWSRunningInstanceToNodeMetadata.class);
+      
bind(IOExceptionRetryHandler.class).to(AWSEC2IOExceptionRetryHandler.class);
    }
 
    protected void installDependencies() {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/5113be22/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandler.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandler.java
 
b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandler.java
new file mode 100644
index 0000000..a54c478
--- /dev/null
+++ 
b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandler.java
@@ -0,0 +1,45 @@
+/*
+ * 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.aws.ec2.compute.strategy;
+
+import java.io.IOException;
+
+import org.jclouds.aws.reference.FormParameters;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.io.Payload;
+
+public class AWSEC2IOExceptionRetryHandler extends BackoffLimitedRetryHandler {
+
+   private static final String DESCRIBE_ACTION = FormParameters.ACTION + 
"=Describe";
+
+   @Override
+   public boolean shouldRetryRequest(HttpCommand command, IOException error) {
+      HttpRequest request = command.getCurrentRequest();
+      if ("POST".equals(request.getMethod())) {
+         Payload payload = request.getPayload();
+         if (!payload.getRawContent().toString().contains(DESCRIBE_ACTION)){
+            logger.error("Command not considered safe to retry because request 
method is POST and action may not be idempotent: %1$s",
+               command);
+            return false;
+         }
+      }
+      return super.shouldRetryRequest(command, error);
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/5113be22/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandlerTest.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandlerTest.java
 
b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandlerTest.java
new file mode 100644
index 0000000..5dd0d75
--- /dev/null
+++ 
b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/compute/strategy/AWSEC2IOExceptionRetryHandlerTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.aws.ec2.compute.strategy;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+
+import java.io.IOException;
+
+import org.jclouds.aws.reference.FormParameters;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpRequest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "AWSEC2IOExceptionRetryHandlerTest")
+public class AWSEC2IOExceptionRetryHandlerTest {
+
+   @Test
+   public void testDescribeMethodIsRetried() throws Exception {
+
+      AWSEC2IOExceptionRetryHandler handler = new 
AWSEC2IOExceptionRetryHandler();
+      IOException e = new IOException("test exception");
+      HttpRequest request = 
HttpRequest.builder().method("POST").endpoint("http://test.endpoint.com/";).addFormParam(FormParameters.ACTION,
 "DescribeInstance").build();
+      HttpCommand command = new HttpCommand(request);
+
+      assertTrue(handler.shouldRetryRequest(command, e));
+       
+   }
+    
+   @Test
+   public void testNonDescribeMethodIsNotRetried() throws Exception {
+       
+      AWSEC2IOExceptionRetryHandler handler = new 
AWSEC2IOExceptionRetryHandler();
+      IOException e = new IOException("test exception");
+      HttpRequest request = 
HttpRequest.builder().method("POST").endpoint("http://test.endpoint.com/";).addFormParam(FormParameters.ACTION,
 "RunInstances").build();
+      HttpCommand command = new HttpCommand(request);
+       
+      assertFalse(handler.shouldRetryRequest(command, e));
+       
+    }
+    
+}

Reply via email to