Browse Source

YARN-4206. Add Application timeouts in Application report and CLI. Contributed by Rohith Sharma K S.

(cherry picked from commit eb0a483ed07399c7a06ed9db4bfbec382de470da)
Sunil 8 years ago
parent
commit
c28dc5e5a4
12 changed files with 479 additions and 4 deletions
  1. 10 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/ApplicationReport.java
  2. 99 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/ApplicationTimeout.java
  3. 7 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_protos.proto
  4. 11 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java
  5. 9 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java
  6. 43 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java
  7. 40 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java
  8. 83 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ApplicationReportPBImpl.java
  9. 130 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ApplicationTimeoutPBImpl.java
  10. 2 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/TestPBImplRecords.java
  11. 17 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java
  12. 28 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestApplicationLifetimeMonitor.java

+ 10 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/ApplicationReport.java

@@ -25,6 +25,7 @@ import org.apache.hadoop.classification.InterfaceStability.Unstable;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.util.Records;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -447,4 +448,13 @@ public abstract class ApplicationReport {
 
   @Unstable
   public abstract void setAmNodeLabelExpression(String amNodeLabelExpression);
+
+  @Public
+  @Unstable
+  public abstract List<ApplicationTimeout> getApplicationTimeouts();
+
+  @Private
+  @Unstable
+  public abstract void setApplicationTimeouts(
+      List<ApplicationTimeout> timeouts);
 }

+ 99 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/records/ApplicationTimeout.java

@@ -0,0 +1,99 @@
+/**
+ * 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;
+
+import org.apache.hadoop.classification.InterfaceAudience.Public;
+import org.apache.hadoop.classification.InterfaceStability.Unstable;
+import org.apache.hadoop.yarn.util.Records;
+
+/**
+ * {@code ApplicationTimeout} is a report for configured application timeouts.
+ * It includes details such as:
+ * <ul>
+ * <li>{@link ApplicationTimeoutType} of the timeout type.</li>
+ * <li>Expiry time in ISO8601 standard with format
+ * <b>yyyy-MM-dd'T'HH:mm:ss.SSSZ</b>.</li>
+ * <li>Remaining time in seconds.</li>
+ * </ul>
+ */
+@Public
+@Unstable
+public abstract class ApplicationTimeout {
+
+  @Public
+  @Unstable
+  public static ApplicationTimeout newInstance(ApplicationTimeoutType type,
+      String expiryTime, long remainingTime) {
+    ApplicationTimeout timeouts = Records.newRecord(ApplicationTimeout.class);
+    timeouts.setTimeoutType(type);
+    timeouts.setExpiryTime(expiryTime);
+    timeouts.setRemainingTime(remainingTime);
+    return timeouts;
+  }
+
+  /**
+   * Get the application timeout type.
+   * @return timeoutType of an application timeout.
+   */
+  @Public
+  @Unstable
+  public abstract ApplicationTimeoutType getTimeoutType();
+
+  /**
+   * Set the application timeout type.
+   * @param timeoutType of an application timeout.
+   */
+  @Public
+  @Unstable
+  public abstract void setTimeoutType(ApplicationTimeoutType timeoutType);
+
+  /**
+   * Get <code>expiryTime</code> for given timeout type.
+   * @return expiryTime in ISO8601 standard with format
+   *         <b>yyyy-MM-dd'T'HH:mm:ss.SSSZ</b>.
+   */
+  @Public
+  @Unstable
+  public abstract String getExpiryTime();
+
+  /**
+   * Set <code>expiryTime</code> for given timeout type.
+   * @param expiryTime in ISO8601 standard with format
+   *          <b>yyyy-MM-dd'T'HH:mm:ss.SSSZ</b>.
+   */
+  @Public
+  @Unstable
+  public abstract void setExpiryTime(String expiryTime);
+
+  /**
+   * Get <code>Remaining Time</code> of an application for given timeout type.
+   * @return Remaining Time in seconds.
+   */
+  @Public
+  @Unstable
+  public abstract long getRemainingTime();
+
+  /**
+   * Set <code>Remaining Time</code> of an application for given timeout type.
+   * @param remainingTime in seconds.
+   */
+  @Public
+  @Unstable
+  public abstract void setRemainingTime(long remainingTime);
+}

