Maintenance Primitives: Added maintenance proto helpers.

This includes helpers for constructing (used in tests) and for
validation (used in HTTP endpoints).

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


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

Branch: refs/heads/master
Commit: 8e43aba00165be0718200755c043fcce1b5ee93e
Parents: 691a40e
Author: Joseph Wu <[email protected]>
Authored: Sun Aug 30 13:55:42 2015 -0400
Committer: Joris Van Remoortere <[email protected]>
Committed: Mon Aug 31 13:09:40 2015 -0400

----------------------------------------------------------------------
 src/Makefile.am               |   2 +
 src/common/protobuf_utils.cpp |  58 ++++++++++++++
 src/common/protobuf_utils.hpp |  44 +++++++++++
 src/master/maintenance.cpp    | 157 +++++++++++++++++++++++++++++++++++++
 src/master/maintenance.hpp    |  79 +++++++++++++++++++
 src/master/registrar.hpp      |  10 ++-
 6 files changed, 348 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 3ea3c8d..1b07af4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -439,6 +439,7 @@ libmesos_no_3rdparty_la_SOURCES =                           
        \
        master/detector.cpp                                             \
        master/flags.cpp                                                \
        master/http.cpp                                                 \
+       master/maintenance.cpp                                          \
        master/master.cpp                                               \
        master/metrics.cpp                                              \
        master/registry.hpp                                             \
@@ -727,6 +728,7 @@ libmesos_no_3rdparty_la_SOURCES +=                          
        \
        master/constants.hpp                                            \
        master/detector.hpp                                             \
        master/flags.hpp                                                \
+       master/maintenance.hpp                                          \
        master/master.hpp                                               \
        master/metrics.hpp                                              \
        master/repairer.hpp                                             \

http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/common/protobuf_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/protobuf_utils.cpp b/src/common/protobuf_utils.cpp
index 6b28355..0861270 100644
--- a/src/common/protobuf_utils.cpp
+++ b/src/common/protobuf_utils.cpp
@@ -248,6 +248,64 @@ ContainerState createContainerState(
 
 } // namespace slave {
 
+namespace maintenance {
+
+Unavailability createUnavailability(
+    const process::Time& start,
+    const Option<Duration>& duration)
+{
+  Unavailability unavailability;
+  unavailability.mutable_start()->set_nanoseconds(start.duration().ns());
+
+  if (duration.isSome()) {
+    unavailability.mutable_duration()->set_nanoseconds(duration.get().ns());
+  }
+
+  return unavailability;
+}
+
+
+MachineIDs createMachineList(std::initializer_list<MachineID> ids)
+{
+  MachineIDs array;
+
+  foreach (const MachineID& id, ids) {
+    array.add_values()->CopyFrom(id);
+  }
+
+  return array;
+}
+
+
+mesos::maintenance::Window createWindow(
+    std::initializer_list<MachineID> ids,
+    const Unavailability& unavailability)
+{
+  mesos::maintenance::Window window;
+  window.mutable_unavailability()->CopyFrom(unavailability);
+
+  foreach (const MachineID& id, ids) {
+    window.add_machine_ids()->CopyFrom(id);
+  }
+
+  return window;
+}
+
+
+mesos::maintenance::Schedule createSchedule(
+    std::initializer_list<mesos::maintenance::Window> windows)
+{
+  mesos::maintenance::Schedule schedule;
+
+  foreach (const mesos::maintenance::Window& window, windows) {
+    schedule.add_windows()->CopyFrom(window);
+  }
+
+  return schedule;
+}
+
+} // namespace maintenance {
+
 } // namespace protobuf {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/common/protobuf_utils.hpp
----------------------------------------------------------------------
diff --git a/src/common/protobuf_utils.hpp b/src/common/protobuf_utils.hpp
index 63eeb77..86474ea 100644
--- a/src/common/protobuf_utils.hpp
+++ b/src/common/protobuf_utils.hpp
@@ -19,13 +19,20 @@
 #ifndef __PROTOBUF_UTILS_HPP__
 #define __PROTOBUF_UTILS_HPP__
 
+#include <initializer_list>
 #include <string>
 
 #include <mesos/mesos.hpp>
 
+#include <mesos/maintenance/maintenance.hpp>
+
 #include <mesos/slave/isolator.hpp>
 
