Repository: mesos Updated Branches: refs/heads/master 8b4d83aec -> 99cbe69eb
Implemented `delegate` method. Review: https://reviews.apache.org/r/51769/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/99cbe69e Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/99cbe69e Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/99cbe69e Branch: refs/heads/master Commit: 99cbe69eb4dd7e5bcac7a5df4134d1af9e4a00b8 Parents: 8b4d83a Author: Avinash sridharan <avin...@mesosphere.io> Authored: Wed Oct 12 16:04:43 2016 -0700 Committer: Jie Yu <yujie....@gmail.com> Committed: Wed Oct 12 17:52:46 2016 -0700 ---------------------------------------------------------------------- .../cni/plugins/port_mapper/port_mapper.cpp | 154 ++++++++++++++++++- 1 file changed, 148 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/99cbe69e/src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp b/src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp index 0473645..2ff8b0e 100644 --- a/src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp +++ b/src/slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.cpp @@ -25,11 +25,19 @@ #include "slave/containerizer/mesos/isolators/network/cni/spec.hpp" #include "slave/containerizer/mesos/isolators/network/cni/plugins/port_mapper/port_mapper.hpp" +namespace io = process::io; + +using std::cerr; +using std::endl; +using std::map; using std::string; +using std::tuple; using std::vector; +using process::Failure; using process::Future; using process::Owned; +using process::Subprocess; using mesos::NetworkInfo; @@ -180,19 +188,23 @@ Try<Owned<PortMapper>, PluginError> PortMapper::create(const string& _cniConfig) // 'delegate' as it should be a valid CNI config JSON. // Make sure the 'delegate' plugin exists. - Result<JSON::String> delegatePlugin = + Result<JSON::String> _delegatePlugin = _delegateConfig->find<JSON::String>("type"); - if (!delegatePlugin.isSome()) { + if (!_delegatePlugin.isSome()) { return PluginError( "Failed to get the delegate plugin 'type'" + - (delegatePlugin.isError() ? delegatePlugin.error() : "Not found"), + (_delegatePlugin.isError() ? _delegatePlugin.error() : "Not found"), ERROR_BAD_ARGS); } - if (os::which(delegatePlugin->value, cniPath.get()).isNone()) { + Option<string> delegatePlugin = os::which( + _delegatePlugin->value, + cniPath.get()); + + if (delegatePlugin.isNone()) { return PluginError( - "Could not find the delegate plugin '" + delegatePlugin->value + + "Could not find the delegate plugin '" + _delegatePlugin->value + "' in '" + cniPath.get() + "'", ERROR_BAD_ARGS); } @@ -211,7 +223,7 @@ Try<Owned<PortMapper>, PluginError> PortMapper::create(const string& _cniConfig) cniArgs, cniPath.get(), networkInfo.get(), - delegatePlugin->value, + delegatePlugin.get(), delegateConfig, chain->value, excludeDevices)); @@ -226,6 +238,136 @@ Try<Option<string>, PluginError> PortMapper::execute() Result<spec::NetworkInfo> PortMapper::delegate(const string& command) { + map<std::string, std::string> environment; + + environment["CNI_COMMAND"] = command; + environment["CNI_IFNAME"] = cniIfName; + environment["CNI_NETNS"] = cniNetNs; + environment["CNI_PATH"] = cniPath; + + if (cniContainerId.isSome()) { + environment["CNI_CONTAINERID"] = cniContainerId.get(); + } + + if (cniArgs.isSome()) { + environment["CNI_ARGS"] = cniArgs.get(); + } + + // Some CNI plugins need to run "iptables" to set up IP Masquerade, + // so we need to set the "PATH" environment variable so that the + // plugin can locate the "iptables" executable file. + Option<string> value = os::getenv("PATH"); + if (value.isSome()) { + environment["PATH"] = value.get(); + } else { + environment["PATH"] = + "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; + } + + Try<string> temp = os::mktemp(); + if (temp.isError()) { + return Error("Failed to create the temp file: " + temp.error()); + } + + Try<Nothing> write = os::write( + temp.get(), + stringify(delegateConfig)); + + if (write.isError()) { + os::rm(temp.get()); + return Error("Failed to write the temp file: " + write.error()); + } + + Try<Subprocess> s = process::subprocess( + delegatePlugin, + {delegatePlugin}, + Subprocess::PATH(temp.get()), + Subprocess::PIPE(), + Subprocess::PIPE(), + nullptr, + environment); + + if (s.isError()) { + return Error( + "Failed to exec the '" + delegatePlugin + + "' subprocess: " + s.error()); + } + + auto result = await( + s->status(), + io::read(s->out().get()), + io::read(s->err().get())); + + result.await(); + + // At this point the subprocess has completed and hence we need to + // remove the `temp` file that we created. + os::rm(temp.get()); + + if (!result.isReady()) { + return Error( + "Failed to wait for exec of delegate CNI plugin '" + + delegatePlugin + "': " + + (result.isDiscarded() ? "discarded" : result.failure())); + } + + Future<Option<int>> status = std::get<0>(result.get()); + if (!status.isReady()) { + return Error( + "Failed to get the exit status of the delegate CNI plugin '" + + delegatePlugin + "' subprocess: " + + (status.isFailed() ? status.failure() : "discarded")); + } + + if (status->isNone()) { + return Error( + "Failed to reap the delegate CNI plugin '" + + delegatePlugin + "' subprocess"); + } + + // CNI plugin will print result or error to stdout. + Future<string> output = std::get<1>(result.get()); + if (!output.isReady()) { + return Error( + "Failed to read stdout from the delegate CNI plugin '" + + delegatePlugin + "' subprocess: " + + (output.isFailed() ? output.failure() : "discarded")); + } + + // We are reading stderr of the plugin since any log messages from + // the CNI plugin would be thrown on stderr. This can be useful for + // debugging issues when the plugin throws a `spec::Error`. + Future<string> err = std::get<2>(result.get()); + if (!err.isReady()) { + return Error( + "Failed to read STDERR from the delegate CNI plugin '" + + delegatePlugin + "' subprocess: " + + (err.isFailed() ? err.failure() : "discarded")); + } + + if (status.get() != 0) { + cerr << "Delegate plugin reported error: " << err.get() << endl; + + return Error( + "The delegate CNI plugin '" + delegatePlugin + + "' return status " + stringify(status->get()) + + ". Could not attach container: " + output.get()); + } + + if (command == spec::CNI_CMD_ADD) { + // Parse the output of CNI plugin. + Try<spec::NetworkInfo> parse = spec::parseNetworkInfo(output.get()); + if (parse.isError()) { + return Error( + "Failed to parse the output of the delegate CNI plugin '" + + delegatePlugin + "': " + parse.error()); + } + + return parse.get(); + } + + // For `spec::CNI_CMD_DEL` CNI plugins don't return a result. They + // will report an error if any, which should be captured above. return None(); }