+ 7 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_protos.proto

@@ -214,6 +214,13 @@ message ApplicationReportProto {
   optional PriorityProto priority = 23;
   optional string appNodeLabelExpression = 24;
   optional string amNodeLabelExpression = 25;
+  repeated ApplicationTimeoutProto application_timeouts = 26;
+}
+
+message ApplicationTimeoutProto {
+  required ApplicationTimeoutTypeProto application_timeout_type = 1;
+  optional string expire_time = 2;
+  optional int64 remaining_time = 3;
 }
 
 enum LogAggregationStatusProto {

+ 11 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java

@@ -43,6 +43,8 @@ import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateResponse;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsResponse;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -844,4 +846,13 @@ public abstract class YarnClient extends AbstractService {
    */
   public abstract void signalToContainer(ContainerId containerId,
       SignalContainerCommand command) throws YarnException, IOException;
+
+  @Public
+  @Unstable
+  public UpdateApplicationTimeoutsResponse updateApplicationTimeouts(
+      UpdateApplicationTimeoutsRequest request)
+      throws YarnException, IOException {
+    throw new UnsupportedOperationException("The sub-class extending "
+        + YarnClient.class.getName() + " is expected to implement this !");
+  }
 }

+ 9 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java

@@ -84,6 +84,8 @@ import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.SignalContainerRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationPriorityRequest;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsResponse;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -917,4 +919,11 @@ public class YarnClientImpl extends YarnClient {
         SignalContainerRequest.newInstance(containerId, command);
     rmClient.signalToContainer(request);
   }
+
+  @Override
+  public UpdateApplicationTimeoutsResponse updateApplicationTimeouts(
+      UpdateApplicationTimeoutsRequest request)
+      throws YarnException, IOException {
+    return rmClient.updateApplicationTimeouts(request);
+  }
 }

+ 43 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java

@@ -24,6 +24,7 @@ import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.text.DecimalFormat;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
@@ -39,11 +40,14 @@ import org.apache.hadoop.classification.InterfaceAudience.Private;
 import org.apache.hadoop.classification.InterfaceStability.Unstable;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.ContainerReport;
 import org.apache.hadoop.yarn.api.records.Priority;
@@ -53,7 +57,6 @@ import org.apache.hadoop.yarn.exceptions.ApplicationAttemptNotFoundException;
 import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
 import org.apache.hadoop.yarn.exceptions.ContainerNotFoundException;
 import org.apache.hadoop.yarn.exceptions.YarnException;