+#include <process/time.hpp>
+
+#include <stout/duration.hpp>
 #include <stout/ip.hpp>
+#include <stout/none.hpp>
 #include <stout/option.hpp>
 #include <stout/uuid.hpp>
 
@@ -92,6 +99,43 @@ mesos::slave::ContainerState createContainerState(
 
 } // namespace slave {
 
+namespace maintenance {
+
+/**
+ * Helper for constructing an unavailability from a `Time` and `Duration`.
+ */
+Unavailability createUnavailability(
+    const process::Time& start,
+    const Option<Duration>& duration = None());
+
+
+/**
+ * Helper for constructing a list of `MachineID`.
+ *
+ * TODO(josephw): Remove this when https://reviews.apache.org/r/37826/
+ * is submitted.
+ */
+MachineIDs createMachineList(std::initializer_list<MachineID> ids);
+
+
+/**
+ * Helper for constructing a maintenance `Window`.
+ * See `createUnavailability` above.
+ */
+mesos::maintenance::Window createWindow(
+    std::initializer_list<MachineID> ids,
+    const Unavailability& unavailability);
+
+
+/**
+ * Helper for constructing a maintenance `Schedule`.
+ * See `createWindow` above.
+ */
+mesos::maintenance::Schedule createSchedule(
+    std::initializer_list<mesos::maintenance::Window> windows);
+
+} // namespace maintenance {
+
 } // namespace protobuf {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/master/maintenance.cpp
