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()(
