YARN-3541. Add version info on timeline service / generic history web UI and REST API. Contributed by Zhijie Shen

This commit is contained in:
Xuan 2015-05-18 13:17:16 -07:00
parent cdfae446ad
commit 76afd28862
14 changed files with 404 additions and 43 deletions

View File

@ -114,6 +114,9 @@ Release 2.8.0 - UNRELEASED
YARN-3505. Node's Log Aggregation Report with SUCCEED should not cached in
RMApps. (Xuan Gong via junping_du)
YARN-3541. Add version info on timeline service / generic history web UI
and REST API. (Zhijie Shen via xgong)
IMPROVEMENTS
YARN-644. Basic null check is not performed on passed in arguments before

View File

@ -0,0 +1,116 @@
/**
* 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.yarn.api.records.timeline;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "about")
@XmlAccessorType(XmlAccessType.NONE)
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class TimelineAbout {
private String about;
private String timelineServiceVersion;
private String timelineServiceBuildVersion;
private String timelineServiceVersionBuiltOn;
private String hadoopVersion;
private String hadoopBuildVersion;
private String hadoopVersionBuiltOn;
public TimelineAbout() {
}
public TimelineAbout(String about) {
this.about = about;
}
@XmlElement(name = "About")
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
@XmlElement(name = "timeline-service-version")
public String getTimelineServiceVersion() {
return timelineServiceVersion;
}
public void setTimelineServiceVersion(String timelineServiceVersion) {
this.timelineServiceVersion = timelineServiceVersion;
}
@XmlElement(name = "timeline-service-build-version")
public String getTimelineServiceBuildVersion() {
return timelineServiceBuildVersion;
}
public void setTimelineServiceBuildVersion(
String timelineServiceBuildVersion) {
this.timelineServiceBuildVersion = timelineServiceBuildVersion;
}
@XmlElement(name = "timeline-service-version-built-on")
public String getTimelineServiceVersionBuiltOn() {
return timelineServiceVersionBuiltOn;
}
public void setTimelineServiceVersionBuiltOn(
String timelineServiceVersionBuiltOn) {
this.timelineServiceVersionBuiltOn = timelineServiceVersionBuiltOn;
}
@XmlElement(name = "hadoop-version")
public String getHadoopVersion() {
return hadoopVersion;
}
public void setHadoopVersion(String hadoopVersion) {
this.hadoopVersion = hadoopVersion;
}
@XmlElement(name = "hadoop-build-version")
public String getHadoopBuildVersion() {
return hadoopBuildVersion;
}
public void setHadoopBuildVersion(String hadoopBuildVersion) {
this.hadoopBuildVersion = hadoopBuildVersion;
}
@XmlElement(name = "hadoop-version-built-on")
public String getHadoopVersionBuiltOn() {
return hadoopVersionBuiltOn;
}
public void setHadoopVersionBuiltOn(String hadoopVersionBuiltOn) {
this.hadoopVersionBuiltOn = hadoopVersionBuiltOn;
}
}

View File

@ -26,7 +26,10 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.util.VersionInfo;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
@ -83,6 +86,17 @@ public static String dumpTimelineRecordtoJSON(Object o, boolean pretty)
}
}
public static TimelineAbout createTimelineAbout(String about) {
TimelineAbout tsInfo = new TimelineAbout(about);
tsInfo.setHadoopBuildVersion(VersionInfo.getBuildVersion());
tsInfo.setHadoopVersion(VersionInfo.getVersion());
tsInfo.setHadoopVersionBuiltOn(VersionInfo.getDate());
tsInfo.setTimelineServiceBuildVersion(YarnVersionInfo.getBuildVersion());
tsInfo.setTimelineServiceVersion(YarnVersionInfo.getVersion());
tsInfo.setTimelineServiceVersionBuiltOn(YarnVersionInfo.getDate());
return tsInfo;
}
public static InetSocketAddress getTimelineTokenServiceAddress(
Configuration conf) {
InetSocketAddress timelineServiceAddr = null;

View File

@ -34,6 +34,10 @@ public void index() {
setTitle("Application History");
}
public void about() {
render(AboutPage.class);
}
public void app() {
render(AppPage.class);
}

View File

@ -56,6 +56,7 @@ public void setup() {
bind(ApplicationBaseProtocol.class).toInstance(historyClientService);
bind(TimelineDataManager.class).toInstance(timelineDataManager);
route("/", AHSController.class);
route("/about", AHSController.class, "about");
route(pajoin("/apps", APP_STATE), AHSController.class);
route(pajoin("/app", APPLICATION_ID), AHSController.class, "app");
route(pajoin("/appattempt", APPLICATION_ATTEMPT_ID), AHSController.class,

View File

@ -34,6 +34,7 @@
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.api.ApplicationBaseProtocol;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.server.webapp.WebServices;
import org.apache.hadoop.yarn.server.webapp.dao.AppAttemptInfo;
import org.apache.hadoop.yarn.server.webapp.dao.AppAttemptsInfo;
@ -41,6 +42,7 @@
import org.apache.hadoop.yarn.server.webapp.dao.AppsInfo;
import org.apache.hadoop.yarn.server.webapp.dao.ContainerInfo;
import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
import org.apache.hadoop.yarn.webapp.BadRequestException;
import com.google.inject.Inject;
@ -55,6 +57,16 @@ public AHSWebServices(ApplicationBaseProtocol appBaseProt) {
super(appBaseProt);
}
@GET
@Path("/about")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public TimelineAbout about(
@Context HttpServletRequest req,
@Context HttpServletResponse res) {
init(res);
return TimelineUtils.createTimelineAbout("Generic History Service API");
}
@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public AppsInfo get(@Context HttpServletRequest req,

View File

@ -0,0 +1,47 @@
/**
* 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.yarn.server.applicationhistoryservice.webapp;
import com.google.inject.Inject;
import org.apache.hadoop.util.VersionInfo;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
import org.apache.hadoop.yarn.webapp.View;
import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
import org.apache.hadoop.yarn.webapp.view.InfoBlock;
public class AboutBlock extends HtmlBlock {
@Inject
AboutBlock(View.ViewContext ctx) {
super(ctx);
}
@Override
protected void render(Block html) {
TimelineAbout tsInfo = TimelineUtils.createTimelineAbout(
"Timeline Server - Generic History Service UI");
info("Timeline Server Overview").
_("Timeline Server Version:", tsInfo.getTimelineServiceBuildVersion() +
" on " + tsInfo.getTimelineServiceVersionBuiltOn()).
_("Hadoop Version:", tsInfo.getHadoopBuildVersion() +
" on " + tsInfo.getHadoopVersionBuiltOn());
html._(InfoBlock.class);
}
}

View File

@ -0,0 +1,36 @@
/**
* 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.yarn.server.applicationhistoryservice.webapp;
import org.apache.hadoop.yarn.webapp.SubView;
import org.apache.hadoop.yarn.webapp.YarnWebParams;
import static org.apache.hadoop.yarn.util.StringHelper.join;
public class AboutPage extends AHSView {
@Override protected void preHead(Page.HTML<_> html) {
commonPreHead(html);
set(TITLE, "Timeline Server - Generic History Service");
}
@Override protected Class<? extends SubView> content() {
return AboutBlock.class;
}
}

View File

@ -43,6 +43,8 @@ public void render(Block html) {
div("#nav").
h3("Application History").
ul().
li().a(url("about"), "About").
_().
li().a(url("apps"), "Applications").
ul().
li().a(url("apps",

View File

@ -42,17 +42,12 @@
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience.Public;
import org.apache.hadoop.classification.InterfaceStability.Unstable;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.util.VersionInfo;
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomain;
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomains;
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
@ -65,6 +60,9 @@
import org.apache.hadoop.yarn.server.timeline.NameValuePair;
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
import org.apache.hadoop.yarn.server.timeline.TimelineReader.Field;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.NotFoundException;
@ -86,43 +84,16 @@ public TimelineWebServices(TimelineDataManager timelineDataManager) {
this.timelineDataManager = timelineDataManager;
}
@XmlRootElement(name = "about")
@XmlAccessorType(XmlAccessType.NONE)
@Public
@Unstable
public static class AboutInfo {
private String about;
public AboutInfo() {
}
public AboutInfo(String about) {
this.about = about;
}
@XmlElement(name = "About")
public String getAbout() {
return about;
}
public void setAbout(String about) {
this.about = about;
}
}
/**
* Return the description of the timeline web services.
*/
@GET
@Produces({ MediaType.APPLICATION_JSON /* , MediaType.APPLICATION_XML */})
public AboutInfo about(
public TimelineAbout about(
@Context HttpServletRequest req,
@Context HttpServletResponse res) {
init(res);
return new AboutInfo("Timeline API");
return TimelineUtils.createTimelineAbout("Timeline API");
}
/**

View File

@ -87,6 +87,20 @@ public void testView() throws Exception {
WebAppTests.flushOutput(injector);
}
@Test
public void testAboutPage() throws Exception {
Injector injector =
WebAppTests.createMockInjector(ApplicationBaseProtocol.class,
mockApplicationHistoryClientService(0, 0, 0));
AboutPage aboutPageInstance = injector.getInstance(AboutPage.class);
aboutPageInstance.render();
WebAppTests.flushOutput(injector);
aboutPageInstance.render();
WebAppTests.flushOutput(injector);
}
@Test
public void testAppPage() throws Exception {
Injector injector =

View File

@ -49,6 +49,8 @@
import org.apache.hadoop.yarn.server.timeline.TimelineDataManager;
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
import org.apache.hadoop.yarn.server.timeline.security.TimelineACLsManager;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.JerseyTestBase;
import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
@ -57,6 +59,7 @@
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -215,6 +218,34 @@ public void testInvalidAccept() throws JSONException, Exception {
}
}
@Test
public void testAbout() throws Exception {
WebResource r = resource();
ClientResponse response = r
.path("ws").path("v1").path("applicationhistory").path("about")
.queryParam("user.name", USERS[round])
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
TimelineAbout actualAbout = response.getEntity(TimelineAbout.class);
TimelineAbout expectedAbout =
TimelineUtils.createTimelineAbout("Generic History Service API");
Assert.assertNotNull(
"Timeline service about response is null", actualAbout);
Assert.assertEquals(expectedAbout.getAbout(), actualAbout.getAbout());
Assert.assertEquals(expectedAbout.getTimelineServiceVersion(),
actualAbout.getTimelineServiceVersion());
Assert.assertEquals(expectedAbout.getTimelineServiceBuildVersion(),
actualAbout.getTimelineServiceBuildVersion());
Assert.assertEquals(expectedAbout.getTimelineServiceVersionBuiltOn(),
actualAbout.getTimelineServiceVersionBuiltOn());
Assert.assertEquals(expectedAbout.getHadoopVersion(),
actualAbout.getHadoopVersion());
Assert.assertEquals(expectedAbout.getHadoopBuildVersion(),
actualAbout.getHadoopBuildVersion());
Assert.assertEquals(expectedAbout.getHadoopVersionBuiltOn(),
actualAbout.getHadoopVersionBuiltOn());
}
@Test
public void testAppsQuery() throws Exception {
WebResource r = resource();

View File

@ -29,7 +29,6 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -59,6 +58,8 @@
import org.apache.hadoop.yarn.server.timeline.TimelineStore;
import org.apache.hadoop.yarn.server.timeline.security.TimelineACLsManager;
import org.apache.hadoop.yarn.server.timeline.security.TimelineAuthenticationFilter;
import org.apache.hadoop.yarn.api.records.timeline.TimelineAbout;
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.JerseyTestBase;
import org.apache.hadoop.yarn.webapp.YarnJacksonJaxbJsonProvider;
@ -184,10 +185,24 @@ public void testAbout() throws Exception {
.accept(MediaType.APPLICATION_JSON)
.get(ClientResponse.class);
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
TimelineWebServices.AboutInfo about =
response.getEntity(TimelineWebServices.AboutInfo.class);
Assert.assertNotNull(about);
Assert.assertEquals("Timeline API", about.getAbout());
TimelineAbout actualAbout = response.getEntity(TimelineAbout.class);
TimelineAbout expectedAbout =
TimelineUtils.createTimelineAbout("Timeline API");
Assert.assertNotNull(
"Timeline service about response is null", actualAbout);
Assert.assertEquals(expectedAbout.getAbout(), actualAbout.getAbout());
Assert.assertEquals(expectedAbout.getTimelineServiceVersion(),
actualAbout.getTimelineServiceVersion());
Assert.assertEquals(expectedAbout.getTimelineServiceBuildVersion(),
actualAbout.getTimelineServiceBuildVersion());
Assert.assertEquals(expectedAbout.getTimelineServiceVersionBuiltOn(),
actualAbout.getTimelineServiceVersionBuiltOn());
Assert.assertEquals(expectedAbout.getHadoopVersion(),
actualAbout.getHadoopVersion());
Assert.assertEquals(expectedAbout.getHadoopBuildVersion(),
actualAbout.getHadoopBuildVersion());
Assert.assertEquals(expectedAbout.getHadoopVersionBuiltOn(),
actualAbout.getHadoopVersionBuiltOn());
}
private static void verifyEntities(TimelineEntities entities) {

View File

@ -358,9 +358,17 @@ Here is a non-normative description of the API.
GET /ws/v1/timeline/
Returns a JSON object describing the server instance.
Returns a JSON object describing the server instance and version information.
{"About":"Timeline API"}
{
About: "Timeline API",
timeline-service-version: "3.0.0-SNAPSHOT",
timeline-service-build-version: "3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum e9ec74ea3ff7bc9f3d35e9cac694fb",
timeline-service-version-built-on: "2015-05-13T19:45Z",
hadoop-version: "3.0.0-SNAPSHOT",
hadoop-build-version: "3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum 95874b192923b43cdb96a6e483afd60",
hadoop-version-built-on: "2015-05-13T19:44Z"
}
## <a name="REST_API_DOMAINS"></a>Domains `/ws/v1/timeline/domain`
@ -889,6 +897,92 @@ Response Body:
Users can access the generic historic information of applications via REST
APIs.
## <a name="REST_API_ABOUT"></a>About
With the about API, you can get an timeline about resource that contains
generic history REST API description and version information.
It is essentially a XML/JSON-serialized form of the YARN `TimelineAbout`
structure.
### URI:
Use the following URI to obtain an timeline about object.
http(s)://<timeline server http(s) address:port>/ws/v1/applicationhistory/about
### HTTP Operations Supported:
GET
### Query Parameters Supported:
None
### Elements of the `about` (Application) Object:
| Item | Data Type | Description |
|:---- |:---- |:---- |
| `About` | string | The description about the service |
| `timeline-service-version` | string | The timeline service version |
| `timeline-service-build-version` | string | The timeline service build version |
| `timeline-service-version-built-on` | string | On what time the timeline service is built |
| `hadoop-version` | string | Hadoop version |
| `hadoop-build-version` | string | Hadoop build version |
| `hadoop-version-built-on` | string | On what time Hadoop is built |
### Response Examples:
#### JSON response
HTTP Request:
http://localhost:8188/ws/v1/applicationhistory/about
Response Header:
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked
Response Body:
{
About: "Generic History Service API",
timeline-service-version: "3.0.0-SNAPSHOT",
timeline-service-build-version: "3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum e9ec74ea3ff7bc9f3d35e9cac694fb",
timeline-service-version-built-on: "2015-05-13T19:45Z",
hadoop-version: "3.0.0-SNAPSHOT",
hadoop-build-version: "3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum 95874b192923b43cdb96a6e483afd60",
hadoop-version-built-on: "2015-05-13T19:44Z"
}
#### XML response
HTTP Request:
GET http://localhost:8188/ws/v1/applicationhistory/about
Accept: application/xml
Response Header:
HTTP/1.1 200 OK
Content-Type: application/xml
Content-Length: 748
Response Body:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<about>
<About>Generic History Service API</About>
<hadoop-build-version>3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum 95874b192923b43cdb96a6e483afd60</hadoop-build-version>
<hadoop-version>3.0.0-SNAPSHOT</hadoop-version>
<hadoop-version-built-on>2015-05-13T19:44Z</hadoop-version-built-on>
<timeline-service-build-version>3.0.0-SNAPSHOT from fcd0702c10ce574b887280476aba63d6682d5271 by zshen source checksum e9ec74ea3ff7bc9f3d35e9cac694fb</timeline-service-build-version>
<timeline-service-version>3.0.0-SNAPSHOT</timeline-service-version>
<timeline-service-version-built-on>2015-05-13T19:45Z</timeline-service-version-built-on>
</about>
## <a name="REST_API_LIST_APPLICATIONS"></a>Application List
With the Application List API, you can obtain a collection of resources, each
@ -1129,7 +1223,8 @@ With the Application API, you can get an application resource contains
information about a particular application that was running on an YARN
cluster.
It is essentially a JSON-serialized form of the YARN `ApplicationReport` structure.
It is essentially a XML/JSON-serialized form of the YARN `ApplicationReport`
structure.
### URI: