You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by ma...@apache.org on 2014/07/22 00:07:24 UTC

git commit: Adding getPendingReason RPC to expose scheduling vetos in the UI/client.

Repository: incubator-aurora
Updated Branches:
  refs/heads/master 3af67c023 -> b625cfa8e


Adding getPendingReason RPC to expose scheduling vetos in the UI/client.

Bugs closed: AURORA-377

Reviewed at https://reviews.apache.org/r/23188/


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

Branch: refs/heads/master
Commit: b625cfa8ef0e6474b091e11c5417d85686e959bf
Parents: 3af67c0
Author: Maxim Khutornenko <ma...@apache.org>
Authored: Mon Jul 21 15:07:12 2014 -0700
Committer: Maxim Khutornenko <ma...@apache.org>
Committed: Mon Jul 21 15:07:12 2014 -0700

----------------------------------------------------------------------
 .../thrift/SchedulerThriftInterface.java        | 56 ++++++++++++++++++--
 .../thrift/org/apache/aurora/gen/api.thrift     | 13 +++++
 .../thrift/SchedulerThriftInterfaceTest.java    | 54 ++++++++++++++++++-
 .../scheduler/thrift/aop/ForwardingThrift.java  |  5 ++
 4 files changed, 124 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b625cfa8/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java b/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
index 2549dd3..95ee7b7 100644
--- a/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
@@ -27,6 +27,7 @@ import javax.inject.Inject;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
+import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
@@ -58,6 +59,7 @@ import org.apache.aurora.gen.DrainHostsResult;
 import org.apache.aurora.gen.EndMaintenanceResult;
 import org.apache.aurora.gen.GetJobsResult;
 import org.apache.aurora.gen.GetLocksResult;
+import org.apache.aurora.gen.GetPendingReasonResult;
 import org.apache.aurora.gen.GetQuotaResult;
 import org.apache.aurora.gen.Hosts;
 import org.apache.aurora.gen.InstanceConfigRewrite;
@@ -73,6 +75,7 @@ import org.apache.aurora.gen.LockKey;
 import org.apache.aurora.gen.LockKey._Fields;
 import org.apache.aurora.gen.LockValidation;
 import org.apache.aurora.gen.MaintenanceStatusResult;
+import org.apache.aurora.gen.PendingReason;
 import org.apache.aurora.gen.PopulateJobResult;
 import org.apache.aurora.gen.QueryRecoveryResult;
 import org.apache.aurora.gen.ResourceAggregate;
@@ -101,6 +104,8 @@ import org.apache.aurora.scheduler.cron.CronJobManager;
 import org.apache.aurora.scheduler.cron.CronPredictor;
 import org.apache.aurora.scheduler.cron.CrontabEntry;
 import org.apache.aurora.scheduler.cron.SanitizedCronJob;
+import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto;
+import org.apache.aurora.scheduler.metadata.NearestFit;
 import org.apache.aurora.scheduler.quota.QuotaInfo;
 import org.apache.aurora.scheduler.quota.QuotaManager;
 import org.apache.aurora.scheduler.quota.QuotaManager.QuotaException;
@@ -173,6 +178,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
   private final CronJobManager cronJobManager;
   private final CronPredictor cronPredictor;
   private final QuotaManager quotaManager;
+  private final NearestFit nearestFit;
 
   @Inject
   SchedulerThriftInterface(
@@ -185,7 +191,8 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       CronJobManager cronJobManager,
       CronPredictor cronPredictor,
       MaintenanceController maintenance,
-      QuotaManager quotaManager) {
+      QuotaManager quotaManager,
+      NearestFit nearestFit) {
 
     this(storage,
         schedulerCore,
@@ -196,7 +203,8 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
         maintenance,
         cronJobManager,
         cronPredictor,
-        quotaManager);
+        quotaManager,
+        nearestFit);
   }
 
   @VisibleForTesting
@@ -210,7 +218,8 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       MaintenanceController maintenance,
       CronJobManager cronJobManager,
       CronPredictor cronPredictor,
-      QuotaManager quotaManager) {
+      QuotaManager quotaManager,
+      NearestFit nearestFit) {
 
     this.storage = requireNonNull(storage);
     this.schedulerCore = requireNonNull(schedulerCore);
@@ -222,6 +231,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     this.cronJobManager = requireNonNull(cronJobManager);
     this.cronPredictor = requireNonNull(cronPredictor);
     this.quotaManager = requireNonNull(quotaManager);
+    this.nearestFit = requireNonNull(nearestFit);
   }
 
   @Override
