You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by wf...@apache.org on 2014/05/28 01:14:29 UTC

git commit: Add support for multiple API response messages, only apply informative messages.

Repository: incubator-aurora
Updated Branches:
  refs/heads/master a8fa267f0 -> 34173d1cd


Add support for multiple API response messages, only apply informative messages.

Bugs closed: AURORA-461

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


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

Branch: refs/heads/master
Commit: 34173d1cd8e059d18a46ef90fce6bc6d80dfa4d5
Parents: a8fa267
Author: Bill Farner <wf...@apache.org>
Authored: Tue May 27 16:00:53 2014 -0700
Committer: Bill Farner <wf...@apache.org>
Committed: Tue May 27 16:00:53 2014 -0700

----------------------------------------------------------------------
 .../thrift/SchedulerThriftInterface.java        | 201 +++++++++----------
 .../apache/aurora/scheduler/thrift/Util.java    |  70 +++++++
 .../thrift/aop/FeatureToggleInterceptor.java    |   5 +-
 .../scheduler/thrift/aop/Interceptors.java      |  69 -------
 .../thrift/aop/LoggingInterceptor.java          |   9 +-
 .../thrift/aop/UserCapabilityInterceptor.java   |   5 +-
 .../python/apache/aurora/client/api/__init__.py |   2 +-
 .../apache/aurora/client/api/command_runner.py  |   4 +-
 .../aurora/client/api/instance_watcher.py       |   2 +-
 .../apache/aurora/client/api/quota_check.py     |   8 +-
 .../apache/aurora/client/api/restarter.py       |   2 +-
 .../python/apache/aurora/client/api/updater.py  |   6 +-
 src/main/python/apache/aurora/client/base.py    |   2 +-
 .../python/apache/aurora/client/cli/context.py  |   8 +-
 .../python/apache/aurora/client/cli/jobs.py     |  12 +-
 .../python/apache/aurora/client/cli/task.py     |   3 +-
 .../apache/aurora/client/commands/admin.py      |   2 +-
 .../apache/aurora/client/commands/core.py       |  10 +-
 .../apache/aurora/client/hooks/hooked_api.py    |   2 +-
 .../thrift/org/apache/aurora/gen/api.thrift     |  11 +-
 .../thrift/SchedulerThriftInterfaceTest.java    |  43 +++-
 .../aurora/client/api/test_disambiguator.py     |   2 +-
 .../aurora/client/api/test_instance_watcher.py  |   4 +-
 .../aurora/client/api/test_job_monitor.py       |   2 +-
 .../aurora/client/api/test_quota_check.py       |   2 +-
 .../apache/aurora/client/api/test_restarter.py  |  10 +-
 .../python/apache/aurora/client/api/test_sla.py |   2 +-
 .../apache/aurora/client/api/test_updater.py    |  18 +-
 .../python/apache/aurora/client/cli/util.py     |   2 +-
 .../apache/aurora/client/commands/test_kill.py  |   2 +-
 .../apache/aurora/client/commands/util.py       |   2 +-
 31 files changed, 280 insertions(+), 242 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/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 29f64f7..9b7f6c3 100644
--- a/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterface.java
@@ -26,7 +26,6 @@ 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;
@@ -74,7 +73,6 @@ import org.apache.aurora.gen.PopulateJobResult;
 import org.apache.aurora.gen.QueryRecoveryResult;
 import org.apache.aurora.gen.ResourceAggregate;
 import org.apache.aurora.gen.Response;
-import org.apache.aurora.gen.ResponseCode;
 import org.apache.aurora.gen.Result;
 import org.apache.aurora.gen.RewriteConfigsRequest;
 import org.apache.aurora.gen.RoleSummary;
@@ -134,7 +132,10 @@ import static org.apache.aurora.gen.ResponseCode.ERROR;
 import static org.apache.aurora.gen.ResponseCode.INVALID_REQUEST;
 import static org.apache.aurora.gen.ResponseCode.LOCK_ERROR;
 import static org.apache.aurora.gen.ResponseCode.OK;
+import static org.apache.aurora.gen.ResponseCode.WARNING;
 import static org.apache.aurora.gen.apiConstants.CURRENT_API_VERSION;