----------------------------------------------------------------------
diff --git a/src/master/maintenance.cpp b/src/master/maintenance.cpp
new file mode 100644
index 0000000..221dd02
--- /dev/null
+++ b/src/master/maintenance.cpp
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mesos/type_utils.hpp>
+
+#include <mesos/maintenance/maintenance.hpp>
+
+#include <stout/duration.hpp>
+#include <stout/error.hpp>
+#include <stout/hashset.hpp>
+#include <stout/ip.hpp>
+#include <stout/nothing.hpp>
+#include <stout/strings.hpp>
+
+#include "master/maintenance.hpp"
+
+namespace mesos {
+namespace internal {
+namespace master {
+namespace maintenance {
+
+using namespace mesos::maintenance;
+
+namespace validation {
+
+Try<Nothing> schedule(
+    const maintenance::Schedule& schedule,
+    const hashmap<MachineID, MachineInfo>& infos)
+{
+  hashset<MachineID> updated;
+  foreach (const maintenance::Window& window, schedule.windows()) {
+    // Check that each window has at least one machine.
+    if (window.machine_ids().size() == 0) {
+      return Error("List of machines in the maintenance window is empty");
+    }
+
+    // Check the time specified by the unavailability.
+    Try<Nothing> unavailability =
+      maintenance::validation::unavailability(window.unavailability());
+
+    if (unavailability.isError()) {
+      return Error(unavailability.error());
+    }
+
+    // Collect machines from the updated schedule into a set.
+    foreach (const MachineID& id, window.machine_ids()) {
+      // Validate the single machine.
+      Try<Nothing> validId = validation::machine(id);
+      if (validId.isError()) {
+        return Error(validId.error());
+      }
+
+      // Check that the machine is unique.
+      if (updated.contains(id)) {
+        return Error(
+            "Machine '" + id.DebugString() +
+              "' appears more than once in the schedule");
+      }
+
+      updated.insert(id);
+    }
+  }
+
+  // Ensure that no `DOWN` machine is removed from the schedule.
+  foreachpair (const MachineID& id, const MachineInfo& info, infos) {
+    if (info.mode() == MachineInfo::DOWN && !updated.contains(id)) {
+      return Error(
+          "Machine '" + id.DebugString() +
+            "' is deactivated and cannot be removed from the schedule");
+    }
+  }
+
+  return Nothing();
+}
+
+
+Try<Nothing> unavailability(const Unavailability& unavailability)
+{
+  const Duration duration =
+    Nanoseconds(unavailability.duration().nanoseconds());
+
+  // Check the bounds of the unavailability.
+  if (duration < Duration::zero()) {
+    return Error("Unavailability 'duration' is negative");
+  }
+
+  return Nothing();
+}
+
+
+Try<Nothing> machines(const MachineIDs& ids)
+{
+  if (ids.values().size() <= 0) {
+    return Error("List of machines is empty");
+  }
+
+  // Verify that the machine has at least one non-empty field value.
+  hashset<MachineID> uniques;
+  foreach (const MachineID& id, ids.values()) {
+    // Validate the single machine.
+    Try<Nothing> validId = validation::machine(id);
+    if (validId.isError()) {
+      return Error(validId.error());
+    }
+
+    // Check machine uniqueness.
+    if (uniques.contains(id)) {
+      return Error(
+          "Machine '" + id.DebugString() +
+            "' appears more than once in the schedule");
+    }
+
+    uniques.insert(id);
+  }
+
+  return Nothing();
+}
+
+
+Try<Nothing> machine(const MachineID& id)
+{
+  // Verify that the machine has at least one non-empty field value.
+  if (id.hostname().empty() && id.ip().empty()) {
+    return Error("Both 'hostname' and 'ip' for a machine are empty");
+  }
+
+  // Validate the IP field.
+  if (!id.ip().empty()) {
+    Try<net::IP> ip = net::IP::parse(id.ip(), AF_INET);
+    if (ip.isError()) {
+      return Error(ip.error());
+    }
+  }
+
+  return Nothing();
+}
+
+} // namespace validation {
+} // namespace maintenance {
+} // namespace master {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/master/maintenance.hpp
----------------------------------------------------------------------
diff --git a/src/master/maintenance.hpp b/src/master/maintenance.hpp
new file mode 100644
index 0000000..833665e
--- /dev/null
+++ b/src/master/maintenance.hpp
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESOS_MASTER_MAINTENANCE_HPP__
+#define __MESOS_MASTER_MAINTENANCE_HPP__
+
+#include <mesos/mesos.hpp>
+
+#include <mesos/maintenance/maintenance.hpp>
+
+#include <stout/hashset.hpp>
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+#include "master/registrar.hpp"
+#include "master/registry.hpp"
+
+namespace mesos {
+namespace internal {
+namespace master {
+namespace maintenance {
+namespace validation {
+
+/**
+ * Performs the following checks on the new maintenance schedule:
+ *   - Each window in the new schedule has at least one machine.
+ *   - All unavailabilities adhere to the `unavailability` method below.
+ *   - Each machine appears in the schedule once and only once.
+ *   - All currently `DOWN` machines are present in the schedule.
+ *   - All checks in the `machine` method below.
+ */
+Try<Nothing> schedule(
+    const mesos::maintenance::Schedule& schedule,
+    const hashmap<MachineID, MachineInfo>& infos);
+
+
+// Checks that the `duration` of the unavailability is non-negative.
+Try<Nothing> unavailability(
+    const Unavailability& unavailability);
+
+
+/**
+ * Performs the following checks on a list of machines:
+ *   - Each machine appears in the list once and only once.
+ *   - The list is non-empty.
+ *   - All checks in the `machine` method below.
+ */
+Try<Nothing> machines(const MachineIDs& ids);
+
+
+/**
+ * Performs the following checks on a single machine:
+ *   - The machine has at least a hostname or IP.
+ *   - IP is correctly formed.
+ */
+Try<Nothing> machine(const MachineID& id);
+
+} // namespace validation {
+} // namespace maintenance {
+} // namespace master {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MESOS_MASTER_MAINTENANCE_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/8e43aba0/src/master/registrar.hpp
----------------------------------------------------------------------
diff --git a/src/master/registrar.hpp b/src/master/registrar.hpp
index c6a0655..d464f61 100644
--- a/src/master/registrar.hpp
+++ b/src/master/registrar.hpp
@@ -49,8 +49,14 @@ public:
   Operation() : success(false) {}
   virtual ~Operation() {}
 
-  // Attempts to invoke the operation on 'registry' (and the
-  // accumulators, in this case 'slaveIDs').
+  // Attempts to invoke the operation on the registry object.
+  // Aided by accumulator(s):
+  //   slaveIDs - is the set of registered slaves.
+  //
+  // NOTE: the "strict" parameter only applies to operations that
+  // affect slaves (i.e. registration).  See Flags::registry_strict
+  // in master/flags.cpp for more information.
+  //
   // Returns whether the operation mutates 'registry', or an error if
   // the operation cannot be applied successfully.
   Try<bool> operator()(

Reply via email to