|
@@ -0,0 +1,273 @@
|
|
|
+/**
|
|
|
+ * 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.hadoop.mapreduce.v2.app.webapp;
|
|
|
+
|
|
|
+import static org.junit.Assert.assertEquals;
|
|
|
+
|
|
|
+import java.io.StringReader;
|
|
|
+import java.util.Enumeration;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Properties;
|
|
|
+
|
|
|
+import javax.servlet.FilterConfig;
|
|
|
+import javax.servlet.ServletException;
|
|
|
+import javax.ws.rs.core.MediaType;
|
|
|
+import javax.xml.parsers.DocumentBuilder;
|
|
|
+import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
+
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.JobId;
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptId;
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptState;
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.AppContext;
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.MockAppContext;
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.job.Job;
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.job.Task;
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt;
|
|
|
+import org.apache.hadoop.mapreduce.v2.util.MRApps;
|
|
|
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
|
|
|
+import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
|
|
|
+import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
|
|
+import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
|
|
|
+import org.codehaus.jettison.json.JSONException;
|
|
|
+import org.codehaus.jettison.json.JSONObject;
|
|
|
+import org.junit.Before;
|
|
|
+import org.junit.Test;
|
|
|
+import org.w3c.dom.Document;
|
|
|
+import org.w3c.dom.Element;
|
|
|
+import org.w3c.dom.NodeList;
|
|
|
+import org.xml.sax.InputSource;
|
|
|
+
|
|
|
+import com.google.inject.Guice;
|
|
|
+import com.google.inject.Injector;
|
|
|
+import com.google.inject.Singleton;
|
|
|
+import com.google.inject.servlet.GuiceServletContextListener;
|
|
|
+import com.google.inject.servlet.ServletModule;
|
|
|
+import com.sun.jersey.api.client.ClientResponse;
|
|
|
+import com.sun.jersey.api.client.WebResource;
|
|
|
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
|
|
|
+import com.sun.jersey.test.framework.JerseyTest;
|
|
|
+import com.sun.jersey.test.framework.WebAppDescriptor;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Test the app master web service Rest API for getting task attempts, a
|
|
|
+ * specific task attempt, and task attempt counters
|
|
|
+ *
|
|
|
+ * /ws/v1/mapreduce/jobs/{jobid}/tasks/{taskid}/attempts/{attemptid}/state
|
|
|
+ */
|
|
|
+public class TestAMWebServicesAttempt extends JerseyTest {
|
|
|
+
|
|
|
+ private static Configuration conf = new Configuration();
|
|
|
+ private static AppContext appContext;
|
|
|
+ private String webserviceUserName = "testuser";
|
|
|
+
|
|
|
+ private Injector injector = Guice.createInjector(new ServletModule() {
|
|
|
+ @Override
|
|
|
+ protected void configureServlets() {
|
|
|
+ appContext = new MockAppContext(0, 1, 2, 1);
|
|
|
+ bind(JAXBContextResolver.class);
|
|
|
+ bind(AMWebServices.class);
|
|
|
+ bind(GenericExceptionHandler.class);
|
|
|
+ bind(AppContext.class).toInstance(appContext);
|
|
|
+ bind(Configuration.class).toInstance(conf);
|
|
|
+
|
|
|
+ serve("/*").with(GuiceContainer.class);
|
|
|
+ filter("/*").through(TestRMCustomAuthFilter.class);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ @Singleton
|
|
|
+ public static class TestRMCustomAuthFilter extends AuthenticationFilter {
|
|
|
+ @Override
|
|
|
+ protected Properties getConfiguration(String configPrefix,
|
|
|
+ FilterConfig filterConfig) throws ServletException {
|
|
|
+ Properties props = new Properties();
|
|
|
+ Enumeration<?> names = filterConfig.getInitParameterNames();
|
|
|
+ while (names.hasMoreElements()) {
|
|
|
+ String name = (String) names.nextElement();
|
|
|
+ if (name.startsWith(configPrefix)) {
|
|
|
+ String value = filterConfig.getInitParameter(name);
|
|
|
+ props.put(name.substring(configPrefix.length()), value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ props.put(AuthenticationFilter.AUTH_TYPE, "simple");
|
|
|
+ props.put(PseudoAuthenticationHandler.ANONYMOUS_ALLOWED, "false");
|
|
|
+ return props;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public class GuiceServletConfig extends GuiceServletContextListener {
|
|
|
+ @Override
|
|
|
+ protected Injector getInjector() {
|
|
|
+ return injector;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Before
|
|
|
+ @Override
|
|
|
+ public void setUp() throws Exception {
|
|
|
+ super.setUp();
|
|
|
+ }
|
|
|
+
|
|
|
+ public TestAMWebServicesAttempt() {
|
|
|
+ super(new WebAppDescriptor.Builder(
|
|
|
+ "org.apache.hadoop.mapreduce.v2.app.webapp")
|
|
|
+ .contextListenerClass(GuiceServletConfig.class)
|
|
|
+ .filterClass(com.google.inject.servlet.GuiceFilter.class)
|
|
|
+ .contextPath("jersey-guice-filter").servletPath("/").build());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testGetTaskAttemptIdState() throws Exception {
|
|
|
+ WebResource r = resource();
|
|
|
+ Map<JobId, Job> jobsMap = appContext.getAllJobs();
|
|
|
+
|
|
|
+ for (JobId id : jobsMap.keySet()) {
|
|
|
+ String jobId = MRApps.toString(id);
|
|
|
+
|
|
|
+ for (Task task : jobsMap.get(id).getTasks().values()) {
|
|
|
+ String tid = MRApps.toString(task.getID());
|
|
|
+
|
|
|
+ for (TaskAttempt att : task.getAttempts().values()) {
|
|
|
+ TaskAttemptId attemptid = att.getID();
|
|
|
+ String attid = MRApps.toString(attemptid);
|
|
|
+
|
|
|
+ ClientResponse response = r.path("ws").path("v1").path("mapreduce")
|
|
|
+ .path("jobs").path(jobId).path("tasks").path(tid)
|
|
|
+ .path("attempts").path(attid).path("state")
|
|
|
+ .queryParam("user.name", webserviceUserName)
|
|
|
+ .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
|
|
+ assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
|
|
+ JSONObject json = response.getEntity(JSONObject.class);
|
|
|
+ assertEquals("incorrect number of elements", 1, json.length());
|
|
|
+ assertEquals(att.getState().toString(), json.get("state"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testGetTaskAttemptIdXMLState() throws Exception {
|
|
|
+ WebResource r = resource();
|
|
|
+ Map<JobId, Job> jobsMap = appContext.getAllJobs();
|
|
|
+ for (JobId id : jobsMap.keySet()) {
|
|
|
+ String jobId = MRApps.toString(id);
|
|
|
+ for (Task task : jobsMap.get(id).getTasks().values()) {
|
|
|
+
|
|
|
+ String tid = MRApps.toString(task.getID());
|
|
|
+ for (TaskAttempt att : task.getAttempts().values()) {
|
|
|
+ TaskAttemptId attemptid = att.getID();
|
|
|
+ String attid = MRApps.toString(attemptid);
|
|
|
+
|
|
|
+ ClientResponse response = r.path("ws").path("v1").path("mapreduce")
|
|
|
+ .path("jobs").path(jobId).path("tasks").path(tid)
|
|
|
+ .path("attempts").path(attid).path("state")
|
|
|
+ .queryParam("user.name", webserviceUserName)
|
|
|
+ .accept(MediaType.APPLICATION_XML).get(ClientResponse.class);
|
|
|
+
|
|
|
+ assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
|
|
|
+ String xml = response.getEntity(String.class);
|
|
|
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
|
+ DocumentBuilder db = dbf.newDocumentBuilder();
|
|
|
+ InputSource is = new InputSource();
|
|
|
+ is.setCharacterStream(new StringReader(xml));
|
|
|
+ Document dom = db.parse(is);
|
|
|
+ NodeList nodes = dom.getElementsByTagName("jobTaskAttemptState");
|
|
|
+ assertEquals(1, nodes.getLength());
|
|
|
+ String state = WebServicesTestUtils.getXmlString(
|
|
|
+ (Element) nodes.item(0), "state");
|
|
|
+ assertEquals(att.getState().toString(), state);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testPutTaskAttemptIdState() throws Exception {
|
|
|
+ WebResource r = resource();
|
|
|
+ Map<JobId, Job> jobsMap = appContext.getAllJobs();
|
|
|
+
|
|
|
+ for (JobId id : jobsMap.keySet()) {
|
|
|
+ String jobId = MRApps.toString(id);
|
|
|
+
|
|
|
+ for (Task task : jobsMap.get(id).getTasks().values()) {
|
|
|
+ String tid = MRApps.toString(task.getID());
|
|
|
+
|
|
|
+ for (TaskAttempt att : task.getAttempts().values()) {
|
|
|
+ TaskAttemptId attemptid = att.getID();
|
|
|
+ String attid = MRApps.toString(attemptid);
|
|
|
+
|
|
|
+ ClientResponse response = r.path("ws").path("v1").path("mapreduce")
|
|
|
+ .path("jobs").path(jobId).path("tasks").path(tid)
|
|
|
+ .path("attempts").path(attid).path("state")
|
|
|
+ .queryParam("user.name", webserviceUserName)
|
|
|
+ .accept(MediaType.APPLICATION_JSON)
|
|
|
+ .type(MediaType.APPLICATION_JSON)
|
|
|
+ .put(ClientResponse.class, "{\"state\":\"KILLED\"}");
|
|
|
+ assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
|
|
+ JSONObject json = response.getEntity(JSONObject.class);
|
|
|
+ assertEquals("incorrect number of elements", 1, json.length());
|
|
|
+ assertEquals(TaskAttemptState.KILLED.toString(), json.get("state"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testPutTaskAttemptIdXMLState() throws Exception {
|
|
|
+ WebResource r = resource();
|
|
|
+ Map<JobId, Job> jobsMap = appContext.getAllJobs();
|
|
|
+
|
|
|
+ for (JobId id : jobsMap.keySet()) {
|
|
|
+ String jobId = MRApps.toString(id);
|
|
|
+
|
|
|
+ for (Task task : jobsMap.get(id).getTasks().values()) {
|
|
|
+ String tid = MRApps.toString(task.getID());
|
|
|
+
|
|
|
+ for (TaskAttempt att : task.getAttempts().values()) {
|
|
|
+ TaskAttemptId attemptid = att.getID();
|
|
|
+ String attid = MRApps.toString(attemptid);
|
|
|
+
|
|
|
+ ClientResponse response = r.path("ws").path("v1").path("mapreduce")
|
|
|
+ .path("jobs").path(jobId).path("tasks").path(tid)
|
|
|
+ .path("attempts").path(attid).path("state")
|
|
|
+ .queryParam("user.name", webserviceUserName)
|
|
|
+ .accept(MediaType.APPLICATION_XML_TYPE)
|
|
|
+ .type(MediaType.APPLICATION_XML_TYPE)
|
|
|
+ .put(ClientResponse.class,
|
|
|
+ "<jobTaskAttemptState><state>KILLED" +
|
|
|
+ "</state></jobTaskAttemptState>");
|
|
|
+ assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
|
|
|
+ String xml = response.getEntity(String.class);
|
|
|
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
|
+ DocumentBuilder db = dbf.newDocumentBuilder();
|
|
|
+ InputSource is = new InputSource();
|
|
|
+ is.setCharacterStream(new StringReader(xml));
|
|
|
+ Document dom = db.parse(is);
|
|
|
+ NodeList nodes = dom.getElementsByTagName("jobTaskAttemptState");
|
|
|
+ assertEquals(1, nodes.getLength());
|
|
|
+ String state = WebServicesTestUtils.getXmlString(
|
|
|
+ (Element) nodes.item(0), "state");
|
|
|
+ assertEquals(TaskAttemptState.KILLED.toString(), state);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|