+import static org.apache.aurora.scheduler.thrift.Util.addMessage;
+import static org.apache.aurora.scheduler.thrift.Util.emptyResponse;
 
 /**
  * Aurora scheduler thrift server implementation.
@@ -226,12 +227,12 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     IJobKey jobKey = JobKeys.assertValid(job.getKey());
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
 
     try {
       sessionValidator.checkAuthenticated(session, ImmutableSet.of(job.getOwner().getRole()));
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(response.setResponseCode(AUTH_FAILED), e.getMessage());
     }
 
     try {
@@ -242,13 +243,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           Optional.fromNullable(mutableLock).transform(ILock.FROM_BUILDER));
 
       schedulerCore.createJob(sanitized);
-      response.setResponseCode(OK)
-          .setMessage(String.format("%d new tasks pending for job %s",
-              sanitized.getJobConfig().getInstanceCount(), JobKeys.canonicalString(job.getKey())));
+      response.setResponseCode(OK);
     } catch (LockException e) {
-      response.setResponseCode(LOCK_ERROR).setMessage(e.getMessage());
+      addMessage(response, LOCK_ERROR, e.getMessage());
     } catch (TaskDescriptionException | ScheduleException e) {
-      response.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+      addMessage(response, INVALID_REQUEST, e.getMessage());
     }
     return response;
   }
@@ -263,12 +262,13 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     IJobKey jobKey = JobKeys.assertValid(job.getKey());
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
 
     try {
       sessionValidator.checkAuthenticated(session, ImmutableSet.of(job.getOwner().getRole()));
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      response.setResponseCode(AUTH_FAILED);
+      return addMessage(response, e.getMessage());
     }
 
     try {
@@ -282,10 +282,10 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
         LOG.info("Invalid attempt to schedule non-cron job "
             + sanitized.getJobConfig().getKey()
             + " with cron.");
-        response.setResponseCode(INVALID_REQUEST)
-            .setMessage("Job " + sanitized.getJobConfig().getKey()
-            + " has no cron schedule");
-        return response;
+        response.setResponseCode(INVALID_REQUEST);
+        return addMessage(
+            response,
+            "Job " + sanitized.getJobConfig().getKey() + " has no cron schedule");
       }
       try {
         // TODO(mchucarroll): Merge CronJobManager.createJob/updateJob
@@ -296,16 +296,14 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           cronJobManager.createJob(SanitizedCronJob.from(sanitized));
         }
       } catch (CronException e) {
-        response.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+        addMessage(response, INVALID_REQUEST, e.getMessage());
         return response;
       }
-      response.setResponseCode(OK)
-          .setMessage(String.format("Job %s scheduled with cron schedule '%s",
-               sanitized.getJobConfig().getKey(), sanitized.getJobConfig().getCronSchedule()));
+      response.setResponseCode(OK);
     } catch (LockException e) {
-      response.setResponseCode(LOCK_ERROR).setMessage(e.getMessage());
+      addMessage(response, LOCK_ERROR, e.getMessage());
     } catch (TaskDescriptionException e) {
-      response.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+      addMessage(response, INVALID_REQUEST, e.getMessage());
     }
     return response;
   }
@@ -316,7 +314,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       @Nullable Lock mutableLock,
       SessionKey session) {
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       IJobKey jobKey = JobKeys.assertValid(IJobKey.build(mutableJobKey));
       lockManager.validateIfLocked(
@@ -324,14 +322,14 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           Optional.fromNullable(mutableLock).transform(ILock.FROM_BUILDER));
 
       if (!cronJobManager.deleteJob(jobKey)) {
-        response.setResponseCode(INVALID_REQUEST)
-            .setMessage("Job " + jobKey + " is not scheduled with cron");
-        return response;
+        return addMessage(
+            response,
+            INVALID_REQUEST,
+            "Job " + jobKey + " is not scheduled with cron");
       }
-      response.setResponseCode(OK)
-          .setMessage(String.format("Job %s removed from cron schedule", jobKey));
+      response.setResponseCode(OK);
     } catch (LockException e) {
-      response.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+      addMessage(response, LOCK_ERROR, e.getMessage());
     }
     return response;
   }
@@ -347,11 +345,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     IJobKey jobKey = JobKeys.assertValid(job.getKey());
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       sessionValidator.checkAuthenticated(session, ImmutableSet.of(job.getOwner().getRole()));
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(response, AUTH_FAILED, e.getMessage());
     }
 
     try {
@@ -360,11 +358,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           Optional.fromNullable(mutableLock).transform(ILock.FROM_BUILDER));
 
       cronJobManager.updateJob(SanitizedCronJob.fromUnsanitized(job));
-      return response.setResponseCode(OK).setMessage("Replaced template for: " + jobKey);
+      return response.setResponseCode(OK);
     } catch (LockException e) {
-      return response.setResponseCode(LOCK_ERROR).setMessage(e.getMessage());
+      return addMessage(response, LOCK_ERROR, e.getMessage());
     } catch (CronException | TaskDescriptionException e) {
-      return response.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+      return addMessage(response, INVALID_REQUEST, e.getMessage());
     }
   }
 
@@ -372,19 +370,18 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
   public Response populateJobConfig(JobConfiguration description) {
     checkNotNull(description);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       SanitizedConfiguration sanitized =
           SanitizedConfiguration.fromUnsanitized(IJobConfiguration.build(description));
 
       PopulateJobResult result = new PopulateJobResult()
           .setPopulated(ITaskConfig.toBuildersSet(sanitized.getTaskConfigs().values()));
-      response.setResult(Result.populateJobResult(result))
-          .setResponseCode(OK)
-          .setMessage("Tasks populated");
+
+      response.setResult(Result.populateJobResult(result));
+      response.setResponseCode(OK);
     } catch (TaskDescriptionException e) {
-      response.setResponseCode(INVALID_REQUEST)
-          .setMessage("Invalid configuration: " + e.getMessage());
+      addMessage(response, INVALID_REQUEST, "Invalid configuration: " + e.getMessage());
     }
     return response;
   }
@@ -394,19 +391,18 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotNull(session);
     IJobKey jobKey = JobKeys.assertValid(IJobKey.build(mutableJobKey));
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       sessionValidator.checkAuthenticated(session, ImmutableSet.of(jobKey.getRole()));
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(response, AUTH_FAILED, e.getMessage());
     }
 
     try {
       cronJobManager.startJobNow(jobKey);
-      return response.setResponseCode(OK).setMessage("Cron run started.");
+      return response.setResponseCode(OK);
     } catch (CronException e) {
-      return response.setResponseCode(INVALID_REQUEST)
-          .setMessage("Failed to start cron job - " + e.getMessage());
+      return addMessage(response, INVALID_REQUEST, "Failed to start cron job - " + e.getMessage());
     }
   }
 
@@ -418,8 +414,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     Set<IScheduledTask> tasks =
         Storage.Util.weaklyConsistentFetchTasks(storage, Query.arbitrary(query));
 
-    return new Response().setResponseCode(OK)
-        .setResult(Result.scheduleStatusResult(
+    return okResponse(Result.scheduleStatusResult(
             new ScheduleStatusResult().setTasks(IScheduledTask.toBuildersList(tasks))));
   }
 
@@ -586,12 +581,13 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotNull(query);
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
 
     if (query.getJobName() != null && StringUtils.isBlank(query.getJobName())) {
-      response.setResponseCode(INVALID_REQUEST).setMessage(
+      return addMessage(
+          response,
+          INVALID_REQUEST,
           String.format("Invalid job name: '%s'", query.getJobName()));
-      return response;
     }
 
     Set<IScheduledTask> tasks = Storage.Util.consistentFetchTasks(storage, Query.arbitrary(query));
@@ -603,8 +599,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       try {
         context = Optional.of(validateSessionKeyForTasks(session, query, tasks));
       } catch (AuthFailedException e) {
-        response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
-        return response;
+        return addMessage(response, AUTH_FAILED, e.getMessage());
       }
     }
 
@@ -612,7 +607,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       validateLockForTasks(Optional.fromNullable(mutablelock).transform(ILock.FROM_BUILDER), tasks);
       schedulerCore.killTasks(Query.arbitrary(query), context.get().getIdentity());
     } catch (LockException e) {
-      return response.setResponseCode(LOCK_ERROR).setMessage(e.getMessage());
+      return addMessage(response, LOCK_ERROR, e.getMessage());
     }
 
     return response.setResponseCode(OK);
@@ -629,13 +624,12 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     MorePreconditions.checkNotBlank(shardIds);
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     SessionContext context;
     try {
       context = sessionValidator.checkAuthenticated(session, ImmutableSet.of(jobKey.getRole()));
     } catch (AuthFailedException e) {
-      response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
-      return response;
+      return addMessage(response, AUTH_FAILED, e.getMessage());
     }
 
     try {
@@ -643,11 +637,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           ILockKey.build(LockKey.job(jobKey.newBuilder())),
           Optional.fromNullable(mutableLock).transform(ILock.FROM_BUILDER));
       schedulerCore.restartShards(jobKey, shardIds, context.getIdentity());
-      response.setResponseCode(OK).setMessage("Shards are restarting.");
+      response.setResponseCode(OK);
     } catch (LockException e) {
-      response.setResponseCode(ResponseCode.LOCK_ERROR).setMessage(e.getMessage());
+      addMessage(response, LOCK_ERROR, e.getMessage());
     } catch (ScheduleException e) {
-      response.setResponseCode(ResponseCode.INVALID_REQUEST).setMessage(e.getMessage());
+      addMessage(response, INVALID_REQUEST, e.getMessage());
     }
 
     return response;
@@ -676,12 +670,12 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotNull(resourceAggregate);
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       quotaManager.saveQuota(ownerRole, IResourceAggregate.build(resourceAggregate));
-      return response.setResponseCode(OK).setMessage("Quota applied.");
+      return response.setResponseCode(OK);
     } catch (QuotaException e) {
-      return response.setResponseCode(ResponseCode.INVALID_REQUEST).setMessage(e.getMessage());
+      return addMessage(response, INVALID_REQUEST, e.getMessage());
     }
   }
 
@@ -716,33 +710,29 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
   }
 
   @Override
-  public Response forceTaskState(
-      String taskId,
-      ScheduleStatus status,
-      SessionKey session) {
-
+  public Response forceTaskState(String taskId, ScheduleStatus status, SessionKey session) {
     checkNotBlank(taskId);
     checkNotNull(status);
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     SessionContext context;
     try {
       // TODO(Sathya): Remove this after AOP-style session validation passes in a SessionContext.
       context = sessionValidator.checkAuthorized(session, Capability.ROOT, AuditCheck.REQUIRED);
     } catch (AuthFailedException e) {
-      response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      addMessage(response, AUTH_FAILED, e.getMessage());
       return response;
     }
 
     schedulerCore.setTaskStatus(taskId, status, transitionMessage(context.getIdentity()));
-    return new Response().setResponseCode(OK).setMessage("Transition attempted.");
+    return okEmptyResponse();
   }
 
   @Override
   public Response performBackup(SessionKey session) {
     backup.backupNow();
-    return new Response().setResponseCode(OK);
+    return okEmptyResponse();
   }
 
   @Override
@@ -753,11 +743,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
 
   @Override
   public Response stageRecovery(String backupId, SessionKey session) {
-    Response response = new Response().setResponseCode(OK);
+    Response response = okEmptyResponse();
     try {
       recovery.stage(backupId);
     } catch (RecoveryException e) {
-      response.setResponseCode(ERROR).setMessage(e.getMessage());
+      addMessage(response, ERROR, e.getMessage());
       LOG.log(Level.WARNING, "Failed to stage recovery: " + e, e);
     }
 
@@ -766,13 +756,13 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
 
   @Override
   public Response queryRecovery(TaskQuery query, SessionKey session) {
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       response.setResponseCode(OK)
           .setResult(Result.queryRecoveryResult(new QueryRecoveryResult()
               .setTasks(IScheduledTask.toBuildersSet(recovery.query(Query.arbitrary(query))))));
     } catch (RecoveryException e) {
-      response.setResponseCode(ERROR).setMessage(e.getMessage());
+      addMessage(response, ERROR, e.getMessage());
       LOG.log(Level.WARNING, "Failed to query recovery: " + e, e);
     }
 
@@ -781,11 +771,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
 
   @Override
   public Response deleteRecoveryTasks(TaskQuery query, SessionKey session) {
-    Response response = new Response().setResponseCode(OK);
+    Response response = okEmptyResponse();
     try {
       recovery.deleteTasks(Query.arbitrary(query));
     } catch (RecoveryException e) {
-      response.setResponseCode(ERROR).setMessage(e.getMessage());
+      addMessage(response, ERROR, e.getMessage());
       LOG.log(Level.WARNING, "Failed to delete recovery tasks: " + e, e);
     }
 
@@ -794,11 +784,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
 
   @Override
   public Response commitRecovery(SessionKey session) {
-    Response response = new Response().setResponseCode(OK);
+    Response response = okEmptyResponse();
     try {
       recovery.commit();
     } catch (RecoveryException e) {
-      response.setResponseCode(ERROR).setMessage(e.getMessage());
+      addMessage(response, ERROR, e.getMessage());
     }
 
     return response;
@@ -807,18 +797,18 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
   @Override
   public Response unloadRecovery(SessionKey session) {
     recovery.unload();
-    return new Response().setResponseCode(OK);
+    return okEmptyResponse();
   }
 
   @Override
   public Response snapshot(SessionKey session) {
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     try {
       storage.snapshot();
-      return response.setResponseCode(OK).setMessage("Compaction successful.");
+      return response.setResponseCode(OK);
     } catch (Storage.StorageException e) {
       LOG.log(Level.WARNING, "Requested snapshot failed.", e);
-      return response.setResponseCode(ERROR).setMessage(e.getMessage());
+      return addMessage(response, ERROR, e.getMessage());
     }
   }
 
@@ -840,9 +830,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       SessionKey session) {
 
     if (request.getRewriteCommandsSize() == 0) {
-      return new Response()
-          .setResponseCode(ResponseCode.ERROR)
-          .setMessage("No rewrite commands provided.");
+      return addMessage(Util.emptyResponse(), ERROR, "No rewrite commands provided.");
     }
 
     return storage.write(new MutateWork.Quiet<Response>() {
@@ -857,11 +845,13 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           }
         }
 
-        Response resp = new Response();
+        Response resp = emptyResponse();
         if (errors.isEmpty()) {
-          resp.setResponseCode(OK).setMessage("All rewrites completed successfully.");
+          resp.setResponseCode(OK);
         } else {
-          resp.setResponseCode(ResponseCode.WARNING).setMessage(Joiner.on(", ").join(errors));
+          for (String error : errors) {
+            addMessage(resp, WARNING, error);
+          }
         }
         return resp;
       }
@@ -986,15 +976,14 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotBlank(config.getInstanceIds());
     IJobKey jobKey = JobKeys.assertValid(IJobKey.build(config.getKey()));
 
-    Response resp = new Response();
+    Response resp = Util.emptyResponse();
     try {
       sessionValidator.checkAuthenticated(session, ImmutableSet.of(jobKey.getRole()));
       ITaskConfig task = ConfigurationManager.validateAndPopulate(
           ITaskConfig.build(config.getTaskConfig()));
 
       if (cronJobManager.hasJob(jobKey)) {
-        return resp.setResponseCode(INVALID_REQUEST)
-            .setMessage("Cron jobs are not supported here.");
+        return addMessage(resp, INVALID_REQUEST, "Instances may not be added to cron jobs.");
       }
 
       lockManager.validateIfLocked(
@@ -1002,13 +991,13 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
           Optional.fromNullable(mutableLock).transform(ILock.FROM_BUILDER));
 
       schedulerCore.addInstances(jobKey, ImmutableSet.copyOf(config.getInstanceIds()), task);
-      return resp.setResponseCode(OK).setMessage("Successfully added instances.");
+      return resp.setResponseCode(OK);
     } catch (AuthFailedException e) {
-      return resp.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(resp, AUTH_FAILED, e.getMessage());
     } catch (LockException e) {
-      return resp.setResponseCode(LOCK_ERROR).setMessage(e.getMessage());
+      return addMessage(resp, LOCK_ERROR, e.getMessage());
     } catch (TaskDescriptionException | ScheduleException e) {
-      return resp.setResponseCode(INVALID_REQUEST).setMessage(e.getMessage());
+      return addMessage(resp, INVALID_REQUEST, e.getMessage());
     }
   }
 
@@ -1027,7 +1016,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotNull(session);
 
     ILockKey lockKey = ILockKey.build(mutableLockKey);
-    Response response = new Response();
+    Response response = Util.emptyResponse();
 
     try {
       SessionContext context = sessionValidator.checkAuthenticated(
@@ -1038,11 +1027,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
       response.setResult(Result.acquireLockResult(
           new AcquireLockResult().setLock(lock.newBuilder())));
 
-      return response.setResponseCode(OK).setMessage("Lock has been acquired.");
+      return response.setResponseCode(OK);
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(response, AUTH_FAILED, e.getMessage());
     } catch (LockException e) {
-      return response.setResponseCode(ResponseCode.LOCK_ERROR).setMessage(e.getMessage());
+      return addMessage(response, LOCK_ERROR, e.getMessage());
     }
   }
 
@@ -1052,7 +1041,7 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     checkNotNull(validation);
     checkNotNull(session);
 
-    Response response = new Response();
+    Response response = Util.emptyResponse();
     ILock lock = ILock.build(mutableLock);
 
     try {
@@ -1064,11 +1053,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
         lockManager.validateIfLocked(lock.getKey(), Optional.of(lock));
       }
       lockManager.releaseLock(lock);
-      return response.setResponseCode(OK).setMessage("Lock has been released.");
+      return response.setResponseCode(OK);
     } catch (AuthFailedException e) {
-      return response.setResponseCode(AUTH_FAILED).setMessage(e.getMessage());
+      return addMessage(response, AUTH_FAILED, e.getMessage());
     } catch (LockException e) {
-      return response.setResponseCode(ResponseCode.LOCK_ERROR).setMessage(e.getMessage());
+      return addMessage(response, LOCK_ERROR, e.getMessage());
     }
   }
 
@@ -1077,7 +1066,11 @@ class SchedulerThriftInterface implements AuroraAdmin.Iface {
     return Optional.of("Transition forced by " + user);
   }
 
+  private static Response okEmptyResponse()  {
+    return emptyResponse().setResponseCode(OK);
+  }
+
   private static Response okResponse(Result result) {
-    return new Response().setResponseCode(OK).setResult(result);
+    return okEmptyResponse().setResult(result);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/java/org/apache/aurora/scheduler/thrift/Util.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/Util.java b/src/main/java/org/apache/aurora/scheduler/thrift/Util.java
new file mode 100644
index 0000000..4ebb608
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/Util.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed 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.aurora.scheduler.thrift;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+import org.apache.aurora.gen.Response;
+import org.apache.aurora.gen.ResponseCode;
+import org.apache.aurora.gen.ResponseDetail;
+
+/**
+ * Utility class for constructing responses to API calls.
+ */
+public final class Util {
+
+  private Util() {
+    // Utility class.
+  }
+
+  /**
+   * Creates a new empty response.
+   *
+   * @return An empty response message.
+   */
+  public static Response emptyResponse() {
+    return new Response().setDetails(Lists.<ResponseDetail>newArrayList());
+  }
+
+  /**
+   * Adds a human-friendly message to a response, usually to indicate a problem or deprecation
+   * encountered while handling the request.
+   *
+   * @param response Response to augment.
+   * @param message Message to include in the response.
+   * @return {@code response} with {@code message} included.
+   */
+  public static Response addMessage(Response response, String message) {
+    String existingMessage = response.getMessageDEPRECATED();
+    String prefix = Strings.isNullOrEmpty(existingMessage) ? "" : existingMessage + ", ";
+    response.setMessageDEPRECATED(prefix + message);
+
+    response.addToDetails(new ResponseDetail(message));
+    return response;
+  }
+
+  /**
+   * Identical to {@link #addMessage(Response, String)} that also applies a response code.
+   *
+   * @param response Response to augment.
+   * @param code Response code to include.
+   * @param message Message to include in the response.
+   * @return {@code response} with {@code message} included.
+   * @see {@link #addMessage(Response, String)}
+   */
+  public static Response addMessage(Response response, ResponseCode code, String message) {
+    return addMessage(response.setResponseCode(code), message);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/java/org/apache/aurora/scheduler/thrift/aop/FeatureToggleInterceptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/aop/FeatureToggleInterceptor.java b/src/main/java/org/apache/aurora/scheduler/thrift/aop/FeatureToggleInterceptor.java
index 0c1477c..e176a0d 100644
--- a/src/main/java/org/apache/aurora/scheduler/thrift/aop/FeatureToggleInterceptor.java
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/aop/FeatureToggleInterceptor.java
@@ -22,6 +22,7 @@ import com.google.common.base.Predicate;
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
 import org.apache.aurora.gen.ResponseCode;
+import org.apache.aurora.scheduler.thrift.Util;
 
 /**
  * A method interceptor that blocks access to features based on a supplied predicate.
@@ -36,8 +37,8 @@ public class FeatureToggleInterceptor implements MethodInterceptor {
     if (allowMethod.apply(method)) {
       return invocation.proceed();
     } else {
-      return Interceptors.properlyTypedResponse(
-          method,
+      return Util.addMessage(
+          Util.emptyResponse(),
           ResponseCode.ERROR,
           "The " + method.getName() + " feature is currently disabled on this scheduler.");
     }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/java/org/apache/aurora/scheduler/thrift/aop/Interceptors.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/aop/Interceptors.java b/src/main/java/org/apache/aurora/scheduler/thrift/aop/Interceptors.java
deleted file mode 100644
index 1d69aa1..0000000
--- a/src/main/java/org/apache/aurora/scheduler/thrift/aop/Interceptors.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Licensed 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.aurora.scheduler.thrift.aop;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.logging.Logger;
-
-import com.google.common.base.Throwables;
-
-import org.apache.aurora.gen.ResponseCode;
-
-/**
- * Utility class for functions useful when implementing an interceptor on the thrift interface.
- */
-final class Interceptors {
-
-  private Interceptors() {
-    // Utility class.
-  }
-
-  private static final Logger LOG = Logger.getLogger(Interceptors.class.getName());
-
-  static Object properlyTypedResponse(Method method, ResponseCode responseCode, String message)
-      throws IllegalAccessException, InstantiationException {
-
-    Class<?> returnType = method.getReturnType();
-    Object response = returnType.newInstance();
-    invoke(returnType, response, "setResponseCode", ResponseCode.class, responseCode);
-    invoke(returnType, response, "setMessage", String.class, message);
-    return response;
-  }
-
-  private static <T> void invoke(
-      Class<?> type,
-      Object obj,
-      String name,
-      Class<T> parameterType,
-      T argument) {
-
-    Method method;
-    try {
-      method = type.getMethod(name, parameterType);
-    } catch (NoSuchMethodException e) {
-      LOG.severe(type + " does not support " + name);
-      throw Throwables.propagate(e);
-    }
-    try {
-      method.invoke(obj, argument);
-    } catch (IllegalAccessException e) {
-      LOG.severe("Method " + name + " is not accessible in " + type);
-      throw Throwables.propagate(e);
-    } catch (InvocationTargetException e) {
-      LOG.severe("Failed to invoke " + name + " on " + type);
-      throw Throwables.propagate(e);
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/java/org/apache/aurora/scheduler/thrift/aop/LoggingInterceptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/aop/LoggingInterceptor.java b/src/main/java/org/apache/aurora/scheduler/thrift/aop/LoggingInterceptor.java
index 8299cf3..8692051 100644
--- a/src/main/java/org/apache/aurora/scheduler/thrift/aop/LoggingInterceptor.java
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/aop/LoggingInterceptor.java
@@ -32,8 +32,7 @@ import org.apache.aurora.gen.ExecutorConfig;
 import org.apache.aurora.gen.JobConfiguration;
 import org.apache.aurora.gen.ResponseCode;
 import org.apache.aurora.gen.SessionKey;
-
-import static org.apache.aurora.scheduler.thrift.aop.Interceptors.properlyTypedResponse;
+import org.apache.aurora.scheduler.thrift.Util;
 
 /**
  * A method interceptor that logs all invocations as well as any unchecked exceptions thrown from
@@ -43,9 +42,9 @@ class LoggingInterceptor implements MethodInterceptor {
 
   private static final Logger LOG = Logger.getLogger(LoggingInterceptor.class.getName());
 
-  @Inject private CapabilityValidator validator;
+  @Inject
+  private CapabilityValidator validator;
 
-  // TODO(wfarner): Scrub updateToken when it is identifiable by type.
   private final Map<Class<?>, Function<Object, String>> printFunctions =
       ImmutableMap.<Class<?>, Function<Object, String>>of(
           JobConfiguration.class,
@@ -88,7 +87,7 @@ class LoggingInterceptor implements MethodInterceptor {
       return invocation.proceed();
     } catch (RuntimeException e) {
       LOG.log(Level.WARNING, "Uncaught exception while handling " + message, e);
-      return properlyTypedResponse(invocation.getMethod(), ResponseCode.ERROR, e.getMessage());
+      return Util.addMessage(Util.emptyResponse(), ResponseCode.ERROR, e.getMessage());
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/java/org/apache/aurora/scheduler/thrift/aop/UserCapabilityInterceptor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/thrift/aop/UserCapabilityInterceptor.java b/src/main/java/org/apache/aurora/scheduler/thrift/aop/UserCapabilityInterceptor.java
index a3e0c42..411efea 100644
--- a/src/main/java/org/apache/aurora/scheduler/thrift/aop/UserCapabilityInterceptor.java
+++ b/src/main/java/org/apache/aurora/scheduler/thrift/aop/UserCapabilityInterceptor.java
@@ -35,6 +35,7 @@ import org.apache.aurora.auth.CapabilityValidator.Capability;
 import org.apache.aurora.auth.SessionValidator.AuthFailedException;
 import org.apache.aurora.gen.ResponseCode;
 import org.apache.aurora.gen.SessionKey;
+import org.apache.aurora.scheduler.thrift.Util;
 import org.apache.aurora.scheduler.thrift.auth.Requires;
 
 /**
@@ -95,8 +96,8 @@ class UserCapabilityInterceptor implements MethodInterceptor {
     }
 
     // User is not permitted to perform this operation.
-    return Interceptors.properlyTypedResponse(
-        method,
+    return Util.addMessage(
+        Util.emptyResponse(),
         ResponseCode.AUTH_FAILED,
         "Session identified by '" + key
             + "' does not have the required capability to perform this action: " + whitelist);

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/__init__.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/__init__.py b/src/main/python/apache/aurora/client/api/__init__.py
index 7c15d9d..a23660b 100644
--- a/src/main/python/apache/aurora/client/api/__init__.py
+++ b/src/main/python/apache/aurora/client/api/__init__.py
@@ -125,7 +125,7 @@ class AuroraClientAPI(object):
     log.info("Canceling update on job %s" % job_key)
     resp = Updater.cancel_update(self._scheduler_proxy, job_key)
     if resp.responseCode != ResponseCode.OK:
-      log.error('Error cancelling the update: %s' % resp.message)
+      log.error('Error cancelling the update: %s' % resp.messageDEPRECATED)
     return resp
 
   def restart(self, job_key, instances, updater_config, health_check_interval_seconds):

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/command_runner.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/command_runner.py b/src/main/python/apache/aurora/client/api/command_runner.py
index b2e2832..8b29f35 100644
--- a/src/main/python/apache/aurora/client/api/command_runner.py
+++ b/src/main/python/apache/aurora/client/api/command_runner.py
@@ -149,5 +149,5 @@ class InstanceDistributedCommandRunner(DistributedCommandRunner):
         yield task
     else:
       print_aurora_log(logging.ERROR,
-          "Error: could not retrieve task information for run command: %s" % resp.message)
-      raise ValueError("Could not retrieve task information: %s" % resp.message)
+          "Error: could not retrieve task information for run command: %s" % resp.messageDEPRECATED)
+      raise ValueError("Could not retrieve task information: %s" % resp.messageDEPRECATED)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/instance_watcher.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/instance_watcher.py b/src/main/python/apache/aurora/client/api/instance_watcher.py
index 15a7f74..e09aa9a 100644
--- a/src/main/python/apache/aurora/client/api/instance_watcher.py
+++ b/src/main/python/apache/aurora/client/api/instance_watcher.py
@@ -141,5 +141,5 @@ class InstanceWatcher(object):
       tasks = resp.result.scheduleStatusResult.tasks
 
     log.debug('Response from scheduler: %s (message: %s)'
-        % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.message))
+        % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.messageDEPRECATED))
     return tasks

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/quota_check.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/quota_check.py b/src/main/python/apache/aurora/client/api/quota_check.py
index 3a73f24..f0b4462 100644
--- a/src/main/python/apache/aurora/client/api/quota_check.py
+++ b/src/main/python/apache/aurora/client/api/quota_check.py
@@ -75,13 +75,13 @@ class QuotaCheck(object):
 
     Returns: ResponseCode.OK if check is successful.
     """
-    resp_ok = Response(responseCode=ResponseCode.OK, message='Quota check successful.')
+    resp_ok = Response(responseCode=ResponseCode.OK, messageDEPRECATED='Quota check successful.')
     if not production:
       return resp_ok
 
     resp = self._scheduler.getQuota(job_key.role)
     if resp.responseCode != ResponseCode.OK:
-      log.error('Failed to get quota from scheduler: %s' % resp.message)
+      log.error('Failed to get quota from scheduler: %s' % resp.messageDEPRECATED)
       return resp
 
     allocated = CapacityRequest(resp.result.getQuotaResult.quota)
@@ -94,7 +94,9 @@ class QuotaCheck(object):
       print_quota(allocated.quota(), 'Total allocated quota', job_key.role)
       print_quota(consumed.quota(), 'Consumed quota', job_key.role)
       print_quota(requested.quota(), 'Requested', job_key.name)
-      return Response(responseCode=ResponseCode.INVALID_REQUEST, message='Failed quota check.')
+      return Response(
+          responseCode=ResponseCode.INVALID_REQUEST,
+          messageDEPRECATED='Failed quota check.')
 
     return resp_ok
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/restarter.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/restarter.py b/src/main/python/apache/aurora/client/api/restarter.py
index 15258c4..e843fc3 100644
--- a/src/main/python/apache/aurora/client/api/restarter.py
+++ b/src/main/python/apache/aurora/client/api/restarter.py
@@ -72,7 +72,7 @@ class Restarter(object):
 
       resp = self._scheduler.restartShards(self._job_key.to_thrift(), batch, self._lock)
       if resp.responseCode != ResponseCode.OK:
-        log.error('Error restarting instances: %s', resp.message)
+        log.error('Error restarting instances: %s', resp.messageDEPRECATED)
         return resp
 
       failed_instances = self._instance_watcher.watch(batch)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/api/updater.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/api/updater.py b/src/main/python/apache/aurora/client/api/updater.py
index f028498..ea7285a 100644
--- a/src/main/python/apache/aurora/client/api/updater.py
+++ b/src/main/python/apache/aurora/client/api/updater.py
@@ -99,7 +99,7 @@ class Updater(object):
     if resp.responseCode == ResponseCode.OK:
       self._lock = None
     else:
-      log.error('There was an error finalizing the update: %s' % resp.message)
+      log.error('There was an error finalizing the update: %s' % resp.messageDEPRECATED)
     return resp
 
   def _update(self, instance_configs):
@@ -422,7 +422,7 @@ class Updater(object):
         instanceIds=instanceIds)
 
   def _failed_response(self, message):
-    return Response(responseCode=ResponseCode.ERROR, message=message)
+    return Response(responseCode=ResponseCode.ERROR, messageDEPRECATED=message)
 
   def update(self, instances=None):
     """Performs the job update, blocking until it completes.
@@ -484,7 +484,7 @@ class Updater(object):
 
     Raises Error in case of unexpected response status.
     """
-    name, message = ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.message
+    name, message = ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.messageDEPRECATED
     if resp.responseCode == ResponseCode.OK:
       log.debug('Response from scheduler: %s (message: %s)' % (name, message))
     else:

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/base.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/base.py b/src/main/python/apache/aurora/client/base.py
index 1f41ef0..d54c7cb 100644
--- a/src/main/python/apache/aurora/client/base.py
+++ b/src/main/python/apache/aurora/client/base.py
@@ -40,7 +40,7 @@ def die(msg):
 
 def log_response(resp):
   log.info('Response from scheduler: %s (message: %s)'
-      % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.message))
+      % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.messageDEPRECATED))
 
 def check_and_log_response(resp):
   log_response(resp)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/cli/context.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/context.py b/src/main/python/apache/aurora/client/cli/context.py
index b1e8e40..d1f1f3f 100644
--- a/src/main/python/apache/aurora/client/cli/context.py
+++ b/src/main/python/apache/aurora/client/cli/context.py
@@ -90,9 +90,9 @@ class AuroraCommandContext(Context):
 
   def check_and_log_response(self, resp):
     self.print_log(logging.INFO, 'Response from scheduler: %s (message: %s)'
-        % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.message))
+        % (ResponseCode._VALUES_TO_NAMES[resp.responseCode], resp.messageDEPRECATED))
     if resp.responseCode != ResponseCode.OK:
-      raise self.CommandError(EXIT_API_ERROR, resp.message)
+      raise self.CommandError(EXIT_API_ERROR, resp.messageDEPRECATED)
 
   @classmethod
   def parse_partial_jobkey(cls, key):
@@ -120,7 +120,7 @@ class AuroraCommandContext(Context):
       api = self.get_api(cluster)
       resp = api.get_jobs(role)
       if resp.responseCode is not ResponseCode.OK:
-        raise self.CommandError(EXIT_COMMAND_FAILURE, resp.message)
+        raise self.CommandError(EXIT_COMMAND_FAILURE, resp.messageDEPRECATED)
       result.extend([AuroraJobKey(cluster, job.key.role, job.key.environment, job.key.name)
           for job in resp.result.getJobsResult.configs])
     return result
@@ -159,7 +159,7 @@ class AuroraCommandContext(Context):
     api = self.get_api(key.cluster)
     resp = api.check_status(key)
     if resp.responseCode is not ResponseCode.OK:
-      raise self.CommandError(EXIT_INVALID_PARAMETER, resp.message)
+      raise self.CommandError(EXIT_INVALID_PARAMETER, resp.messageDEPRECATED)
     return resp.result.scheduleStatusResult.tasks or None
 
   def get_active_instances(self, key):

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/cli/jobs.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/jobs.py b/src/main/python/apache/aurora/client/cli/jobs.py
index 59e3a37..8020c35 100644
--- a/src/main/python/apache/aurora/client/cli/jobs.py
+++ b/src/main/python/apache/aurora/client/cli/jobs.py
@@ -119,7 +119,7 @@ class CreateJobCommand(Verb):
     if resp.responseCode == ResponseCode.INVALID_REQUEST:
       raise context.CommandError(EXIT_INVALID_PARAMETER, 'Job not found')
     elif resp.responseCode == ResponseCode.ERROR:
-      raise context.CommandError(EXIT_COMMAND_FAILURE, resp.message)
+      raise context.CommandError(EXIT_COMMAND_FAILURE, resp.messageDEPRECATED)
     if context.options.open_browser:
       context.open_job_page(api, config)
     if context.options.wait_until == 'RUNNING':
@@ -185,7 +185,7 @@ alternate diff program by setting the DIFF_VIEWER environment variable."""
     resp = api.populate_job_config(config)
     if resp.responseCode != ResponseCode.OK:
       raise context.CommandError(EXIT_INVALID_CONFIGURATION,
-          'Error loading configuration: %s' % resp.message)
+          'Error loading configuration: %s' % resp.messageDEPRECATED)
     local_tasks = resp.result.populateJobResult.populated
     diff_program = os.environ.get('DIFF_VIEWER', 'diff')
     with NamedTemporaryFile() as local:
@@ -311,7 +311,7 @@ class AbstractKillCommand(Verb):
           context, api.scheduler_proxy, job, batch) is not EXIT_OK:
 
         context.print_log(logging.INFO,
-                          'Kill of shards %s failed with error %s' % (batch, resp.message))
+            'Kill of shards %s failed with error %s' % (batch, resp.messageDEPRECATED))
         errors += 1
         if errors > context.options.max_total_failures:
           raise context.CommandError(EXIT_COMMAND_FAILURE,
@@ -609,12 +609,12 @@ to preview what changes will take effect.
       # NOTE(mchucarroll): we assume here that updating a cron schedule and updating a
       # running job are different operations; in client v1, they were both done with update.
       raise context.CommandError(EXIT_COMMAND_FAILURE,
-          'Server could not find running job to update: %s' % resp.message)
+          'Server could not find running job to update: %s' % resp.messageDEPRECATED)
     remote_tasks = [t.assignedTask.task for t in resp.result.scheduleStatusResult.tasks]
     resp = api.populate_job_config(config)
     if resp.responseCode != ResponseCode.OK:
       raise context.CommandError(EXIT_COMMAND_FAILURE,
-          'Server could not populate job config for comparison: %s' % resp.message)
+          'Server could not populate job config for comparison: %s' % resp.messageDEPRECATED)
     local_task_count = len(resp.result.populateJobResult.populated)
     remote_task_count = len(remote_tasks)
     if (local_task_count >= 4 * remote_task_count or
@@ -637,7 +637,7 @@ to preview what changes will take effect.
     resp = api.update_job(config, context.options.healthcheck_interval_seconds,
         instances)
     if resp.responseCode != ResponseCode.OK:
-      raise context.CommandError(EXIT_COMMAND_FAILURE, 'Update failed: %s' % resp.message)
+      raise context.CommandError(EXIT_COMMAND_FAILURE, 'Update failed: %s' % resp.messageDEPRECATED)
     return EXIT_OK
 
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/cli/task.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/cli/task.py b/src/main/python/apache/aurora/client/cli/task.py
index d5c58db..2b8ea26 100644
--- a/src/main/python/apache/aurora/client/cli/task.py
+++ b/src/main/python/apache/aurora/client/cli/task.py
@@ -131,7 +131,8 @@ class SshCommand(Verb):
     api = context.get_api(cluster)
     resp = api.query(api.build_query(role, name, set([int(instance)]), env=env))
     if resp.responseCode != ResponseCode.OK:
-      raise context.CommandError('Unable to get information about instance: %s' % resp.message)
+      raise context.CommandError('Unable to get information about instance: %s'
+          % resp.messageDEPRECATED)
     first_task = resp.result.scheduleStatusResult.tasks[0]
     remote_cmd = context.options.command or 'bash'
     command = DistributedCommandRunner.substitute(remote_cmd, first_task,

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/commands/admin.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/commands/admin.py b/src/main/python/apache/aurora/client/commands/admin.py
index 4bf5735..b4c0483 100644
--- a/src/main/python/apache/aurora/client/commands/admin.py
+++ b/src/main/python/apache/aurora/client/commands/admin.py
@@ -155,7 +155,7 @@ def query(args, options):
   query_info = api.query(api.build_query(role, job, instances=instances, statuses=states))
   tasks = query_info.result.scheduleStatusResult.tasks
   if query_info.responseCode != ResponseCode.OK:
-    die('Failed to query scheduler: %s' % query_info.message)
+    die('Failed to query scheduler: %s' % query_info.messageDEPRECATED)
   if tasks is None:
     return
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/commands/core.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/commands/core.py b/src/main/python/apache/aurora/client/commands/core.py
index 4eb4e46..29e70a9 100644
--- a/src/main/python/apache/aurora/client/commands/core.py
+++ b/src/main/python/apache/aurora/client/commands/core.py
@@ -186,11 +186,11 @@ def diff(job_spec, config_file):
   api = make_client(cluster)
   resp = api.query(api.build_query(role, name, statuses=ACTIVE_STATES, env=env))
   if resp.responseCode != ResponseCode.OK:
-    die('Request failed, server responded with "%s"' % resp.message)
+    die('Request failed, server responded with "%s"' % resp.messageDEPRECATED)
   remote_tasks = [t.assignedTask.task for t in resp.result.scheduleStatusResult.tasks]
   resp = api.populate_job_config(config)
   if resp.responseCode != ResponseCode.OK:
-    die('Request failed, server responded with "%s"' % resp.message)
+    die('Request failed, server responded with "%s"' % resp.messageDEPRECATED)
   local_tasks = resp.result.populateJobResult.populated
 
   pp = pprint.PrettyPrinter(indent=2)
@@ -450,7 +450,7 @@ def kill_in_batches(api, job_key, instances_arg, batch_size, max_failures):
     for batch in make_batches(instances_to_kill, batch_size):
       resp = api.kill_job(job_key, batch)
       if resp.responseCode is not ResponseCode.OK:
-        log.info("Kill of shards %s failed with error %s" % (batch, resp.message))
+        log.info("Kill of shards %s failed with error %s" % (batch, resp.messageDEPRECATED))
         print('ERROR IN KILL_JOB')
         errors += 1
         if errors > max_failures:
@@ -593,11 +593,11 @@ def update(job_spec, config_file):
     resp = api.query(api.build_query(config.role(), config.name(),
         statuses=ACTIVE_STATES, env=config.environment()))
     if resp.responseCode != ResponseCode.OK:
-      die('Could not get job status from server for comparison: %s' % resp.message)
+      die('Could not get job status from server for comparison: %s' % resp.messageDEPRECATED)
     remote_tasks = [t.assignedTask.task for t in resp.result.scheduleStatusResult.tasks]
     resp = api.populate_job_config(config)
     if resp.responseCode != ResponseCode.OK:
-      die('Server could not populate job config for comparison: %s' % resp.message)
+      die('Server could not populate job config for comparison: %s' % resp.messageDEPRECATED)
     local_task_count = len(resp.result.populateJobResult.populated)
     remote_task_count = len(remote_tasks)
     if (local_task_count >= 4 * remote_task_count or local_task_count <= 4 * remote_task_count

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/main/python/apache/aurora/client/hooks/hooked_api.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/client/hooks/hooked_api.py b/src/main/python/apache/aurora/client/hooks/hooked_api.py
index 7c3b866..a205777 100644
--- a/src/main/python/apache/aurora/client/hooks/hooked_api.py
+++ b/src/main/python/apache/aurora/client/hooks/hooked_api.py
@@ -85,7 +85,7 @@ class HookedAuroraClientAPI(NonHookedAuroraClientAPI):
     def __str__(self):
       return '%s: %s: %s' % (self.__class__.__name__,
           ResponseCode._VALUES_TO_NAMES.get(self.response.responseCode, 'UNKNOWN'),
-          self.response.message)
+          self.response.messageDEPRECATED)
 
   @classmethod
   def _meta_hook(cls, hook, hook_method):

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/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 c6e1fcb..e72bcfb 100644
--- a/src/main/thrift/org/apache/aurora/gen/api.thrift
+++ b/src/main/thrift/org/apache/aurora/gen/api.thrift
@@ -454,14 +454,19 @@ union Result {
   18: JobSummaryResult jobSummaryResult
 }
 
+struct ResponseDetail {
+  1: string message
+}
+
 struct Response {
   1: ResponseCode responseCode
-  2: string message
-  // TODO(Suman Karumuri): Remove version field
-  // version field is deprecated.
+  // TODO(wfarner): Remove the message field in 0.7.0. (AURORA-466)
+  2: optional string messageDEPRECATED
+  // TODO(wfarner): Remove version field in 0.7.0. (AURORA-467)
   4: APIVersion DEPRECATEDversion
   5: ServerInfo serverInfo
   3: optional Result result
+  6: list<ResponseDetail> details
 }
 
 // A service that provides all the read only calls to the Aurora scheduler.

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/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 75bb01b..16ece20 100644
--- a/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/thrift/SchedulerThriftInterfaceTest.java
@@ -13,10 +13,14 @@
  */
 package org.apache.aurora.scheduler.thrift;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.util.Date;
 import java.util.Set;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -57,6 +61,7 @@ import org.apache.aurora.gen.LockKey;
 import org.apache.aurora.gen.ResourceAggregate;
 import org.apache.aurora.gen.Response;
 import org.apache.aurora.gen.ResponseCode;
+import org.apache.aurora.gen.ResponseDetail;
 import org.apache.aurora.gen.Result;
 import org.apache.aurora.gen.RewriteConfigsRequest;
 import org.apache.aurora.gen.RoleSummary;
@@ -126,6 +131,7 @@ import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 public class SchedulerThriftInterfaceTest extends EasyMockTest {
@@ -203,7 +209,23 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
       }
     };
     Injector injector = Guice.createInjector(testModule, new AopModule());
-    thrift = injector.getInstance(AuroraAdmin.Iface.class);
+    final AuroraAdmin.Iface realThrift = injector.getInstance(AuroraAdmin.Iface.class);
+
+    // Capture all API method calls to validate response objects.
+    Class<AuroraAdmin.Iface> thriftClass = AuroraAdmin.Iface.class;
+    thrift = (AuroraAdmin.Iface) Proxy.newProxyInstance(
+        thriftClass.getClassLoader(),
+        new Class<?>[] {thriftClass},
+        new InvocationHandler() {
+          @Override
+          public Object invoke(Object o, Method method, Object[] args) throws Throwable {
+            System.out.println("Invoking " + method);
+            Response response = (Response) method.invoke(realThrift, args);
+            assertTrue(response.isSetResponseCode());
+            assertNotNull(response.getDetails());
+            return response;
+          }
+        });
   }
 
   private void setUpValidationExpectations() throws Exception {
@@ -278,6 +300,18 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
         thrift.createJob(job.newBuilder(), LOCK.newBuilder(), SESSION));
   }
 
+  private void assertMessageMatches(Response response, final String string) {
+    // TODO(wfarner): This test coverage could be much better.  Circle back to apply more thorough
+    // response contents testing throughout.
+    assertEquals(string, response.getMessageDEPRECATED());
+    assertTrue(Iterables.any(response.getDetails(), new Predicate<ResponseDetail>() {
+      @Override
+      public boolean apply(ResponseDetail detail) {
+        return detail.getMessage().equals(string);
+      }
+    }));
+  }
+
   @Test
   public void testCreateJobFailsNoExecutorConfig() throws Exception {
     JobConfiguration job = makeJob();
@@ -287,7 +321,8 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
 
     Response response = thrift.createJob(job, LOCK.newBuilder(), SESSION);
     assertResponse(INVALID_REQUEST, response);
-    assertTrue(response.getMessage().contains("Configuration"));
+    // TODO(wfarner): Don't rely on a magic string here, reference a constant from the source.
+    assertMessageMatches(response, "Configuration may not be null");
   }
 
   @Test
@@ -588,7 +623,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
 
     Response resp = thrift.restartShards(JOB_KEY.newBuilder(), shards, DEFAULT_LOCK, SESSION);
     assertResponse(INVALID_REQUEST, resp);
-    assertEquals(message, resp.getMessage());
+    assertMessageMatches(resp, message);
   }
 
   @Test
@@ -1089,7 +1124,7 @@ public class SchedulerThriftInterfaceTest extends EasyMockTest {
   }
 
   private Response okResponse(Result result) {
-    return new Response()
+    return Util.emptyResponse()
         .setResponseCode(OK)
         .setDEPRECATEDversion(API_VERSION)
         .setServerInfo(SERVER_INFO)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_disambiguator.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_disambiguator.py b/src/test/python/apache/aurora/client/api/test_disambiguator.py
index 08d671b..fb6380d 100644
--- a/src/test/python/apache/aurora/client/api/test_disambiguator.py
+++ b/src/test/python/apache/aurora/client/api/test_disambiguator.py
@@ -50,7 +50,7 @@ class LiveJobDisambiguatorTest(mox.MoxTestBase):
   def _expect_get_jobs(self, *envs):
     self._api.get_jobs(self.ROLE).AndReturn(Response(
       responseCode=ResponseCode.OK,
-      message='Mock OK',
+      messageDEPRECATED='Mock OK',
       result = Result(getJobsResult=GetJobsResult(
         configs=set(JobConfiguration(key=JobKey(role=self.ROLE, environment=env, name=self.NAME))
         for env in envs)))))

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_instance_watcher.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_instance_watcher.py b/src/test/python/apache/aurora/client/api/test_instance_watcher.py
index 89f1d90..b2d0c80 100644
--- a/src/test/python/apache/aurora/client/api/test_instance_watcher.py
+++ b/src/test/python/apache/aurora/client/api/test_instance_watcher.py
@@ -74,7 +74,7 @@ class InstanceWatcherTest(unittest.TestCase):
 
   def expect_get_statuses(self, instance_ids=WATCH_INSTANCES, num_calls=EXPECTED_CYCLES):
     tasks = [self.create_task(instance_id) for instance_id in instance_ids]
-    response = Response(responseCode=ResponseCode.OK, message='test')
+    response = Response(responseCode=ResponseCode.OK, messageDEPRECATED='test')
     response.result = Result()
     response.result.scheduleStatusResult = ScheduleStatusResult(tasks=tasks)
 
@@ -84,7 +84,7 @@ class InstanceWatcherTest(unittest.TestCase):
 
   def expect_io_error_in_get_statuses(self, instance_ids=WATCH_INSTANCES, num_calls=EXPECTED_CYCLES):
     tasks = [self.create_task(instance_id) for instance_id in instance_ids]
-    response = Response(responseCode=ResponseCode.OK, message='test')
+    response = Response(responseCode=ResponseCode.OK, messageDEPRECATED='test')
     response.result = Result()
     response.result.scheduleStatusResult = ScheduleStatusResult(tasks=tasks)
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_job_monitor.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_job_monitor.py b/src/test/python/apache/aurora/client/api/test_job_monitor.py
index 7fe1a00..665db74 100644
--- a/src/test/python/apache/aurora/client/api/test_job_monitor.py
+++ b/src/test/python/apache/aurora/client/api/test_job_monitor.py
@@ -58,7 +58,7 @@ class JobMonitorTest(unittest.TestCase):
     )
   def mock_get_tasks(self, tasks, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    resp = Response(responseCode=response_code, message='test')
+    resp = Response(responseCode=response_code, messageDEPRECATED='test')
     resp.result = Result(scheduleStatusResult=ScheduleStatusResult(tasks=tasks))
     self._scheduler.getTasksStatus.return_value = resp
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_quota_check.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_quota_check.py b/src/test/python/apache/aurora/client/api/test_quota_check.py
index 9439d99..6c241b7 100644
--- a/src/test/python/apache/aurora/client/api/test_quota_check.py
+++ b/src/test/python/apache/aurora/client/api/test_quota_check.py
@@ -42,7 +42,7 @@ class QuotaCheckTest(unittest.TestCase):
   def mock_get_quota(self, allocated, consumed, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
 
-    resp = Response(responseCode=response_code, message='test')
+    resp = Response(responseCode=response_code, messageDEPRECATED='test')
     resp.result = Result(
         getQuotaResult=GetQuotaResult(
           quota=deepcopy(allocated), prodConsumption=deepcopy(consumed)))

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_restarter.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_restarter.py b/src/test/python/apache/aurora/client/api/test_restarter.py
index 0e00f44..e31fd6d 100644
--- a/src/test/python/apache/aurora/client/api/test_restarter.py
+++ b/src/test/python/apache/aurora/client/api/test_restarter.py
@@ -54,7 +54,7 @@ class TestRestarter(MoxTestBase):
         FakeSchedulerProxy(CLUSTER, self.mock_scheduler, SESSION_KEY), self.mock_instance_watcher)
 
   def mock_restart_instances(self, instances, lock=None):
-    response = Response(responseCode=ResponseCode.OK, message='test')
+    response = Response(responseCode=ResponseCode.OK, messageDEPRECATED='test')
 
     self.mock_scheduler.restartShards(
         JOB.to_thrift(),
@@ -91,7 +91,7 @@ class TestRestarter(MoxTestBase):
           status=ScheduleStatus.RUNNING,
           assignedTask=AssignedTask(task=TaskConfig(), instanceId=i)
       ))
-    response = Response(responseCode=ResponseCode.OK, message='test')
+    response = Response(responseCode=ResponseCode.OK, messageDEPRECATED='test')
     response.result = Result()
     response.result.scheduleStatusResult = ScheduleStatusResult(tasks=tasks)
 
@@ -106,7 +106,7 @@ class TestRestarter(MoxTestBase):
     self.restarter.restart(None)
 
   def mock_status_no_active_task(self):
-    response = Response(responseCode=ResponseCode.INVALID_REQUEST, message='test')
+    response = Response(responseCode=ResponseCode.INVALID_REQUEST, messageDEPRECATED='test')
     self.mock_scheduler.getTasksStatus(IgnoreArg()).AndReturn(response)
 
   def test_restart_no_instance_active(self):
@@ -117,7 +117,7 @@ class TestRestarter(MoxTestBase):
     self.restarter.restart(None)
 
   def mock_restart_fails(self):
-    response = Response(responseCode=ResponseCode.ERROR, message='test error')
+    response = Response(responseCode=ResponseCode.ERROR, messageDEPRECATED='test error')
 
     self.mock_scheduler.restartShards(
         JOB.to_thrift(),
@@ -134,7 +134,7 @@ class TestRestarter(MoxTestBase):
     assert self.restarter.restart(None).responseCode == ResponseCode.ERROR
 
   def mock_restart_watch_fails(self, instances):
-    response = Response(responseCode=ResponseCode.OK, message='test')
+    response = Response(responseCode=ResponseCode.OK, messageDEPRECATED='test')
 
     self.mock_scheduler.restartShards(
         JOB.to_thrift(),

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_sla.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_sla.py b/src/test/python/apache/aurora/client/api/test_sla.py
index f9490b4..f723410 100644
--- a/src/test/python/apache/aurora/client/api/test_sla.py
+++ b/src/test/python/apache/aurora/client/api/test_sla.py
@@ -51,7 +51,7 @@ class SlaTest(unittest.TestCase):
 
   def mock_get_tasks(self, tasks, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    resp = Response(responseCode=response_code, message='test')
+    resp = Response(responseCode=response_code, messageDEPRECATED='test')
     resp.result = Result(scheduleStatusResult=ScheduleStatusResult(tasks=tasks))
     self._scheduler.getTasksStatus.return_value = resp
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/api/test_updater.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/api/test_updater.py b/src/test/python/apache/aurora/client/api/test_updater.py
index 6be419e..ba783da 100644
--- a/src/test/python/apache/aurora/client/api/test_updater.py
+++ b/src/test/python/apache/aurora/client/api/test_updater.py
@@ -156,14 +156,14 @@ class UpdaterTest(TestCase):
 
   def expect_populate(self, job_config, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    resp = Response(responseCode=response_code, message='test')
+    resp = Response(responseCode=response_code, messageDEPRECATED='test')
     result = set([deepcopy(job_config.taskConfig)])
     resp.result = Result(populateJobResult=PopulateJobResult(populated=result))
     self._scheduler.populateJobConfig(job_config).AndReturn(resp)
 
   def expect_get_tasks(self, tasks, ignore_ids=None, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     scheduled = []
     for index, task in enumerate(tasks):
       if not ignore_ids or index not in ignore_ids:
@@ -178,12 +178,12 @@ class UpdaterTest(TestCase):
 
   def expect_cron_replace(self, job_config, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    resp = Response(responseCode=response_code, message='test')
+    resp = Response(responseCode=response_code, messageDEPRECATED='test')
     self._scheduler.replaceCronTemplate(job_config, self._lock, self._session_key).AndReturn(resp)
 
   def expect_restart(self, instance_ids, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     self._scheduler.restartShards(
         self._job_key,
         instance_ids,
@@ -192,7 +192,7 @@ class UpdaterTest(TestCase):
 
   def expect_kill(self, instance_ids, response_code=None, monitor_result=True):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     query = TaskQuery(
         owner=Identity(role=self._job_key.role),
         environment=self._job_key.environment,
@@ -208,7 +208,7 @@ class UpdaterTest(TestCase):
 
   def expect_add(self, instance_ids, task_config, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     add_config = AddInstancesConfig(
         key=self._job_key,
         taskConfig=task_config,
@@ -217,13 +217,13 @@ class UpdaterTest(TestCase):
 
   def expect_start(self, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     response.result = Result(acquireLockResult=AcquireLockResult(lock=self._lock))
     self._scheduler.acquireLock(LockKey(job=self._job_key), self._session_key).AndReturn(response)
 
   def expect_finish(self, response_code=None):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     self._scheduler.releaseLock(
         self._lock,
         LockValidation.CHECKED,
@@ -231,7 +231,7 @@ class UpdaterTest(TestCase):
 
   def expect_quota_check(self, num_released, num_acquired, response_code=None, prod=True):
     response_code = ResponseCode.OK if response_code is None else response_code
-    response = Response(responseCode=response_code, message='test')
+    response = Response(responseCode=response_code, messageDEPRECATED='test')
     released = CapacityRequest(ResourceAggregate(
         numCpus=num_released * self._num_cpus,
         ramMb=num_released * self._num_ram,

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/cli/util.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/cli/util.py b/src/test/python/apache/aurora/client/cli/util.py
index 6533fae..dac4928 100644
--- a/src/test/python/apache/aurora/client/cli/util.py
+++ b/src/test/python/apache/aurora/client/cli/util.py
@@ -95,7 +95,7 @@ class AuroraClientCommandTest(unittest.TestCase):
   def create_blank_response(cls, code, msg):
     response = Mock(spec=Response)
     response.responseCode = code
-    response.message = msg
+    response.messageDEPRECATED = msg
     response.result = Mock(spec=Result)
     return response
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/commands/test_kill.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/commands/test_kill.py b/src/test/python/apache/aurora/client/commands/test_kill.py
index 00c4cef..3e2ac1f 100644
--- a/src/test/python/apache/aurora/client/commands/test_kill.py
+++ b/src/test/python/apache/aurora/client/commands/test_kill.py
@@ -230,7 +230,7 @@ class TestClientKillCommand(AuroraClientCommandTest):
     """
     status_response = Mock(spec=Response)
     status_response.responseCode = ResponseCode.OK
-    status_response.message = "Ok"
+    status_response.messageDEPRECATED = "Ok"
     status_response.result = Mock(spec=Result)
     schedule_status = Mock(spec=ScheduleStatusResult)
     status_response.result.scheduleStatusResult = schedule_status

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/34173d1c/src/test/python/apache/aurora/client/commands/util.py
----------------------------------------------------------------------
diff --git a/src/test/python/apache/aurora/client/commands/util.py b/src/test/python/apache/aurora/client/commands/util.py
index f534684..8478417 100644
--- a/src/test/python/apache/aurora/client/commands/util.py
+++ b/src/test/python/apache/aurora/client/commands/util.py
@@ -28,7 +28,7 @@ class AuroraClientCommandTest(unittest.TestCase):
   def create_blank_response(cls, code, msg):
     response = Mock(spec=Response)
     response.responseCode = code
-    response.message = msg
+    response.messageDEPRECATED = msg
     response.result = Mock(spec=Result)
     return response