SLIDER-678: liveness API in REST API. includes test improvements and better 
exception handling


Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/e2388afc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/e2388afc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/e2388afc

Branch: refs/heads/develop
Commit: e2388afc210e8d2547a9c7da01e6b25a4d78bed3
Parents: dce1236
Author: Steve Loughran <[email protected]>
Authored: Fri Jan 30 19:55:00 2015 +0000
Committer: Steve Loughran <[email protected]>
Committed: Fri Jan 30 19:55:45 2015 +0000

----------------------------------------------------------------------
 .../slider/client/rest/BaseRestClient.java      | 12 ++++-
 .../client/rest/SliderApplicationAPI.java       | 52 ++++++++++++++----
 .../core/exceptions/ExceptionConverter.java     | 26 +++++++++
 .../application/actions/RestActionStop.java     |  3 +-
 .../agent/rest/AbstractRestTestDelegate.groovy  |  2 +
 .../agent/rest/LowLevelRestTestDelegates.groovy |  3 --
 .../rest/RestAPIClientTestDelegates.groovy      | 57 +++++++++-----------
 .../slider/agent/rest/TestStandaloneREST.groovy | 30 ++++-------
 .../funtest/lifecycle/AgentWebPagesIT.groovy    | 17 +++---
 9 files changed, 130 insertions(+), 72 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/main/java/org/apache/slider/client/rest/BaseRestClient.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/client/rest/BaseRestClient.java 
b/slider-core/src/main/java/org/apache/slider/client/rest/BaseRestClient.java
index 46be2aa..41cfe9d 100644
--- 
a/slider-core/src/main/java/org/apache/slider/client/rest/BaseRestClient.java
+++ 
b/slider-core/src/main/java/org/apache/slider/client/rest/BaseRestClient.java
@@ -20,9 +20,11 @@ package org.apache.slider.client.rest;
 
 import com.google.common.base.Preconditions;
 import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.GenericType;
 import com.sun.jersey.api.client.UniformInterfaceException;
 import com.sun.jersey.api.client.WebResource;
+import org.apache.slider.core.exceptions.ExceptionConverter;
 import org.apache.slider.core.restclient.HttpVerb;
 import org.apache.slider.core.restclient.UgiJerseyBinding;
 import org.slf4j.Logger;