-import org.apache.hadoop.yarn.util.ConverterUtils;
 import org.apache.hadoop.yarn.util.Times;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -84,6 +87,7 @@ public class ApplicationCLI extends YarnCLI {
   public static final String CONTAINER = "container";
   public static final String APP_ID = "appId";
   public static final String UPDATE_PRIORITY = "updatePriority";
+  public static final String UPDATE_LIFETIME = "updateLifetime";
 
   private boolean allAppStates;
 
@@ -139,6 +143,9 @@ public class ApplicationCLI extends YarnCLI {
       opts.addOption(UPDATE_PRIORITY, true,
           "update priority of an application. ApplicationId can be"
               + " passed using 'appId' option.");
+      opts.addOption(UPDATE_LIFETIME, true,
+          "update timeout of an application from NOW. ApplicationId can be"
+              + " passed using 'appId' option. Timeout value is in seconds.");
       Option killOpt = new Option(KILL_CMD, true, "Kills the application. "
           + "Set of applications can be provided separated with space");
       killOpt.setValueSeparator(' ');
@@ -150,6 +157,7 @@ public class ApplicationCLI extends YarnCLI {
       opts.getOption(STATUS_CMD).setArgName("Application ID");
       opts.getOption(APP_ID).setArgName("Application ID");
       opts.getOption(UPDATE_PRIORITY).setArgName("Priority");
+      opts.getOption(UPDATE_LIFETIME).setArgName("Timeout");
     } else if (args.length > 0 && args[0].equalsIgnoreCase(APPLICATION_ATTEMPT)) {
       title = APPLICATION_ATTEMPT;
       opts.addOption(STATUS_CMD, true,
@@ -296,6 +304,17 @@ public class ApplicationCLI extends YarnCLI {
       }
       updateApplicationPriority(cliParser.getOptionValue(APP_ID),
           cliParser.getOptionValue(UPDATE_PRIORITY));
+    } else if (cliParser.hasOption(UPDATE_LIFETIME)) {
+      if (!cliParser.hasOption(APP_ID)) {
+        printUsage(title, opts);
+        return exitCode;
+      }
+
+      long timeoutInSec =
+          Long.parseLong(cliParser.getOptionValue(UPDATE_LIFETIME));
+
+      updateApplicationTimeout(cliParser.getOptionValue(APP_ID),
+          ApplicationTimeoutType.LIFETIME, timeoutInSec);
     } else if (cliParser.hasOption(SIGNAL_CMD)) {
       if (args.length < 3 || args.length > 4) {
         printUsage(title, opts);
@@ -316,6 +335,22 @@ public class ApplicationCLI extends YarnCLI {
     return 0;
   }
 
+  private void updateApplicationTimeout(String applicationId,
+      ApplicationTimeoutType timeoutType, long timeoutInSec)
+      throws YarnException, IOException {
+    ApplicationId appId = ApplicationId.fromString(applicationId);
+    String newTimeout =
+        Times.formatISO8601(System.currentTimeMillis() + timeoutInSec * 1000);
+    sysout.println("Updating timeout for given timeoutType: "
+        + timeoutType.toString() + " of an application " + applicationId);
+    UpdateApplicationTimeoutsRequest request = UpdateApplicationTimeoutsRequest
+        .newInstance(appId, Collections.singletonMap(timeoutType, newTimeout));
+    client.updateApplicationTimeouts(request);
+    sysout.println(
+        "Successfully updated " + timeoutType.toString() + " of an application "
+            + applicationId + ". New expiry time is " + newTimeout);
+  }
+
   /**
    * Signals the containerId
    *
@@ -678,7 +713,13 @@ public class ApplicationCLI extends YarnCLI {
       appReportStr.print("\tApplication Node Label Expression : ");
       appReportStr.println(appReport.getAppNodeLabelExpression());
       appReportStr.print("\tAM container Node Label Expression : ");
-      appReportStr.print(appReport.getAmNodeLabelExpression());
+      appReportStr.println(appReport.getAmNodeLabelExpression());
+      for (ApplicationTimeout timeout : appReport.getApplicationTimeouts()) {
+        appReportStr.print("\tTimeoutType : " + timeout.getTimeoutType());
+        appReportStr.print("\tExpiryTime : " + timeout.getExpiryTime());
+        appReportStr.println(
+            "\tRemainingTime : " + timeout.getRemainingTime() + "seconds");
+      }
     } else {
       appReportStr.print("Application with id '" + applicationId
           + "' doesn't exist in RM.");

+ 40 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java

@@ -36,6 +36,7 @@ import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashSet;
@@ -46,11 +47,14 @@ import java.util.regex.Pattern;
 import org.apache.commons.cli.Options;
 import org.apache.commons.lang.time.DateFormatUtils;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.ContainerReport;
 import org.apache.hadoop.yarn.api.records.ContainerState;
@@ -124,6 +128,11 @@ public class TestYarnCLI {
           null, null, false, Priority.newInstance(0), "high-mem", "high-mem");
       newApplicationReport.setLogAggregationStatus(LogAggregationStatus.SUCCEEDED);
       newApplicationReport.setPriority(Priority.newInstance(0));
+      ApplicationTimeout timeout = ApplicationTimeout
+          .newInstance(ApplicationTimeoutType.LIFETIME, "UNLIMITED", -1);
+      newApplicationReport
+          .setApplicationTimeouts(Collections.singletonList(timeout));
+
       when(client.getApplicationReport(any(ApplicationId.class))).thenReturn(
           newApplicationReport);
       int result = cli.run(new String[] { "application", "-status", applicationId.toString() });
@@ -155,6 +164,10 @@ public class TestYarnCLI {
       pw.println("\tUnmanaged Application : false");
       pw.println("\tApplication Node Label Expression : high-mem");
       pw.println("\tAM container Node Label Expression : high-mem");
+      pw.print("\tTimeoutType : LIFETIME");
+      pw.print("\tExpiryTime : UNLIMITED");
+      pw.println("\tRemainingTime : -1seconds");
+      pw.println();
       pw.close();
       String appReportStr = baos.toString("UTF-8");
       Assert.assertEquals(appReportStr, sysOutStream.toString());
@@ -1984,6 +1997,10 @@ public class TestYarnCLI {
     pw.println("                                 specify which queue to move an");
     pw.println("                                 application to.");
     pw.println(" -status <Application ID>        Prints the status of the application.");
+    pw.println(" -updateLifetime <Timeout>       update timeout of an application from");
+    pw.println("                                 NOW. ApplicationId can be passed using");
+    pw.println("                                 'appId' option. Timeout value is in");
+    pw.println("                                 seconds.");
     pw.println(" -updatePriority <Priority>      update priority of an application.");
     pw.println("                                 ApplicationId can be passed using 'appId'");
     pw.println("                                 option.");
@@ -2074,4 +2091,27 @@ public class TestYarnCLI {
             applicationId.toString() });
     assertEquals(0, result);
   }
+
+  @Test(timeout = 60000)
+  public void testUpdateApplicationTimeout() throws Exception {
+    ApplicationCLI cli = createAndGetAppCLI();
+    ApplicationId applicationId = ApplicationId.newInstance(1234, 6);
+
+    ApplicationReport appReport = ApplicationReport.newInstance(applicationId,
+        ApplicationAttemptId.newInstance(applicationId, 1), "user", "queue",
+        "appname", "host", 124, null, YarnApplicationState.RUNNING,
+        "diagnostics", "url", 0, 0, FinalApplicationStatus.UNDEFINED, null,
+        "N/A", 0.53789f, "YARN", null);
+    ApplicationTimeout timeout = ApplicationTimeout
+        .newInstance(ApplicationTimeoutType.LIFETIME, "N/A", -1);
+    appReport.setApplicationTimeouts(Collections.singletonList(timeout));
+    when(client.getApplicationReport(any(ApplicationId.class)))
+        .thenReturn(appReport);
+
+    int result = cli.run(new String[] { "application", "-appId",
+        applicationId.toString(), "-updateLifetime", "10" });
+    Assert.assertEquals(result, 0);
+    verify(client)
+        .updateApplicationTimeouts(any(UpdateApplicationTimeoutsRequest.class));
+  }
 }

+ 83 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ApplicationReportPBImpl.java

@@ -25,6 +25,7 @@ import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
 import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
 import org.apache.hadoop.yarn.api.records.LogAggregationStatus;
 import org.apache.hadoop.yarn.api.records.Priority;
@@ -35,6 +36,7 @@ import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationIdProto;
 import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationReportProto;
 import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationReportProtoOrBuilder;
 import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationResourceUsageReportProto;
+import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationTimeoutProto;
 import org.apache.hadoop.yarn.proto.YarnProtos.FinalApplicationStatusProto;
 import org.apache.hadoop.yarn.proto.YarnProtos.LogAggregationStatusProto;
 import org.apache.hadoop.yarn.proto.YarnProtos.PriorityProto;
@@ -42,7 +44,10 @@ import org.apache.hadoop.yarn.proto.YarnProtos.YarnApplicationStateProto;
 
 import com.google.protobuf.TextFormat;
 
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 @Private
@@ -58,6 +63,7 @@ public class ApplicationReportPBImpl extends ApplicationReport {
   private Token amRmToken = null;
   private Set<String> applicationTags = null;
   private Priority priority = null;
+  private List<ApplicationTimeout> applicationTimeoutList = null;
 
   public ApplicationReportPBImpl() {
     builder = ApplicationReportProto.newBuilder();
@@ -492,6 +498,9 @@ public class ApplicationReportPBImpl extends ApplicationReport {
             builder.getPriority())) {
       builder.setPriority(convertToProtoFormat(this.priority));
     }
+    if (this.applicationTimeoutList != null) {
+      addLocalApplicationTimeoutToProto();
+    }
   }
 
   private void mergeLocalToProto() {
@@ -668,4 +677,78 @@ public class ApplicationReportPBImpl extends ApplicationReport {
     }
     builder.setAmNodeLabelExpression((amNodeLabelExpression));
   }
+
+  @Override
+  public List<ApplicationTimeout> getApplicationTimeouts() {
+    initLocalApplicationsList();
+    return this.applicationTimeoutList;
+  }
+
+  private void initLocalApplicationsList() {
+    if (this.applicationTimeoutList != null) {
+      return;
+    }
+    ApplicationReportProtoOrBuilder p = viaProto ? proto : builder;
+    List<ApplicationTimeoutProto> list = p.getApplicationTimeoutsList();
+    this.applicationTimeoutList = new ArrayList<ApplicationTimeout>();
+
+    for (ApplicationTimeoutProto a : list) {
+      this.applicationTimeoutList.add(convertFromProtoFormat(a));
+    }
+  }
+
+  private void addLocalApplicationTimeoutToProto() {
+    maybeInitBuilder();
+    builder.clearApplicationTimeouts();
+    if (applicationTimeoutList == null) {
+      return;
+    }
+    Iterable<ApplicationTimeoutProto> iterable =
+        new Iterable<ApplicationTimeoutProto>() {
+          @Override
+          public Iterator<ApplicationTimeoutProto> iterator() {
+            return new Iterator<ApplicationTimeoutProto>() {
+
+              private Iterator<ApplicationTimeout> iter =
+                  applicationTimeoutList.iterator();
+
+              @Override
+              public boolean hasNext() {
+                return iter.hasNext();
+              }
+
+              @Override
+              public ApplicationTimeoutProto next() {
+                return convertToProtoFormat(iter.next());
+              }
+
+              @Override
+              public void remove() {
+                throw new UnsupportedOperationException();
+
+              }
+            };
+
+          }
+        };
+    builder.addAllApplicationTimeouts(iterable);
+  }
+
+  private ApplicationTimeoutPBImpl convertFromProtoFormat(
+      ApplicationTimeoutProto p) {
+    return new ApplicationTimeoutPBImpl(p);
+  }
+
+  private ApplicationTimeoutProto convertToProtoFormat(ApplicationTimeout t) {
+    return ((ApplicationTimeoutPBImpl) t).getProto();
+  }
+
+  @Override
+  public void setApplicationTimeouts(List<ApplicationTimeout> timeouts) {
+    maybeInitBuilder();
+    if (timeouts == null) {
+      builder.clearApplicationTimeouts();
+    }
+    this.applicationTimeoutList = timeouts;
+  }
 }

+ 130 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/ApplicationTimeoutPBImpl.java

@@ -0,0 +1,130 @@
+/**
+ * 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.impl.pb;
+
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
+import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationTimeoutProto;
+import org.apache.hadoop.yarn.proto.YarnProtos.ApplicationTimeoutProtoOrBuilder;
+
+import com.google.protobuf.TextFormat;
+
+/**
+ * PB implementation for ApplicationTimeout class.
+ */
+public class ApplicationTimeoutPBImpl extends ApplicationTimeout {
+
+  private ApplicationTimeoutProto proto =
+      ApplicationTimeoutProto.getDefaultInstance();
+  private ApplicationTimeoutProto.Builder builder = null;
+  private boolean viaProto = false;
+
+  public ApplicationTimeoutPBImpl() {
+    builder = ApplicationTimeoutProto.newBuilder();
+  }
+
+  public ApplicationTimeoutPBImpl(ApplicationTimeoutProto proto) {
+    this.proto = proto;
+    viaProto = true;
+  }
+
+  public ApplicationTimeoutProto getProto() {
+    proto = viaProto ? proto : builder.build();
+    viaProto = true;
+    return proto;
+  }
+
+  private void maybeInitBuilder() {
+    if (viaProto || builder == null) {
+      builder = ApplicationTimeoutProto.newBuilder(proto);
+    }
+    viaProto = false;
+  }
+
+  @Override
+  public ApplicationTimeoutType getTimeoutType() {
+    ApplicationTimeoutProtoOrBuilder p = viaProto ? proto : builder;
+    if (!p.hasApplicationTimeoutType()) {
+      return null;
+    }
+    return ProtoUtils.convertFromProtoFormat(p.getApplicationTimeoutType());
+  }
+
+  @Override
+  public void setTimeoutType(ApplicationTimeoutType type) {
+    maybeInitBuilder();
+    if (type == null) {
+      builder.clearApplicationTimeoutType();
+      return;
+    }
+    builder.setApplicationTimeoutType(ProtoUtils.convertToProtoFormat(type));
+  }
+
+  @Override
+  public String getExpiryTime() {
+    ApplicationTimeoutProtoOrBuilder p = viaProto ? proto : builder;
+    if (!p.hasExpireTime()) {
+      return null;
+    }
+    return p.getExpireTime();
+  }
+
+  @Override
+  public void setExpiryTime(String expiryTime) {
+    maybeInitBuilder();
+    if (expiryTime == null) {
+      builder.clearExpireTime();
+      return;
+    }
+    builder.setExpireTime(expiryTime);
+  }
+
+  @Override
+  public long getRemainingTime() {
+    ApplicationTimeoutProtoOrBuilder p = viaProto ? proto : builder;
+    return p.getRemainingTime();
+  }
+
+  @Override
+  public void setRemainingTime(long remainingTime) {
+    maybeInitBuilder();
+    builder.setRemainingTime(remainingTime);
+  }
+
+  @Override
+  public int hashCode() {
+    return getProto().hashCode();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == null) {
+      return false;
+    }
+    if (other.getClass().isAssignableFrom(this.getClass())) {
+      return this.getProto().equals(this.getClass().cast(other).getProto());
+    }
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return TextFormat.shortDebugString(getProto());
+  }
+}

+ 2 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/api/TestPBImplRecords.java

@@ -104,6 +104,7 @@ import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
 import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
 import org.apache.hadoop.yarn.api.records.Container;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
@@ -397,6 +398,7 @@ public class TestPBImplRecords extends BasePBImplRecordsTest {
     generateByNewInstance(RestartContainerResponse.class);
     generateByNewInstance(RollbackResponse.class);
     generateByNewInstance(CommitResponse.class);
+    generateByNewInstance(ApplicationTimeout.class);
   }
 
   @Test

+ 17 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java

@@ -56,6 +56,7 @@ import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
 import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
 import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
 import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
 import org.apache.hadoop.yarn.api.records.LogAggregationStatus;
@@ -111,6 +112,7 @@ import org.apache.hadoop.yarn.state.StateMachine;
 import org.apache.hadoop.yarn.state.StateMachineFactory;
 import org.apache.hadoop.yarn.util.Clock;
 import org.apache.hadoop.yarn.util.SystemClock;
+import org.apache.hadoop.yarn.util.Times;
 import org.apache.hadoop.yarn.util.resource.Resources;
 import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
 
@@ -121,6 +123,8 @@ public class RMAppImpl implements RMApp, Recoverable {
 
   private static final Log LOG = LogFactory.getLog(RMAppImpl.class);
   private static final String UNAVAILABLE = "N/A";
+  private static final String UNLIMITED = "UNLIMITED";
+  private static final long UNKNOWN = -1L;
   private static final EnumSet<RMAppState> COMPLETED_APP_STATES =
       EnumSet.of(RMAppState.FINISHED, RMAppState.FINISHING, RMAppState.FAILED,
           RMAppState.KILLED, RMAppState.FINAL_SAVING, RMAppState.KILLING);
@@ -720,6 +724,19 @@ public class RMAppImpl implements RMApp, Recoverable {
       report.setUnmanagedApp(submissionContext.getUnmanagedAM());
       report.setAppNodeLabelExpression(getAppNodeLabelExpression());
       report.setAmNodeLabelExpression(getAmNodeLabelExpression());
+
+      ApplicationTimeout timeout = ApplicationTimeout
+          .newInstance(ApplicationTimeoutType.LIFETIME, UNLIMITED, UNKNOWN);
+      // Currently timeout type supported is LIFETIME. When more timeout types
+      // are supported in YARN-5692, the below logic need to be changed.
+      if (!this.applicationTimeouts.isEmpty()) {
+        long timeoutInMillis = applicationTimeouts
+            .get(ApplicationTimeoutType.LIFETIME).longValue();
+        timeout.setExpiryTime(Times.formatISO8601(timeoutInMillis));
+        timeout.setRemainingTime(
+            Math.max((timeoutInMillis - systemClock.getTime()) / 1000, 0));
+      }
+      report.setApplicationTimeouts(Collections.singletonList(timeout));
       return report;
     } finally {
       this.readLock.unlock();

+ 28 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestApplicationLifetimeMonitor.java

@@ -24,18 +24,23 @@ import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.api.records.ApplicationTimeout;
 import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.ContainerState;
 import org.apache.hadoop.yarn.api.records.Priority;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnException;
+import org.apache.hadoop.yarn.factories.RecordFactory;
+import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider;
 import org.apache.hadoop.yarn.server.api.protocolrecords.NMContainerStatus;
 import org.apache.hadoop.yarn.server.resourcemanager.MockAM;
 import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
@@ -101,8 +106,9 @@ public class TestApplicationLifetimeMonitor {
           new HashMap<ApplicationTimeoutType, String>();
       long newLifetime = 10L;
       // update 10L seconds more to timeout
-      updateTimeout.put(ApplicationTimeoutType.LIFETIME,
-          Times.formatISO8601(System.currentTimeMillis() + newLifetime * 1000));
+      String formatISO8601 =
+          Times.formatISO8601(System.currentTimeMillis() + newLifetime * 1000);
+      updateTimeout.put(ApplicationTimeoutType.LIFETIME, formatISO8601);
       UpdateApplicationTimeoutsRequest request =
           UpdateApplicationTimeoutsRequest.newInstance(app2.getApplicationId(),
               updateTimeout);
@@ -124,6 +130,26 @@ public class TestApplicationLifetimeMonitor {
       Assert.assertTrue("Application lifetime value not updated",
           afterUpdate > beforeUpdate);
 
+      // verify for application report.
+      RecordFactory recordFactory =
+          RecordFactoryProvider.getRecordFactory(null);
+      GetApplicationReportRequest appRequest =
+          recordFactory.newRecordInstance(GetApplicationReportRequest.class);
+      appRequest.setApplicationId(app2.getApplicationId());
+      List<ApplicationTimeout> applicationTimeoutList = rm.getRMContext()
+          .getClientRMService().getApplicationReport(appRequest)
+          .getApplicationReport().getApplicationTimeouts();
+      Assert.assertTrue("Application Timeout list are empty.",
+          !applicationTimeoutList.isEmpty());
+      ApplicationTimeout timeout = applicationTimeoutList.iterator().next();
+      Assert.assertEquals("Application timeout Type is incorrect.",
+          ApplicationTimeoutType.LIFETIME.toString(),
+          timeout.getTimeoutType().toString());
+      Assert.assertEquals("Application timeout string is incorrect.",
+          formatISO8601, timeout.getExpiryTime());
+      Assert.assertTrue("Application remaining time is incorrect",
+          timeout.getRemainingTime() > 0);
+
       rm.waitForState(app2.getApplicationId(), RMAppState.KILLED);
       // verify for app killed with updated lifetime
       Assert.assertTrue("Application killed before lifetime value",