@@ -461,6 +471,46 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
   };
 
   @Override
+  public Response getPendingReason(TaskQuery query) throws TException {
+    requireNonNull(query);
+
+    Response response = Util.emptyResponse();
+    if (query.isSetSlaveHosts() || query.isSetStatuses()) {
+      return addMessage(
+          response,
+          INVALID_REQUEST,
+          "Statuses or slaveHosts are not supported in " + query.toString());
+    }
+
+    // Only PENDING tasks should be considered.
+    query.setStatuses(ImmutableSet.of(ScheduleStatus.PENDING));
+
+    Set<PendingReason> reasons = FluentIterable.from(getTasks(query))
+        .transform(new Function<ScheduledTask, PendingReason>() {
+          @Override
+          public PendingReason apply(ScheduledTask scheduledTask) {
+            String taskId = scheduledTask.getAssignedTask().getTaskId();
+            String reason = Joiner.on(',').join(Iterables.transform(
+                nearestFit.getNearestFit(taskId),
+                new Function<Veto, String>() {
+                  @Override
+                  public String apply(Veto veto) {
+                    return veto.getReason();
+                  }
+                }));
+
+            return new PendingReason()
+                .setTaskId(taskId)
+                .setReason(reason);
+          }
+        }).toSet();
+
+    return response
+        .setResponseCode(OK)
+        .setResult(Result.getPendingReasonResult(new GetPendingReasonResult(reasons)));
+  }
+
+  @Override
   public Response getConfigSummary(JobKey job) throws TException {
     IJobKey jobKey = JobKeys.assertValid(IJobKey.build(job));
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b625cfa8/src/main/thrift/org/apache/aurora/gen/api.thrift
----------------------------------------------------------------------
diff --git a/src/main/thrift/org/apache/aurora/gen/api.thrift b/src/main/thrift/org/apache/aurora/gen/api.thrift
index 8ee43fa..978525e 100644
--- a/src/main/thrift/org/apache/aurora/gen/api.thrift
+++ b/src/main/thrift/org/apache/aurora/gen/api.thrift
@@ -410,6 +410,11 @@ struct Hosts {
   1: set<string> hostNames
 }
 
+struct PendingReason {
+  1: string taskId
+  2: string reason
+}
+
 struct ListBackupsResult {
   1: set<string> backups
 }
@@ -451,6 +456,10 @@ struct ConfigSummaryResult {
   1: ConfigSummary summary
 }
 
+struct GetPendingReasonResult {
+  1: set<PendingReason> reasons
+}
+
 // meta-data about the thrift server that is wrapped around every thrift response
 struct ServerInfo {
   1: string clusterName
@@ -475,6 +484,7 @@ union Result {
   18: JobSummaryResult jobSummaryResult
   19: GetLocksResult getLocksResult
   20: ConfigSummaryResult configSummaryResult
+  21: GetPendingReasonResult getPendingReasonResult
 
 }
 
@@ -508,6 +518,9 @@ service ReadOnlyScheduler {
   // This is an interim solution until we have a better way to query TaskConfigs (AURORA-541).
   Response getTasksWithoutConfigs(1: TaskQuery query)
 
+  // Returns user-friendly reasons (if available) for tasks retained in PENDING state.
+  Response getPendingReason(1: TaskQuery query)
+
   // Fetches the configuration summary of active tasks for the specified job.
   Response getConfigSummary(1: JobKey job)
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b625cfa8/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java b/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
index 2cffa74..b28761d 100644
--- a/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
@@ -67,6 +67,7 @@ import org.apache.aurora.gen.JobSummaryResult;
 import org.apache.aurora.gen.LimitConstraint;
 import org.apache.aurora.gen.Lock;
 import org.apache.aurora.gen.LockKey;
+import org.apache.aurora.gen.PendingReason;
 import org.apache.aurora.gen.ResourceAggregate;
 import org.apache.aurora.gen.Response;
 import org.apache.aurora.gen.ResponseCode;
@@ -94,6 +95,8 @@ import org.apache.aurora.scheduler.cron.CronJobManager;
 import org.apache.aurora.scheduler.cron.CronPredictor;
 import org.apache.aurora.scheduler.cron.CrontabEntry;
 import org.apache.aurora.scheduler.cron.SanitizedCronJob;
+import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto;
+import org.apache.aurora.scheduler.metadata.NearestFit;
 import org.apache.aurora.scheduler.quota.QuotaInfo;
 import org.apache.aurora.scheduler.quota.QuotaManager;
 import org.apache.aurora.scheduler.state.LockManager;
@@ -182,6 +185,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
   private CronJobManager cronJobManager;
   private CronPredictor cronPredictor;
   private QuotaManager quotaManager;
+  private NearestFit nearestFit;
 
   @Before
   public void setUp() throws Exception {
@@ -198,6 +202,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
     cronJobManager = createMock(CronJobManager.class);
     cronPredictor = createMock(CronPredictor.class);
     quotaManager = createMock(QuotaManager.class);
+    nearestFit = createMock(NearestFit.class);
 
     // Use guice and install AuthModule to apply AOP-style auth layer.
     Module testModule = new AbstractModule() {
@@ -216,6 +221,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
         bind(AuroraAdmin.Iface.class).to(SchedulerThriftInterface.class);
         bind(IServerInfo.class).toInstance(IServerInfo.build(SERVER_INFO));
         bind(CronPredictor.class).toInstance(cronPredictor);
+        bind(NearestFit.class).toInstance(nearestFit);
       }
     };
     Injector injector = Guice.createInjector(testModule, new AopModule());
@@ -1318,6 +1324,52 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
   }
 
   @Test
+  public void testGetPendingReason() throws Exception {
+    Builder query = Query.unscoped().byJob(JOB_KEY);
+    Builder filterQuery = Query.unscoped().byJob(JOB_KEY).byStatus(ScheduleStatus.PENDING);
+    String taskId = "task_id_test";
+    ImmutableSet<Veto> result = ImmutableSet.of(
+        Veto.constraintMismatch("first"),
+        Veto.constraintMismatch("second"));
+
+    IScheduledTask pendingTask = IScheduledTask.build(new ScheduledTask()
+        .setAssignedTask(new AssignedTask()
+            .setTask(defaultTask(true))
+            .setTaskId(taskId))
+        .setStatus(ScheduleStatus.PENDING));
+
+    storageUtil.expectTaskFetch(filterQuery, pendingTask);
+    expect(nearestFit.getNearestFit(taskId)).andReturn(result);
+
+    control.replay();
+
+    Set<PendingReason> expected = ImmutableSet.of(new PendingReason()
+        .setTaskId(taskId)
+        .setReason("first,second"));
+
+    Response response = assertOkResponse(thrift.getPendingReason(query.get()));
+    assertEquals(expected, response.getResult().getGetPendingReasonResult().getReasons());
+  }
+
+  @Test
+  public void testGetPendingReasonFailsStatusSet() throws Exception {
+    Builder query = Query.unscoped().byStatus(ScheduleStatus.ASSIGNED);
+
+    control.replay();
+
+    assertResponse(INVALID_REQUEST, thrift.getPendingReason(query.get()));
+  }
+
+  @Test
+  public void testGetPendingReasonFailsSlavesSet() throws Exception {
+    Builder query = Query.unscoped().bySlave("host1");
+
+    control.replay();
+
+    assertResponse(INVALID_REQUEST, thrift.getPendingReason(query.get()));
+  }
+
+  @Test
   public void testGetConfigSummary() throws Exception {
     IJobKey key = JobKeys.from("test", "test", "test");
 
@@ -1746,7 +1798,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
 
   private static TaskConfig defaultTask(boolean production) {
     return new TaskConfig()
-        .setOwner(new Identity("role", "user"))
+        .setOwner(new Identity(ROLE, USER))
         .setEnvironment(DEFAULT_ENVIRONMENT)
         .setJobName(JOB_NAME)
         .setContactEmail("testing@twitter.com")

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b625cfa8/src/test/java/org/apache/aurora/scheduler/thrift/aop/ForwardingThrift.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/thrift/aop/ForwardingThrift.java b/src/test/java/org/apache/aurora/scheduler/thrift/aop/ForwardingThrift.java
index ed24ca0..2ea4a9b 100644
--- a/src/test/java/org/apache/aurora/scheduler/thrift/aop/ForwardingThrift.java
+++ b/src/test/java/org/apache/aurora/scheduler/thrift/aop/ForwardingThrift.java
@@ -257,4 +257,9 @@ abstract class ForwardingThrift implements AuroraAdmin.Iface {
 
     return delegate.addInstances(config, lock, session);
   }
+
+  @Override
+  public Response getPendingReason(TaskQuery query) throws TException {
+    return delegate.getPendingReason(query);
+  }
 }