You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ne...@apache.org on 2017/07/11 18:03:45 UTC

[4/8] mesos git commit: Added domain to MasterInfo and SlaveInfo.

Added domain to MasterInfo and SlaveInfo.

This means that each master's domain is stored in ZooKeeper, along with
the rest of the MasterInfo protobuf message.

Each agent's domain is stored as part of its checkpointed resources.
Changing the agent's domain requires a full drain of the agent; this
behavior might be relaxed in the future.

Review: https://reviews.apache.org/r/59762/


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

Branch: refs/heads/master
Commit: ac2b2c8dfd03bb5243ac18025b025c1e60af7224
Parents: 5b99d39
Author: Neil Conway <ne...@gmail.com>
Authored: Tue Jul 11 10:43:21 2017 -0700
Committer: Neil Conway <ne...@gmail.com>
Committed: Tue Jul 11 10:43:21 2017 -0700

----------------------------------------------------------------------
 include/mesos/mesos.proto    |  10 +++
 include/mesos/v1/mesos.proto |  10 +++
 src/common/http.cpp          |  33 +++++++++
 src/common/http.hpp          |   1 +
 src/common/type_utils.cpp    |   6 +-
 src/internal/evolve.cpp      |   6 ++
 src/internal/evolve.hpp      |   1 +
 src/master/http.cpp          |  12 ++++
 src/master/master.cpp        |   4 ++
 src/slave/http.cpp           |   4 ++
 src/slave/slave.cpp          |   4 ++
 src/tests/api_tests.cpp      |  34 ++++++---
 src/tests/master_tests.cpp   | 144 +++++++++++++++++++++++++++++++++++++-
 src/tests/mesos.hpp          |  22 ++++++
 src/tests/slave_tests.cpp    |  79 +++++++++++++++++++++
 src/v1/mesos.cpp             |   6 +-
 16 files changed, 362 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 2ee3861..64ec085 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -865,6 +865,10 @@ message MasterInfo {
   // and supersedes the use of `ip`, `port` and `hostname`.
   // Since Mesos 0.24.
   optional Address address = 7;
+
+  // The domain that this master belongs to. All masters in a Mesos
+  // cluster should belong to the same region.
+  optional DomainInfo domain = 8;
 }
 
 