@@ -34,7 +36,7 @@ import java.net.URI;
 
 
 /**
- * This is a base class for Jersey Rest clients in Slider.
+ * This is a base class for Jersey REST clients in Slider.
  * It supports bonding to an AM and the execution of operations —with
  * exceptions uprated to IOExceptions when needed.
  * <p>
@@ -89,6 +91,10 @@ public class BaseRestClient  {
       Preconditions.checkArgument(c != null);
       resource.accept(MediaType.APPLICATION_JSON_TYPE);
       return (T) resource.method(method.getVerb(), c);
+    } catch (ClientHandlerException ex) {
+      throw ExceptionConverter.convertJerseyException(method.getVerb(),
+          resource.getURI().toString(),
+          ex);
     } catch (UniformInterfaceException ex) {
       throw UgiJerseyBinding.uprateFaults(method,
           resource.getURI().toString(),
@@ -111,6 +117,10 @@ public class BaseRestClient  {
       Preconditions.checkArgument(t != null);
       resource.accept(MediaType.APPLICATION_JSON_TYPE);
       return resource.method(method.getVerb(), t);
+    } catch (ClientHandlerException ex) {
+      throw ExceptionConverter.convertJerseyException(method.getVerb(),
+          resource.getURI().toString(),
+          ex);
     } catch (UniformInterfaceException ex) {
       throw UgiJerseyBinding.uprateFaults(method, resource.getURI().toString(),
           ex);

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/main/java/org/apache/slider/client/rest/SliderApplicationAPI.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/client/rest/SliderApplicationAPI.java
 
b/slider-core/src/main/java/org/apache/slider/client/rest/SliderApplicationAPI.java
index a8dfe8f..ee4760e 100644
--- 
a/slider-core/src/main/java/org/apache/slider/client/rest/SliderApplicationAPI.java
+++ 
b/slider-core/src/main/java/org/apache/slider/client/rest/SliderApplicationAPI.java
@@ -60,15 +60,18 @@ public class SliderApplicationAPI extends BaseRestClient {
   }
 
   /**
-   * Create a resource under the application path
-   * @param subpath
-   * @return an resource under the application path
+   * Create a resource under the application path set up to accept
+   * JSON
+   * @param subpath path under application
+   * @return a resource under the application path
    */
   public WebResource applicationResource(String subpath) {
     Preconditions.checkArgument(!StringUtils.isEmpty(subpath),
         "empty path");
     Preconditions.checkNotNull(appResource, "Null app resource");
-    return appResource.path(subpath);
+    WebResource resource = appResource.path(subpath);
+    resource.accept(MediaType.APPLICATION_JSON_TYPE);
+    return resource;
   }
   
   /**
@@ -258,18 +261,49 @@ public class SliderApplicationAPI extends BaseRestClient {
   }
 
   /**
-   * Ping as a post
+   * Ping as a GET
    * @param text text to include
    * @return the response
    * @throws IOException on any failure
    */
   public PingResource ping(String text) throws IOException {
-    WebResource pingOut = applicationResource(ACTION_PING);
-    pingOut.accept(MediaType.APPLICATION_JSON_TYPE);
-    pingOut.type(MediaType.APPLICATION_JSON_TYPE);
+    return pingPost(text);
+  }
+  
+  /**
+   * Ping as a GET
+   * @param text text to include
+   * @return the response
+   * @throws IOException on any failure
+   */
+  public PingResource pingGet(String text) throws IOException {
+    WebResource pingResource = applicationResource(ACTION_PING);
+    pingResource.getUriBuilder().queryParam("body", text);
+    return pingResource.get(PingResource.class);
+  }
+  
+  /**
+   * Ping as a POST
+   * @param text text to include
+   * @return the response
+   * @throws IOException on any failure
+   */
+  public PingResource pingPost(String text) throws IOException {
+    WebResource pingResource = applicationResource(ACTION_PING);
+    pingResource.type(MediaType.APPLICATION_JSON_TYPE);
     Form f = new Form();
     f.add("text", text);
-    return pingOut.post(PingResource.class, f);
+    return pingResource.post(PingResource.class, f);
+  }
+
+  /**
+   * Stop the AM (async operation)
+   * @param text text to include
+   * @throws IOException on any failure
+   */
+  public void stop(String text) throws IOException {
+    WebResource resource = applicationResource(ACTION_STOP);
+    resource.post(text);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/main/java/org/apache/slider/core/exceptions/ExceptionConverter.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/core/exceptions/ExceptionConverter.java
 
b/slider-core/src/main/java/org/apache/slider/core/exceptions/ExceptionConverter.java
index e3cd508..02cc64a 100644
--- 
a/slider-core/src/main/java/org/apache/slider/core/exceptions/ExceptionConverter.java
+++ 
b/slider-core/src/main/java/org/apache/slider/core/exceptions/ExceptionConverter.java
@@ -18,6 +18,7 @@
 
 package org.apache.slider.core.exceptions;
 
+import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.UniformInterfaceException;
 import org.apache.hadoop.fs.PathAccessDeniedException;
@@ -70,4 +71,29 @@ public class ExceptionConverter {
     ioe.initCause(exception);
     return ioe; 
   }
+
+  /**
+   * Handle a client-side Jersey exception.
+   * <p>
+   * If there's an inner IOException, return that.
+   * <p>
+   * Otherwise: create a new wrapper IOE including verb and target details
+   * @param verb HTTP Verb used
+   * @param targetURL URL being targeted 
+   * @param exception original exception
+   * @return an exception to throw
+   */
+  public static IOException convertJerseyException(String verb,
+      String targetURL,
+      ClientHandlerException exception) {
+    if (exception.getCause() instanceof IOException) {
+      return (IOException)exception.getCause();
+    } else {
+      IOException ioe = new IOException(
+          verb + " " + targetURL + " failed: " + exception);
+      ioe.initCause(exception);
+      return ioe;
+    } 
+  }
+  
 }

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionStop.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionStop.java
 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionStop.java
