You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@slider.apache.org by st...@apache.org on 2015/01/21 18:55:46 UTC

[04/10] incubator-slider git commit: SLIDER-719 factoring out jersey tests into own class

SLIDER-719 factoring out jersey tests into own class


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

Branch: refs/heads/develop
Commit: c0a39d926b231167199655290bcbb43972cb86b2
Parents: c97f760
Author: Steve Loughran <st...@apache.org>
Authored: Tue Jan 20 10:32:24 2015 +0000
Committer: Steve Loughran <st...@apache.org>
Committed: Tue Jan 20 14:50:24 2015 +0000

----------------------------------------------------------------------
 .../agent/rest/JerseyTestDelegates.groovy       | 343 +++++++++++++++++++
 .../apache/slider/test/SliderTestUtils.groovy   |   5 +-
 2 files changed, 345 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/c0a39d92/slider-core/src/test/groovy/org/apache/slider/agent/rest/JerseyTestDelegates.groovy
----------------------------------------------------------------------
diff --git a/slider-core/src/test/groovy/org/apache/slider/agent/rest/JerseyTestDelegates.groovy b/slider-core/src/test/groovy/org/apache/slider/agent/rest/JerseyTestDelegates.groovy
new file mode 100644
index 0000000..2f20bf5
--- /dev/null
+++ b/slider-core/src/test/groovy/org/apache/slider/agent/rest/JerseyTestDelegates.groovy
@@ -0,0 +1,343 @@
+/*
+ * 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.apache.slider.agent.rest
+
+import com.sun.jersey.api.client.Client
+import com.sun.jersey.api.client.WebResource
+import groovy.transform.CompileStatic
+import groovy.util.logging.Slf4j
+import org.apache.hadoop.yarn.webapp.NotFoundException
+import org.apache.slider.api.StateValues
+import org.apache.slider.api.types.SerializedComponentInformation
+import org.apache.slider.api.types.SerializedContainerInformation
+import org.apache.slider.core.conf.AggregateConf
+import org.apache.slider.core.conf.ConfTree
+import org.apache.slider.core.conf.ConfTreeOperations
+import org.apache.slider.core.restclient.HttpOperationResponse
+import org.apache.slider.core.restclient.HttpVerb
+import org.apache.slider.core.restclient.UrlConnectionOperations
+import org.apache.slider.server.appmaster.web.rest.RestPaths
+import org.apache.slider.server.appmaster.web.rest.application.ApplicationResource
+import org.apache.slider.server.appmaster.web.rest.application.resources.PingResource
+import org.apache.slider.test.Outcome
+import org.apache.slider.test.SliderTestUtils
+
+import javax.ws.rs.core.MediaType
+
+import static org.apache.slider.api.ResourceKeys.COMPONENT_INSTANCES
+import static org.apache.slider.api.StatusKeys.*
+import static org.apache.slider.common.SliderKeys.COMPONENT_AM
+import static org.apache.slider.server.appmaster.web.rest.RestPaths.*
+
+/**
+ * This class contains parts of tests that can be run
+ * against a deployed AM: local or remote.
+ * It uses Jersey ... and must be passed a client that is either secure
+ * or not
+ * 
+ */
+@CompileStatic
+@Slf4j
+class JerseyTestDelegates extends SliderTestUtils {
+  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;
+  final Client jersey;
+  
+
+  JerseyTestDelegates(String appmaster, Client jersey) {
+    this.appmaster = appmaster
+    application = appendToURL(appmaster, RestPaths.SLIDER_PATH_APPLICATION)
+    this.jersey = jersey
+  }
+
+  /**
+   * <T> T get(Class<T> c)
+   * Get operation against the EM
+   * @param path path
+   * @return
+   */
+  public <T> T jerseyGet(String path, Class<T> c) {
+    assert c
+    WebResource webResource = buildResource(path)
+    webResource.get(c)
+  }
+
+  /**
+   * Build a resource against a path under the AM API
+   * @param path path
+   * @return a resource for use
+   */
+  public WebResource buildResource(String path) {
+    assert path
+    String fullpath = appendToURL(application, path)
+    WebResource webResource = jersey.resource(fullpath)
+    webResource.type(MediaType.APPLICATION_JSON)
+    log.info("HTTP operation against $fullpath");
+    return webResource
+  }
+
+  public void testJerseyGetConftree() throws Throwable {
+    jerseyGet(LIVE_RESOURCES, ConfTree.class);
+  }
+  public void testCodahaleOperations() throws Throwable {
+    describe "Codahale operations"
+    
+    getWebPage(appmaster)
+    getWebPage(appmaster, SYSTEM_THREADS)
+    getWebPage(appmaster, SYSTEM_HEALTHCHECK)
+    getWebPage(appmaster, SYSTEM_PING)
+    getWebPage(appmaster, SYSTEM_METRICS_JSON)
+  }
+  
+  public void logCodahaleMetrics() {
+    // query Coda Hale metrics
+    log.info getWebPage(appmaster, SYSTEM_HEALTHCHECK)
+    log.info getWebPage(appmaster, SYSTEM_METRICS)
+  }
+
+  public void testLiveResources() throws Throwable {
+    describe "Live Resources"
+    ConfTreeOperations tree = fetchConfigTree(appmaster, LIVE_RESOURCES)
+
+    log.info tree.toString()
+    def liveAM = tree.getComponent(COMPONENT_AM)
+    def desiredInstances = liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES);
+    assert desiredInstances ==
+           liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_ACTUAL)
+
+    assert 1 == liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_STARTED)
+    assert 0 == liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_REQUESTING)
+    assert 0 == liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_FAILED)
+    assert 0 == liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_COMPLETED)
+    assert 0 == liveAM.getMandatoryOptionInt(COMPONENT_INSTANCES_RELEASING)
+  }
+
+  public void testLiveContainers() throws Throwable {
+    describe "Application REST ${LIVE_CONTAINERS}"
+
+    Map<String, SerializedContainerInformation> containers =
+        fetchType(HashMap, appmaster, LIVE_CONTAINERS)
+    assert containers.size() == 1
+    log.info "${containers}"
+    SerializedContainerInformation amContainerInfo =
+        (SerializedContainerInformation) containers.values()[0]
+    assert amContainerInfo.containerId
+
+    def amContainerId = amContainerInfo.containerId
+    assert containers[amContainerId]
+
+    assert amContainerInfo.component == COMPONENT_AM
+    assert amContainerInfo.createTime > 0
+    assert amContainerInfo.exitCode == null
+    assert amContainerInfo.output == null
+    assert amContainerInfo.released == null
+    assert amContainerInfo.state == StateValues.STATE_LIVE
+
+    describe "containers"
+
+    SerializedContainerInformation retrievedContainerInfo =
+        fetchType(SerializedContainerInformation, appmaster,
+            LIVE_CONTAINERS + "/${amContainerId}")
+    assert retrievedContainerInfo.containerId == amContainerId
+
+    // fetch missing
+    try {
+      def result = fetchType(SerializedContainerInformation, appmaster,
+          LIVE_CONTAINERS + "/unknown")
+      fail("expected an error, got $result")
+    } catch (NotFoundException e) {
+      // expected
+    }
+
+
+    describe "components"
+
+    Map<String, SerializedComponentInformation> components =
+        fetchType(HashMap, appmaster, LIVE_COMPONENTS)
+    // two components
+    assert components.size() == 1
+    log.info "${components}"
+
+    SerializedComponentInformation amComponentInfo =
+        (SerializedComponentInformation) components[COMPONENT_AM]
+
+    SerializedComponentInformation amFullInfo = fetchType(
+        SerializedComponentInformation,
+        appmaster,
+        LIVE_COMPONENTS + "/${COMPONENT_AM}")
+
+    assert amFullInfo.containers.size() == 1
+    assert amFullInfo.containers[0] == amContainerId
+
+  }
+
+  /**
+   * Test the rest model. For this to work the cluster has to be configured
+   * with the global option
+   * @param appmaster
+   */
+  public void testRESTModel() {
+    describe "model"
+
+    assertPathServesList(appmaster,
+        MODEL,
+        ApplicationResource.MODEL_ENTRIES)
+
+    def unresolvedConf = fetchType(AggregateConf, appmaster, MODEL_DESIRED)
+//    log.info "Unresolved \n$unresolvedConf"
+    def unresolvedAppConf = unresolvedConf.appConfOperations
+
+    def sam = "slider-appmaster"
+    assert unresolvedAppConf.getComponentOpt(sam,
+        TEST_GLOBAL_OPTION, "") == ""
+    def resolvedConf = fetchType(AggregateConf, appmaster, MODEL_RESOLVED)
+//    log.info "Resolved \n$resolvedConf"
+    assert resolvedConf.appConfOperations.getComponentOpt(
+        sam, TEST_GLOBAL_OPTION, "") == TEST_GLOBAL_OPTION_PRESENT
+
+    def unresolved = fetchTypeList(ConfTree, appmaster,
+        [MODEL_DESIRED_APPCONF, MODEL_DESIRED_RESOURCES])
+    assert unresolved[MODEL_DESIRED_APPCONF].components[sam]
+    [TEST_GLOBAL_OPTION] == null
+
+
+    def resolved = fetchTypeList(ConfTree, appmaster,
+        [MODEL_RESOLVED_APPCONF, MODEL_RESOLVED_RESOURCES])
+    assert resolved[MODEL_RESOLVED_APPCONF].components[sam]
+    [TEST_GLOBAL_OPTION] ==
+    TEST_GLOBAL_OPTION_PRESENT
+  }
+
+
+  public void testRestletGetOperations() throws Throwable {
+    Client client = createJerseyClient()
+    String path = appendToURL(application, LIVE_RESOURCES)
+    WebResource webResource = client.resource(path)
+    webResource.type(MediaType.APPLICATION_JSON)
+               .get(ConfTree.class);
+  }
+
+  public void testPing() {
+    // GET
+    String ping = appendToURL(appmaster, SLIDER_PATH_APPLICATION, ACTION_PING)
+    describe "ping to AM URL $appmaster, ping URL $ping"
+    def pinged = fetchType(PingResource, appmaster, ACTION_PING + "?body=hello")
+    log.info "Ping GET: $pinged"
+
+    URL pingUrl = new URL(ping)
+    def message = "hello"
+
+    // HEAD
+    pingAction(HttpVerb.HEAD, pingUrl, message)
+
+    // Other verbs
+    pingAction(HttpVerb.POST, pingUrl, message)
+    pingAction(HttpVerb.PUT, pingUrl, message)
+    pingAction(HttpVerb.DELETE, pingUrl, message)
+
+  }
+
+
+  private HttpOperationResponse pingAction(
+      HttpVerb verb,
+      URL pingUrl,
+      String payload) {
+    return pingAction(connectionOperations, verb, pingUrl, payload)
+  }
+
+  private HttpOperationResponse pingAction(
+      UrlConnectionOperations ops, HttpVerb verb, URL pingUrl, String payload) {
+    def pinged
+    def outcome = ops.execHttpOperation(
+        verb,
+        pingUrl,
+        payload.bytes,
+        MediaType.TEXT_PLAIN)
+    byte[] bytes = outcome.data
+    if (verb.hasResponseBody()) {
+      assert bytes.length > 0, "0 bytes from ping $verb.verb"
+      pinged = deser(PingResource, bytes)
+      log.info "Ping $verb.verb: $pinged"
+      assert verb.verb == pinged.verb
+    } else {
+      assert bytes.length ==
+             0, "${bytes.length} bytes of data from ping $verb.verb"
+    }
+    return outcome
+  }
+
+  /**
+   * Test the stop command.
+   * 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],
+        true,
+        "AM failed to shut down") {
+      def pinged = fetchType(
+          PingResource,
+          appmaster,
+          ACTION_PING + "?body=hello")
+      fail("AM didn't shut down; Ping GET= $pinged")
+    }
+    
+  }
+
+  /**
+   * Probe that spins until the url specified by "url") refuses
+   * connections
+   * @param args argument map
+   * @return the outcome
+   */
+  Outcome probePingFailing(Map args) {
+    String ping = args["url"]
+    URL pingUrl = new URL(ping)
+    try {
+      def response = pingAction(HttpVerb.HEAD, pingUrl, "should not be running")
+      return Outcome.Retry
+    } catch (IOException e) {
+      // expected
+      return Outcome.Success
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/c0a39d92/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy
----------------------------------------------------------------------
diff --git a/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy b/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy
index df62057..897d7e3 100644
--- a/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy
@@ -643,7 +643,7 @@ class SliderTestUtils extends Assert {
     assert path
     assertHttpSupportInitialized()
 
-    log.info("Fetching HTTP content at " + path);
+    log.info("Fetching HTTP content at $path");
     URL url = new URL(path)
     def outcome = connectionOperations.execGet(url)
     String body = new String(outcome.data)
@@ -675,8 +675,7 @@ class SliderTestUtils extends Assert {
     log.info("Asserting component $component expected count $expected}",)
     int actual = extractLiveContainerCount(clusterDescription, component)
     if (expected != actual) {
-      log.warn(
-          "$component actual=$actual, expected $expected in \n$clusterDescription")
+      log.warn("$component actual=$actual, expected $expected in \n$clusterDescription")
     }
     assert expected == actual
   }