@@ -885,6 +889,12 @@ message SlaveInfo {
   repeated Attribute attributes = 5;
   optional SlaveID id = 6;
 
+  // The domain that this slave belongs to. If the slave's region
+  // differs from the master's region, it will not appear in resource
+  // offers to frameworks that have not enabled the REGION_AWARE
+  // capability.
+  optional DomainInfo domain = 10;
+
   // Slave checkpointing is always enabled in recent Mesos versions;
   // the value of this field is ignored.
   // TODO(joerg84): Remove checkpoint field after deprecation cycle starting

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/include/mesos/v1/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index b143df8..5b8d00d 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -865,6 +865,10 @@ message MasterInfo {
   // and supersedes the use of `ip`, `port` and `hostname`.
   // Since Mesos 0.24.
   optional Address address = 7;
+
+  // The domain that this master belongs to. All masters in a Mesos
+  // cluster should belong to the same region.
+  optional DomainInfo domain = 8;
 }
 
 
@@ -885,6 +889,12 @@ message AgentInfo {
   repeated Attribute attributes = 5;
   optional AgentID id = 6;
 
+  // The domain that this agent belongs to. If the agent's region
+  // differs from the master's region, it will not appear in resource
+  // offers to frameworks that have not enabled the REGION_AWARE
+  // capability.
+  optional DomainInfo domain = 10;
+
   message Capability {
     enum Type {
       // This must be the first enum value in this list, to

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/common/http.cpp
----------------------------------------------------------------------
diff --git a/src/common/http.cpp b/src/common/http.cpp
index fdb591e..7dce4cd 100644
--- a/src/common/http.cpp
+++ b/src/common/http.cpp
@@ -717,6 +717,39 @@ void json(JSON::ObjectWriter* writer, const TaskStatus& status)
 }
 
 
+static void json(
+    JSON::ObjectWriter* writer,
+    const DomainInfo::FaultDomain::RegionInfo& regionInfo)
+{
+  writer->field("name", regionInfo.name());
+}
+
+
+static void json(
+    JSON::ObjectWriter* writer,
+    const DomainInfo::FaultDomain::ZoneInfo& zoneInfo)
+{
+  writer->field("name", zoneInfo.name());
+}
+
+
+static void json(
+    JSON::ObjectWriter* writer,
+    const DomainInfo::FaultDomain& faultDomain)
+{
+    writer->field("region", faultDomain.region());
+    writer->field("zone", faultDomain.zone());
+}
+
+
+void json(JSON::ObjectWriter* writer, const DomainInfo& domainInfo)
+{
+  if (domainInfo.has_fault_domain()) {
+    writer->field("fault_domain", domainInfo.fault_domain());
+  }
+}
+
+
 static void json(JSON::NumberWriter* writer, const Value::Scalar& scalar)
 {
   writer->set(scalar.value());

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/common/http.hpp
----------------------------------------------------------------------
diff --git a/src/common/http.hpp b/src/common/http.hpp
index b7e4a8a..93c9b2e 100644
--- a/src/common/http.hpp
+++ b/src/common/http.hpp
@@ -133,6 +133,7 @@ void json(JSON::ArrayWriter* writer, const Labels& labels);
 void json(JSON::ObjectWriter* writer, const Resources& resources);
 void json(JSON::ObjectWriter* writer, const Task& task);
 void json(JSON::ObjectWriter* writer, const TaskStatus& status);
+void json(JSON::ObjectWriter* writer, const DomainInfo& domainInfo);
 
 namespace authorization {
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/common/type_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.cpp b/src/common/type_utils.cpp
index a43a6c8..031344c 100644
--- a/src/common/type_utils.cpp
+++ b/src/common/type_utils.cpp
@@ -345,7 +345,8 @@ bool operator==(const MasterInfo& left, const MasterInfo& right)
     left.port() == right.port() &&
     left.pid() == right.pid() &&
     left.hostname() == right.hostname() &&
-    left.version() == right.version();
+    left.version() == right.version() &&
+    left.domain() == right.domain();
 }
 
 
@@ -392,7 +393,8 @@ bool operator==(const SlaveInfo& left, const SlaveInfo& right)
     Attributes(left.attributes()) == Attributes(right.attributes()) &&
     left.id() == right.id() &&
     left.checkpoint() == right.checkpoint() &&
-    left.port() == right.port();
+    left.port() == right.port() &&
+    left.domain() == right.domain();
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/internal/evolve.cpp
----------------------------------------------------------------------
diff --git a/src/internal/evolve.cpp b/src/internal/evolve.cpp
index 93196f3..3ac55ac 100644
--- a/src/internal/evolve.cpp
+++ b/src/internal/evolve.cpp
@@ -86,6 +86,12 @@ v1::AgentInfo evolve(const SlaveInfo& slaveInfo)
 }
 
 
+v1::DomainInfo evolve(const DomainInfo& domainInfo)
+{
+  return evolve<v1::DomainInfo>(domainInfo);
+}
+
+
 v1::ExecutorID evolve(const ExecutorID& executorId)
 {
   return evolve<v1::ExecutorID>(executorId);

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/internal/evolve.hpp
----------------------------------------------------------------------
diff --git a/src/internal/evolve.hpp b/src/internal/evolve.hpp
index 9db5fe6..42ead34 100644
--- a/src/internal/evolve.hpp
+++ b/src/internal/evolve.hpp
@@ -58,6 +58,7 @@ namespace internal {
 // Helpers for evolving types between versions. Please add as necessary!
 v1::AgentID evolve(const SlaveID& slaveId);
 v1::AgentInfo evolve(const SlaveInfo& slaveInfo);
+v1::DomainInfo evolve(const DomainInfo& domainInfo);
 v1::ExecutorID evolve(const ExecutorID& executorId);
 v1::ExecutorInfo evolve(const ExecutorInfo& executorInfo);
 v1::FileInfo evolve(const FileInfo& fileInfo);

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index dbf3d0f..69f7561 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -161,6 +161,10 @@ static void json(JSON::ObjectWriter* writer, const MasterInfo& info)
   writer->field("pid", info.pid());
   writer->field("port", info.port());
   writer->field("hostname", info.hostname());
+
+  if (info.has_domain()) {
+    writer->field("domain", info.domain());
+  }
 }
 
 
@@ -170,6 +174,10 @@ static void json(JSON::ObjectWriter* writer, const SlaveInfo& slaveInfo)
   writer->field("hostname", slaveInfo.hostname());
   writer->field("port", slaveInfo.port());
   writer->field("attributes", Attributes(slaveInfo.attributes()));
+
+  if (slaveInfo.has_domain()) {
+    writer->field("domain", slaveInfo.domain());
+  }
 }
 
 namespace internal {
@@ -2828,6 +2836,10 @@ Future<Response> Master::Http::state(
         writer->field("deactivated_slaves", master->_slaves_inactive());
         writer->field("unreachable_slaves", master->_slaves_unreachable());
 
+        if (master->info().has_domain()) {
+          writer->field("domain", master->info().domain());
+        }
+
         // TODO(haosdent): Deprecated this in favor of `leader_info` below.
         if (master->leader.isSome()) {
           writer->field("leader", master->leader->pid());

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 7668749..39b2fea 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -358,6 +358,10 @@ Master::Master(
   info_.mutable_address()->set_ip(stringify(self().address.ip));
   info_.mutable_address()->set_port(self().address.port);
   info_.mutable_address()->set_hostname(hostname);
+
+  if (flags.domain.isSome()) {
+    info_.mutable_domain()->CopyFrom(flags.domain.get());
+  }
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 700871e..3070b3b 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -1302,6 +1302,10 @@ Future<Response> Http::state(
         writer->field("hostname", slave->info.hostname());
         writer->field("capabilities", AGENT_CAPABILITIES());
 
+        if (slave->info.has_domain()) {
+          writer->field("domain", slave->info.domain());
+        }
+
         const Resources& totalResources = slave->totalResources;
 
         writer->field("resources", totalResources);

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/slave/slave.cpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.cpp b/src/slave/slave.cpp
index a1a6b64..beb0c79 100644
--- a/src/slave/slave.cpp
+++ b/src/slave/slave.cpp
@@ -575,6 +575,10 @@ void Slave::initialize()
   // Checkpointing of slaves is always enabled.
   info.set_checkpoint(true);
 
+  if (flags.domain.isSome()) {
+    info.mutable_domain()->CopyFrom(flags.domain.get());
+  }
+
   LOG(INFO) << "Agent hostname: " << info.hostname();
 
   statusUpdateManager->initialize(defer(self(), &Slave::forward, lambda::_1)

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/tests/api_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/api_tests.cpp b/src/tests/api_tests.cpp
index cdaa724..a460e56 100644
--- a/src/tests/api_tests.cpp
+++ b/src/tests/api_tests.cpp
@@ -145,7 +145,10 @@ INSTANTIATE_TEST_CASE_P(
 
 TEST_P(MasterAPITest, GetAgents)
 {
-  Try<Owned<cluster::Master>> master = this->StartMaster();
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.domain = createDomainInfo("region-abc", "zone-123");
+
+  Try<Owned<cluster::Master>> master = this->StartMaster(masterFlags);
   ASSERT_SOME(master);
 
   Owned<MasterDetector> detector = master.get()->createDetector();
@@ -154,10 +157,11 @@ TEST_P(MasterAPITest, GetAgents)
   Future<SlaveRegisteredMessage> agentRegisteredMessage =
     FUTURE_PROTOBUF(SlaveRegisteredMessage(), master.get()->pid, _);
 
-  slave::Flags flags = CreateSlaveFlags();
-  flags.hostname = "host";
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.hostname = "host";
+  slaveFlags.domain = createDomainInfo("region-xyz", "zone-456");
 
-  Try<Owned<cluster::Slave>> agent = StartSlave(detector.get(), flags);
+  Try<Owned<cluster::Slave>> agent = StartSlave(detector.get(), slaveFlags);
   ASSERT_SOME(agent);
 
   AWAIT_READY(agentRegisteredMessage);
@@ -179,6 +183,7 @@ TEST_P(MasterAPITest, GetAgents)
       v1Response->get_agents().agents(0);
 
   ASSERT_EQ("host", v1Agent.agent_info().hostname());
+  ASSERT_EQ(evolve(slaveFlags.domain.get()), v1Agent.agent_info().domain());
   ASSERT_EQ(agent.get()->pid, v1Agent.pid());
   ASSERT_TRUE(v1Agent.active());
   ASSERT_EQ(MESOS_VERSION, v1Agent.version());
@@ -930,7 +935,10 @@ TEST_P(MasterAPITest, GetRoles)
 
 TEST_P(MasterAPITest, GetMaster)
 {
-  Try<Owned<cluster::Master>> master = this->StartMaster();
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.domain = createDomainInfo("region-abc", "zone-123");
+
+  Try<Owned<cluster::Master>> master = this->StartMaster(masterFlags);
   ASSERT_SOME(master);
 
   v1::master::Call v1Call;
@@ -944,8 +952,12 @@ TEST_P(MasterAPITest, GetMaster)
   AWAIT_READY(v1Response);
   ASSERT_TRUE(v1Response->IsInitialized());
   ASSERT_EQ(v1::master::Response::GET_MASTER, v1Response->type());
-  ASSERT_EQ(master.get()->getMasterInfo().ip(),
-            v1Response->get_master().master_info().ip());
+
+  const mesos::v1::MasterInfo& masterInfo =
+    v1Response->get_master().master_info();
+
+  ASSERT_EQ(evolve(masterFlags.domain.get()), masterInfo.domain());
+  ASSERT_EQ(master.get()->getMasterInfo().ip(), masterInfo.ip());
 }
 
 
@@ -3439,6 +3451,7 @@ TEST_P(AgentAPITest, GetAgent)
 
   slave::Flags flags = CreateSlaveFlags();
   flags.hostname = "host";
+  flags.domain = createDomainInfo("region-xyz", "zone-456");
 
   StandaloneMasterDetector detector;
   Try<Owned<cluster::Slave>> slave = this->StartSlave(&detector, flags);
@@ -3461,8 +3474,11 @@ TEST_P(AgentAPITest, GetAgent)
   AWAIT_READY(v1Response);
   ASSERT_TRUE(v1Response->IsInitialized());
   ASSERT_EQ(v1::agent::Response::GET_AGENT, v1Response->type());
-  ASSERT_EQ(flags.hostname,
-            v1Response->get_agent().agent_info().hostname());
+
+  const mesos::v1::AgentInfo& agentInfo = v1Response->get_agent().agent_info();
+
+  ASSERT_EQ(flags.hostname, agentInfo.hostname());
+  ASSERT_EQ(evolve(flags.domain.get()), agentInfo.domain());
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index 9cfa510..ac33e93 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -800,6 +800,144 @@ TEST_F(MasterTest, StatusUpdateAck)
 }
 
 
+// This test checks that domain information is correctly returned by
+// the master's HTTP endpoints.
+TEST_F(MasterTest, DomainEndpoints)
+{
+  const string MASTER_REGION = "region-abc";
+  const string MASTER_ZONE = "zone-123";
+
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.domain = createDomainInfo(MASTER_REGION, MASTER_ZONE);
+
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  const string AGENT_REGION = "region-xyz";
+  const string AGENT_ZONE = "zone-456";
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+  slaveFlags.domain = createDomainInfo(AGENT_REGION, AGENT_ZONE);
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  StandaloneMasterDetector detector(master.get()->pid);
+  Try<Owned<cluster::Slave>> slave = StartSlave(&detector, slaveFlags);
+  ASSERT_SOME(slave);
+
+  AWAIT_READY(slaveRegisteredMessage);
+
+  // Query the "/state" master endpoint.
+  {
+    Future<Response> response = process::http::get(
+        master.get()->pid,
+        "state",
+        None(),
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+    Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
+    ASSERT_SOME(parse);
+
+    Result<JSON::String> masterRegion = parse->find<JSON::String>(
+        "domain.fault_domain.region.name");
+    Result<JSON::String> masterZone = parse->find<JSON::String>(
+        "domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(MASTER_REGION), masterRegion);
+    EXPECT_SOME_EQ(JSON::String(MASTER_ZONE), masterZone);
+
+    Result<JSON::String> leaderRegion = parse->find<JSON::String>(
+        "leader_info.domain.fault_domain.region.name");
+    Result<JSON::String> leaderZone = parse->find<JSON::String>(
+        "leader_info.domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(MASTER_REGION), leaderRegion);
+    EXPECT_SOME_EQ(JSON::String(MASTER_ZONE), leaderZone);
+
+    Result<JSON::String> agentRegion = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.region.name");
+    Result<JSON::String> agentZone = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(AGENT_REGION), agentRegion);
+    EXPECT_SOME_EQ(JSON::String(AGENT_ZONE), agentZone);
+  }
+
+  // Query the "/state-summary" master endpoint.
+  {
+    Future<Response> response = process::http::get(
+        master.get()->pid,
+        "state-summary",
+        None(),
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+    Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
+    ASSERT_SOME(parse);
+
+    Result<JSON::String> agentRegion = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.region.name");
+    Result<JSON::String> agentZone = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(AGENT_REGION), agentRegion);
+    EXPECT_SOME_EQ(JSON::String(AGENT_ZONE), agentZone);
+  }
+
+  // Query the "/slaves" master endpoint.
+  {
+    Future<Response> response = process::http::get(
+        master.get()->pid,
+        "slaves",
+        None(),
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+    Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
+    ASSERT_SOME(parse);
+
+    Result<JSON::String> agentRegion = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.region.name");
+    Result<JSON::String> agentZone = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(AGENT_REGION), agentRegion);
+    EXPECT_SOME_EQ(JSON::String(AGENT_ZONE), agentZone);
+  }
+
+  // Query the "/state" agent endpoint.
+  {
+    Future<Response> response = process::http::get(
+        slave.get()->pid,
+        "state",
+        None(),
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+    Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
+    ASSERT_SOME(parse);
+
+    Result<JSON::String> agentRegion = parse->find<JSON::String>(
+        "domain.fault_domain.region.name");
+    Result<JSON::String> agentZone = parse->find<JSON::String>(
+        "domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(AGENT_REGION), agentRegion);
+    EXPECT_SOME_EQ(JSON::String(AGENT_ZONE), agentZone);
+  }
+}
+
+
 TEST_F(MasterTest, RecoverResources)
 {
   master::Flags masterFlags = CreateMasterFlags();
@@ -1110,7 +1248,10 @@ TEST_F(MasterTest, MultipleExecutors)
 
 TEST_F(MasterTest, MasterInfo)
 {
-  Try<Owned<cluster::Master>> master = StartMaster();
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.domain = createDomainInfo("region-abc", "zone-xyz");
+
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
   ASSERT_SOME(master);
 
   Owned<MasterDetector> detector = master.get()->createDetector();
@@ -1131,6 +1272,7 @@ TEST_F(MasterTest, MasterInfo)
   driver.start();
 
   AWAIT_READY(masterInfo);
+  EXPECT_EQ(masterFlags.domain, masterInfo->domain());
   EXPECT_EQ(master.get()->pid.address.port, masterInfo->port());
   EXPECT_EQ(
       master.get()->pid.address.ip,

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 06b22f9..2cb3239 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -1019,6 +1019,21 @@ inline hashmap<std::string, double> convertToHashmap(
 }
 
 
+// Helper to create DomainInfo.
+template <typename TDomainInfo>
+inline TDomainInfo createDomainInfo(
+    const std::string& regionName,
+    const std::string& zoneName)
+{
+  TDomainInfo domain;
+
+  domain.mutable_fault_domain()->mutable_region()->set_name(regionName);
+  domain.mutable_fault_domain()->mutable_zone()->set_name(zoneName);
+
+  return domain;
+}
+
+
 // Helpers for creating offer operations.
 template <typename TResources, typename TOffer>
 inline typename TOffer::Operation RESERVE(const TResources& resources)
@@ -1330,6 +1345,13 @@ inline hashmap<std::string, double> convertToHashmap(Args&&... args)
 
 
 template <typename... Args>
+inline DomainInfo createDomainInfo(Args&&... args)
+{
+  return common::createDomainInfo<DomainInfo>(std::forward<Args>(args)...);
+}
+
+
+template <typename... Args>
 inline Offer::Operation RESERVE(Args&&... args)
 {
   return common::RESERVE<Resources, Offer>(std::forward<Args>(args)...);

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/tests/slave_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_tests.cpp b/src/tests/slave_tests.cpp
index 8a69cc2..035db18 100644
--- a/src/tests/slave_tests.cpp
+++ b/src/tests/slave_tests.cpp
@@ -7476,6 +7476,85 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(SlaveTest, ExecutorReregistrationTimeoutFlag)
   driver.join();
 }
 
+
+// This test checks that if an agent is shutdown gracefully, then its
+// domain is configured and the agent is restarted, the agent restarts
+// successfully. Note that shutting down the agent gracefully (killing
+// all tasks) is necessary, because changing the agent's domain is an
+// incompatible change to its SlaveInfo.
+TEST_F(SlaveTest, ChangeDomain)
+{
+  Clock::pause();
+
+  master::Flags masterFlags = CreateMasterFlags();
+  masterFlags.domain = createDomainInfo("region-abc", "zone-123");
+
+  Try<Owned<cluster::Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  slave::Flags slaveFlags = CreateSlaveFlags();
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage1 =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  StandaloneMasterDetector detector(master.get()->pid);
+  Try<Owned<cluster::Slave>> slave1 = StartSlave(&detector, slaveFlags);
+  ASSERT_SOME(slave1);
+
+  Clock::advance(slaveFlags.registration_backoff_factor);
+  AWAIT_READY(slaveRegisteredMessage1);
+
+  // Gracefully shutdown the agent.
+  slave1.get()->shutdown();
+
+  // Restart the agent with a domain. We use the same `slave::Flags`,
+  // so the new instance of the agent uses the same `work_dir`.
+  const string AGENT_REGION = "region-abc";
+  const string AGENT_ZONE = "zone-456";
+
+  slaveFlags.domain = createDomainInfo(AGENT_REGION, AGENT_ZONE);
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage2 =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  Try<Owned<cluster::Slave>> slave2 = StartSlave(&detector, slaveFlags);
+  ASSERT_SOME(slave2);
+
+  Clock::advance(slaveFlags.registration_backoff_factor);
+  AWAIT_READY(slaveRegisteredMessage2);
+
+  // The agent should be assigned a new AgentID.
+  EXPECT_NE(slaveRegisteredMessage1->slave_id(),
+            slaveRegisteredMessage2->slave_id());
+
+  // Check that the new agent domain is correctly reflected in the
+  // master's HTTP endpoints.
+  {
+    Future<Response> response = process::http::get(
+        master.get()->pid,
+        "slaves",
+        None(),
+        createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+    AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+    Try<JSON::Object> parse = JSON::parse<JSON::Object>(response->body);
+    ASSERT_SOME(parse);
+
+    JSON::Array slaves = parse->values["slaves"].as<JSON::Array>();
+    ASSERT_EQ(1u, slaves.values.size());
+
+    Result<JSON::String> agentRegion = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.region.name");
+    Result<JSON::String> agentZone = parse->find<JSON::String>(
+        "slaves[0].domain.fault_domain.zone.name");
+
+    EXPECT_SOME_EQ(JSON::String(AGENT_REGION), agentRegion);
+    EXPECT_SOME_EQ(JSON::String(AGENT_ZONE), agentZone);
+  }
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/ac2b2c8d/src/v1/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/v1/mesos.cpp b/src/v1/mesos.cpp
index 13f336c..e5143e1 100644
--- a/src/v1/mesos.cpp
+++ b/src/v1/mesos.cpp
@@ -339,7 +339,8 @@ bool operator==(const MasterInfo& left, const MasterInfo& right)
     left.port() == right.port() &&
     left.pid() == right.pid() &&
     left.hostname() == right.hostname() &&
-    left.version() == right.version();
+    left.version() == right.version() &&
+    left.domain() == right.domain();
 }
 
 
@@ -385,7 +386,8 @@ bool operator==(const AgentInfo& left, const AgentInfo& right)
     Resources(left.resources()) == Resources(right.resources()) &&
     Attributes(left.attributes()) == Attributes(right.attributes()) &&
     left.id() == right.id() &&
-    left.port() == right.port();
+    left.port() == right.port() &&
+    left.domain() == right.domain();
 }