index c0e30cc..544f589 100644
--- 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionStop.java
+++ 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionStop.java
@@ -51,9 +51,10 @@ public class RestActionStop {
             "Stopping action %s received at %tc",
             verb, time);
     response.text = text;
+    log.info(text);
     ActionStopSlider stopSlider =
         new ActionStopSlider(text,
-            500,
+            1000,
             TimeUnit.MILLISECONDS,
             LauncherExitCodes.EXIT_SUCCESS,
             FinalApplicationStatus.SUCCEEDED,

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/test/groovy/org/apache/slider/agent/rest/AbstractRestTestDelegate.groovy
----------------------------------------------------------------------
diff --git 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/AbstractRestTestDelegate.groovy
 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/AbstractRestTestDelegate.groovy
index 15026e4..9498e2f 100644
--- 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/AbstractRestTestDelegate.groovy
+++ 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/AbstractRestTestDelegate.groovy
@@ -26,6 +26,8 @@ import org.apache.slider.test.SliderTestUtils
 abstract class AbstractRestTestDelegate extends SliderTestUtils {
   public static final String TEST_GLOBAL_OPTION = "test.global.option"
   public static final String TEST_GLOBAL_OPTION_PRESENT = "present"
+  public static final int STOP_WAIT_TIME = 30000
+  public static final int STOP_PROBE_INTERVAL = 500
   public final boolean enableComplexVerbs
 
   AbstractRestTestDelegate(boolean enableComplexVerbs) {

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/test/groovy/org/apache/slider/agent/rest/LowLevelRestTestDelegates.groovy
----------------------------------------------------------------------
diff --git 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/LowLevelRestTestDelegates.groovy
 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/LowLevelRestTestDelegates.groovy
index be3de8c..f499d63 100644
--- 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/LowLevelRestTestDelegates.groovy
+++ 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/LowLevelRestTestDelegates.groovy
@@ -267,12 +267,9 @@ class LowLevelRestTestDelegates extends 
AbstractRestTestDelegate {
         MediaType.TEXT_PLAIN)
     log.info "Stopped: $outcome"
 
-    // await the shutdown
-    sleep(1000)
     
     // now a ping is expected to fail
     String ping = appendToURL(appmaster, SLIDER_PATH_APPLICATION, ACTION_PING)
-    URL pingUrl = new URL(ping)
 
     repeatUntilSuccess("probe for missing registry entry",
         this.&probePingFailing, 30000, 500,

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/test/groovy/org/apache/slider/agent/rest/RestAPIClientTestDelegates.groovy
----------------------------------------------------------------------
diff --git 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/RestAPIClientTestDelegates.groovy
 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/RestAPIClientTestDelegates.groovy
index 1851dd7..07c5ef0 100644
--- 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/RestAPIClientTestDelegates.groovy
+++ 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/RestAPIClientTestDelegates.groovy
@@ -29,6 +29,7 @@ import org.apache.slider.client.rest.SliderApplicationAPI
 import org.apache.slider.core.conf.ConfTree
 import org.apache.slider.core.conf.ConfTreeOperations
 import 
org.apache.slider.server.appmaster.web.rest.application.ApplicationResource
+import org.apache.slider.test.Outcome
 
 import javax.ws.rs.core.MediaType
 
@@ -44,8 +45,6 @@ import static 
org.apache.slider.server.appmaster.web.rest.RestPaths.*
 @CompileStatic
 @Slf4j
 class RestAPIClientTestDelegates extends AbstractRestTestDelegate {
-  public static final String TEST_GLOBAL_OPTION = "test.global.option"
-  public static final String TEST_GLOBAL_OPTION_PRESENT = "present"
 
   final String appmaster;
   final String application;
@@ -176,14 +175,14 @@ class RestAPIClientTestDelegates extends 
AbstractRestTestDelegate {
 
     def unresolved = fetchTypeList(ConfTree, appmaster,
         [MODEL_DESIRED_APPCONF, MODEL_DESIRED_RESOURCES])
-    assert unresolved[MODEL_DESIRED_APPCONF].components[sam]
-    [TEST_GLOBAL_OPTION] == null
+    assert null == 
+           
unresolved[MODEL_DESIRED_APPCONF].components[sam][TEST_GLOBAL_OPTION] 
 
 
     
     def resolvedAppconf = appAPI.getResolvedAppconf() 
-    assert resolvedAppconf.
-               components[sam][TEST_GLOBAL_OPTION] == 
TEST_GLOBAL_OPTION_PRESENT
+    assert TEST_GLOBAL_OPTION_PRESENT==
+           resolvedAppconf. components[sam][TEST_GLOBAL_OPTION]
   }
 
   public void testPing() {
@@ -199,39 +198,35 @@ class RestAPIClientTestDelegates extends 
AbstractRestTestDelegate {
    * Important: once executed, the AM is no longer there.
    * This must be the last test in the sequence.
    */
-/*
 
   public void testStop() {
-    String target = appendToURL(appmaster, SLIDER_PATH_APPLICATION, 
ACTION_STOP)
-    describe "Stop URL $target"
-    URL targetUrl = new URL(target)
-    def outcome = connectionOperations.execHttpOperation(
-        HttpVerb.POST,
-        targetUrl,
-        new byte[0],
-        MediaType.TEXT_PLAIN)
-    log.info "Stopped: $outcome"
-
-    // await the shutdown
-    sleep(1000)
-    
-    // now a ping is expected to fail
-    String ping = appendToURL(appmaster, SLIDER_PATH_APPLICATION, ACTION_PING)
-    URL pingUrl = new URL(ping)
 
-    repeatUntilSuccess("probe for missing registry entry",
-        this.&probePingFailing, 30000, 500,
-        [url: ping],
+    appAPI.stop("stop")
+
+    repeatUntilSuccess("probe for liveness",
+        this.&probeForLivenessFailing, STOP_WAIT_TIME, STOP_PROBE_INTERVAL,
+        [:],
         true,
         "AM failed to shut down") {
-      def pinged = jFetchType(ACTION_PING + "?body=hello",
-          PingResource
-      )
-      fail("AM didn't shut down; Ping GET= $pinged")
+      appAPI.getApplicationLiveness()
     }
     
   }
-*/
+
+  /**
+   * Probe that spins until the liveness query fails
+   * @param args argument map
+   * @return the outcome
+   */
+  Outcome probeForLivenessFailing(Map args) {
+    try {
+      appAPI.getApplicationLiveness()
+      return Outcome.Retry
+    } catch (IOException e) {
+      // expected
+      return Outcome.Success
+    }
+  }
 
   public void testSuiteGetOperations() {
 

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-core/src/test/groovy/org/apache/slider/agent/rest/TestStandaloneREST.groovy
----------------------------------------------------------------------
diff --git 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/TestStandaloneREST.groovy
 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/TestStandaloneREST.groovy
index ab5f156..e210812 100644
--- 
a/slider-core/src/test/groovy/org/apache/slider/agent/rest/TestStandaloneREST.groovy
+++ 
b/slider-core/src/test/groovy/org/apache/slider/agent/rest/TestStandaloneREST.groovy
@@ -124,14 +124,14 @@ class TestStandaloneREST extends AgentMiniClusterTestBase 
{
     def ugiClient = createUGIJerseyClient();
     
     describe "Proxy SliderRestClient Tests"
-    RestAPIClientTestDelegates proxySliderRestClient =
+    RestAPIClientTestDelegates proxySliderRestAPI =
         new RestAPIClientTestDelegates(proxyAM, ugiClient, proxyComplexVerbs)
-    proxySliderRestClient.testSuiteGetOperations()
+    proxySliderRestAPI.testSuiteGetOperations()
 
     describe "Direct SliderRestClient Tests"
-    RestAPIClientTestDelegates directSliderRestClient =
+    RestAPIClientTestDelegates directSliderRestAPI =
         new RestAPIClientTestDelegates(directAM, ugiClient, directComplexVerbs)
-    directSliderRestClient.testSuiteAll()
+    directSliderRestAPI.testSuiteAll()
     
     
     describe "Proxy Jersey Tests"
@@ -169,26 +169,14 @@ class TestStandaloneREST extends AgentMiniClusterTestBase 
{
     sliderApplicationApi.resolvedModel
     sliderApplicationApi.ping("registry located")
 
-/*    DISABLED: this client does not pass the tests.
-    
-    // http client direct
-    describe "Proxied Jersey Apache HttpClient"
-    JerseyTestDelegates proxiedHttpClientJersey =
-        new JerseyTestDelegates(proxyAM, createJerseyClientHttpClient())
-    proxiedHttpClientJersey.testSuiteGetOperations()
-    
-    describe "Direct Jersey Apache HttpClient"
-    JerseyTestDelegates directHttpClientJersey =
-        new JerseyTestDelegates(directAM, createJerseyClientHttpClient())
-    directHttpClientJersey.testSuiteGetOperations()
-    directHttpClientJersey.testSuiteComplexVerbs()
-    */
-    createJerseyClientHttpClient()
     // log the metrics to show what's up
     direct.logCodahaleMetrics();
 
-    // this MUST be the final test
-    direct.testStop();
+    // finally, stop the AM
+    if (directComplexVerbs) {
+      describe "Stopping AM via REST API"
+      directSliderRestAPI.testStop();
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/e2388afc/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentWebPagesIT.groovy
----------------------------------------------------------------------
diff --git 
a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentWebPagesIT.groovy
 
b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentWebPagesIT.groovy
index 2cd13dd..a364e63 100644
--- 
a/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentWebPagesIT.groovy
+++ 
b/slider-funtest/src/test/groovy/org/apache/slider/funtest/lifecycle/AgentWebPagesIT.groovy
@@ -138,14 +138,14 @@ public class AgentWebPagesIT extends AgentCommandTestBase
     directJerseyTests.testSuiteAll()
 
     describe "Proxy SliderRestClient Tests"
-    RestAPIClientTestDelegates proxySliderRestClient =
+    RestAPIClientTestDelegates proxySliderRestAPI =
         new RestAPIClientTestDelegates(proxyAM, ugiClient, proxyComplexVerbs)
-    proxySliderRestClient.testSuiteAll()
+    proxySliderRestAPI.testSuiteAll()
 
     describe "Direct SliderRestClient Tests"
-    RestAPIClientTestDelegates directSliderRestClient =
+    RestAPIClientTestDelegates directSliderRestAPI =
         new RestAPIClientTestDelegates(directAM, ugiClient, directComplexVerbs)
-    directSliderRestClient.testSuiteAll()
+    directSliderRestAPI.testSuiteAll()
 
     if (UserGroupInformation.securityEnabled) {
       describe "Insecure Proxy Tests against a secure cluster"
@@ -176,10 +176,15 @@ public class AgentWebPagesIT extends AgentCommandTestBase
     def sliderApplicationApi = restClientFactory.createSliderApplicationApi();
     sliderApplicationApi.desiredModel
     sliderApplicationApi.resolvedModel
-    sliderApplicationApi.ping("registry located")
+    if (proxyComplexVerbs) {
+      sliderApplicationApi.ping("registry located")
+    }
     
     // finally, stop the AM
-    direct.testStop();
+    if (directComplexVerbs) {
+      describe "Stopping AM via REST API"
+      directSliderRestAPI.testStop();
+    }
   }
 
 }

Reply via email to