diff --git a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java index 38612f5223..67f48befe1 100644 --- a/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java +++ b/compute/src/main/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.java @@ -47,6 +47,7 @@ import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.base.Stopwatch; import com.google.common.collect.Multimap; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; @@ -127,16 +128,26 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap implements Cal try { if (options.shouldBlockUntilRunning()) { try { + Stopwatch stopwatch = new Stopwatch().start(); if (!nodeRunning.apply(node)) { + long timeWaited = stopwatch.elapsedMillis(); + long earlyReturnGrace = 10; // sleeps can sometimes return milliseconds early + if (node.get() == null) { node.set(originalNode); throw new IllegalStateException(format("api response for node(%s) was null, so we can't customize", originalId)); + } else if (timeWaited < (timeouts.nodeRunning - earlyReturnGrace)) { + throw new IllegalStateException( + format( + "node(%s) didn't achieve the state running, so we couldn't customize; aborting prematurely after %d seconds with final state: %s", + originalId, timeWaited / 1000, node.get().getState())); + } else { + throw new IllegalStateException( + format( + "node(%s) didn't achieve the state running within %d seconds, so we couldn't customize; final state: %s", + originalId, timeouts.nodeRunning / 1000, node.get().getState())); } - throw new IllegalStateException( - format( - "node(%s) didn't achieve the state running within %d seconds, so we couldn't customize; final state: %s", - originalId, timeouts.nodeRunning / 1000, node.get().getState())); } } catch (IllegalStateException e) { if (node.get().getState() == NodeState.TERMINATED) { diff --git a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java index 9e3db80062..9b36d3a382 100644 --- a/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java +++ b/compute/src/test/java/org/jclouds/compute/strategy/CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest.java @@ -23,6 +23,7 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; import java.util.Map; import java.util.NoSuchElementException; @@ -91,8 +92,9 @@ public class CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapTest { assertEquals(goodNodes.size(), 0); assertEquals(badNodes.keySet(), ImmutableSet.of(node)); - assertEquals(badNodes.get(node).getMessage(), - "node(id) didn't achieve the state running within 1200 seconds, so we couldn't customize; final state: PENDING"); + assertTrue(badNodes.get(node).getMessage() != null && badNodes.get(node).getMessage().matches( + "node\\(id\\) didn't achieve the state running, so we couldn't customize; aborting prematurely after .* seconds with final state: PENDING"), + badNodes.get(node).getMessage()); assertEquals(customizationResponses.size(), 0); // verify mocks