You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2015/07/25 01:37:59 UTC
[06/12] mesos git commit: Moved containerizer related tests under
src/tests/containerizer.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/routing_tests.cpp b/src/tests/containerizer/routing_tests.cpp
new file mode 100644
index 0000000..e4f1bcf
--- /dev/null
+++ b/src/tests/containerizer/routing_tests.cpp
@@ -0,0 +1,1416 @@
+/**
+ * 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 <signal.h>
+#include <unistd.h>
+
+#include <linux/version.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gtest/gtest.h>
+
+#include <process/clock.hpp>
+#include <process/gtest.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/ip.hpp>
+#include <stout/mac.hpp>
+#include <stout/net.hpp>
+#include <stout/stringify.hpp>
+
+#include "linux/routing/handle.hpp"
+#include "linux/routing/route.hpp"
+#include "linux/routing/utils.hpp"
+
+#include "linux/routing/diagnosis/diagnosis.hpp"
+
+#include "linux/routing/filter/basic.hpp"
+#include "linux/routing/filter/handle.hpp"
+#include "linux/routing/filter/icmp.hpp"
+#include "linux/routing/filter/ip.hpp"
+
+#include "linux/routing/link/link.hpp"
+
+#include "linux/routing/queueing/fq_codel.hpp"
+#include "linux/routing/queueing/htb.hpp"
+#include "linux/routing/queueing/ingress.hpp"
+#include "linux/routing/queueing/statistics.hpp"
+
+using namespace process;
+
+using namespace routing;
+using namespace routing::filter;
+using namespace routing::queueing;
+
+using std::endl;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+static const string TEST_VETH_LINK = "veth-test";
+static const string TEST_PEER_LINK = "veth-peer";
+
+
+class RoutingTest : public ::testing::Test
+{
+protected:
+ virtual void SetUp()
+ {
+ ASSERT_SOME(routing::check())
+ << "-------------------------------------------------------------\n"
+ << "We cannot run any routing tests because either your libnl\n"
+ << "library or kernel is not new enough. You can either upgrade,\n"
+ << "or disable this test case\n"
+ << "-------------------------------------------------------------";
+ }
+};
+
+
+// Tests that require setting up virtual ethernet on host.
+class RoutingVethTest : public RoutingTest
+{
+protected:
+ virtual void SetUp()
+ {
+ RoutingTest::SetUp();
+
+ // Clean up the test links, in case it wasn't cleaned up properly
+ // from previous tests.
+ link::remove(TEST_VETH_LINK);
+
+ ASSERT_SOME_FALSE(link::exists(TEST_VETH_LINK));
+ ASSERT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+ }
+
+ virtual void TearDown()
+ {
+ link::remove(TEST_VETH_LINK);
+ }
+};
+
+
+TEST_F(RoutingTest, PortRange)
+{
+ Try<ip::PortRange> ports = ip::PortRange::fromBeginEnd(1, 0);
+ EXPECT_ERROR(ports);
+
+ ports = ip::PortRange::fromBeginEnd(4, 11);
+ EXPECT_ERROR(ports);
+
+ ports = ip::PortRange::fromBeginEnd(4, 7);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(4u, ports.get().begin());
+ EXPECT_EQ(7u, ports.get().end());
+ EXPECT_EQ(0xfffc, ports.get().mask());
+ EXPECT_EQ("[4,7]", stringify(ports.get()));
+
+ ports = ip::PortRange::fromBeginEnd(10, 10);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(10u, ports.get().begin());
+ EXPECT_EQ(10u, ports.get().end());
+ EXPECT_EQ(0xffff, ports.get().mask());
+ EXPECT_EQ("[10,10]", stringify(ports.get()));
+
+ ports = ip::PortRange::fromBeginMask(20, 0xffff);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(20u, ports.get().begin());
+ EXPECT_EQ(20u, ports.get().end());
+ EXPECT_EQ(0xffff, ports.get().mask());
+ EXPECT_EQ("[20,20]", stringify(ports.get()));
+
+ ports = ip::PortRange::fromBeginMask(1024, 0xfff8);
+ ASSERT_SOME(ports);
+ EXPECT_EQ(1024u, ports.get().begin());
+ EXPECT_EQ(1031u, ports.get().end());
+ EXPECT_EQ(0xfff8, ports.get().mask());
+ EXPECT_EQ("[1024,1031]", stringify(ports.get()));
+}
+
+
+TEST_F(RoutingTest, RouteTable)
+{
+ Try<vector<route::Rule> > table = route::table();
+ EXPECT_SOME(table);
+
+ Result<net::IP> gateway = route::defaultGateway();
+ EXPECT_FALSE(gateway.isError());
+}
+
+
+TEST_F(RoutingTest, LinkIndex)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_NE(0, link::index(link));
+ }
+
+ EXPECT_NONE(link::index("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkName)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_NE(0, link::index(link));
+ EXPECT_SOME_EQ(link, link::name(link::index(link).get()));
+ }
+}
+
+
+TEST_F(RoutingTest, LinkStatistics)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ Result<hashmap<string, uint64_t> > statistics = link::statistics(link);
+
+ ASSERT_SOME(statistics);
+ EXPECT_TRUE(statistics.get().contains("rx_packets"));
+ EXPECT_TRUE(statistics.get().contains("rx_bytes"));
+ EXPECT_TRUE(statistics.get().contains("tx_packets"));
+ EXPECT_TRUE(statistics.get().contains("tx_bytes"));
+ }
+
+ EXPECT_NONE(link::statistics("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkExists)
+{
+ Try<set<string> > links = net::links();
+ ASSERT_SOME(links);
+
+ foreach (const string& link, links.get()) {
+ EXPECT_SOME_TRUE(link::exists(link));
+ }
+
+ EXPECT_SOME_FALSE(link::exists("not-exist"));
+}
+
+
+TEST_F(RoutingTest, Eth0)
+{
+ Result<string> eth0 = link::eth0();
+ EXPECT_FALSE(eth0.isError());
+
+ if (eth0.isSome()) {
+ ASSERT_SOME_TRUE(link::exists(eth0.get()));
+ }
+}
+
+
+TEST_F(RoutingTest, Lo)
+{
+ Result<string> lo = link::lo();
+ EXPECT_FALSE(lo.isError());
+
+ if (lo.isSome()) {
+ ASSERT_SOME_TRUE(link::exists(lo.get()));
+ }
+}
+
+
+TEST_F(RoutingTest, INETSockets)
+{
+ Try<vector<diagnosis::socket::Info> > infos =
+ diagnosis::socket::infos(AF_INET, diagnosis::socket::state::ALL);
+
+ EXPECT_SOME(infos);
+
+ foreach (const diagnosis::socket::Info& info, infos.get()) {
+ // Both source and destination IPs should be present since
+ // 'AF_INET' is asked for.
+ EXPECT_SOME(info.sourceIP);
+ EXPECT_SOME(info.destinationIP);
+ }
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+ EXPECT_SOME_NE(0, link::index(TEST_PEER_LINK));
+
+ // Test the case where the veth (with the same name) already exists.
+ EXPECT_SOME_FALSE(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkRemove)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(link::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(link::remove(TEST_PEER_LINK));
+}
+
+
+// An old glibc might not have this symbol.
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000
+#endif
+
+
+// Entry point of the child process (used in clone()).
+static int child(void*)
+{
+ // Wait to be killed.
+ while (true) {
+ sleep(1);
+ }
+
+ // Should not reach here.
+ ABORT("Child process should not reach here");
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
+{
+ // Stack used in the child process.
+ unsigned long long stack[32];
+
+ pid_t pid = ::clone(child, &stack[31], CLONE_NEWNET | SIGCHLD, NULL);
+ ASSERT_NE(-1, pid);
+
+ // In parent process.
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, pid));
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+
+ // The peer should not exist in parent network namespace.
+ EXPECT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+
+ // TODO(jieyu): Enter the child network namespace and make sure that
+ // the TEST_PEER_LINK is there.
+
+ EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+
+ // Kill the child process.
+ ASSERT_NE(-1, kill(pid, SIGKILL));
+
+ // Wait for the child process.
+ int status;
+ EXPECT_NE(-1, waitpid((pid_t) -1, &status, 0));
+ ASSERT_TRUE(WIFSIGNALED(status));
+ EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkWait)
+{
+ AWAIT_READY(link::removed(TEST_VETH_LINK));
+
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ Future<Nothing> removed = link::removed(TEST_VETH_LINK);
+ EXPECT_TRUE(removed.isPending());
+
+ ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+ AWAIT_READY(removed);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetUp)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ EXPECT_SOME_FALSE(link::isUp(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::setUp(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::isUp(TEST_VETH_LINK));
+
+ EXPECT_SOME_FALSE(link::isUp(TEST_PEER_LINK));
+ EXPECT_SOME_TRUE(link::setUp(TEST_PEER_LINK));
+ EXPECT_SOME_TRUE(link::isUp(TEST_PEER_LINK));
+
+ EXPECT_NONE(link::isUp("non-exist"));
+ EXPECT_SOME_FALSE(link::setUp("non-exist"));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ uint8_t bytes[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+
+ EXPECT_SOME_TRUE(link::setMAC(TEST_VETH_LINK, net::MAC(bytes)));
+ EXPECT_SOME_TRUE(link::setMAC(TEST_PEER_LINK, net::MAC(bytes)));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+
+ ASSERT_SOME(mac);
+ EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+ mac = net::mac(TEST_PEER_LINK);
+
+ ASSERT_SOME(mac);
+ EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+ EXPECT_SOME_FALSE(link::setMAC("non-exist", net::MAC(bytes)));
+
+ // Kernel will reject a multicast MAC address.
+ uint8_t multicast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkMTU)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ EXPECT_SOME_TRUE(link::setMTU(TEST_VETH_LINK, 10000));
+
+ Result<unsigned int> mtu = link::mtu(TEST_VETH_LINK);
+ ASSERT_SOME(mtu);
+ EXPECT_EQ(10000u, mtu.get());
+
+ EXPECT_NONE(link::mtu("not-exist"));
+ EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IngressQdisc)
+{
+ // Test for a qdisc on a nonexistent interface should fail.
+ EXPECT_SOME_FALSE(ingress::exists("noSuchInterface"));
+
+ EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ // Interface exists but does not have an ingress qdisc.
+ EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+ // Interfaces without qdisc established no data.
+ EXPECT_NONE(ingress::statistics(TEST_VETH_LINK));
+ EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+ // Try to create an ingress qdisc on a nonexistent interface.
+ EXPECT_ERROR(ingress::create("noSuchInterface"));
+
+ // Create an ingress qdisc on an existing interface.
+ EXPECT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ // Interface exists and has an ingress qdisc.
+ EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+ // Interfaces which exist return at least the core statisitcs.
+ Result<hashmap<string, uint64_t>> stats = ingress::statistics(TEST_VETH_LINK);
+ ASSERT_SOME(stats);
+ EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+ EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+ EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+ EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+ EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+ EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+ EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+ // Interface without qdisc returns no data.
+ EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+ // Try to create a second ingress qdisc on an existing interface.
+ EXPECT_SOME_FALSE(ingress::create(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+ // Remove the ingress qdisc.
+ EXPECT_SOME_TRUE(ingress::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+ // Try to remove it from a nonexistent interface.
+ EXPECT_SOME_FALSE(ingress::remove("noSuchInterface"));
+
+ // Remove the ingress qdisc when it does not exist.
+ EXPECT_SOME_FALSE(ingress::remove(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+ EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_HTBQdisc)
+{
+ // Test for a qdisc on a nonexistent interface should fail.
+ EXPECT_SOME_FALSE(htb::exists("noSuchInterface", EGRESS_ROOT));
+
+ EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ // This test uses a common handle throughout
+ const Handle handle = Handle(1, 0);
+
+ // Interface exists but does not have an htb qdisc.
+ EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Interfaces without qdisc established no data.
+ EXPECT_NONE(htb::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create an htb qdisc on a nonexistent interface.
+ EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, handle));
+
+ // Create an htb qdisc on an existing interface.
+ EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+ // Interface exists and has an htb qdisc.
+ EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Interfaces which exist return at least the core statisitcs.
+ Result<hashmap<string, uint64_t>> stats =
+ htb::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+ ASSERT_SOME(stats);
+ EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+ EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+ EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+ EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+ EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+ EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+ EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+ // Interface without htb qdisc returns no data.
+ EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create a second htb qdisc on an existing interface.
+ EXPECT_SOME_FALSE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+ EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Remove the htb qdisc.
+ EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to remove it from a nonexistent interface.
+ EXPECT_SOME_FALSE(htb::remove("noSuchInterface", EGRESS_ROOT));
+
+ // Remove the htb qdisc when it does not exist.
+ EXPECT_SOME_FALSE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create an htb qdisc on a nonexistent interface and
+ // default handle.
+ EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, None()));
+
+ // Create an htb qdisc on an existing interface.
+ EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+ // Interface exists and has an htb qdisc.
+ EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Remove the htb qdisc.
+ EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodeQdisc)
+{
+ // Test for a qdisc on a nonexistent interface should fail.
+ EXPECT_SOME_FALSE(fq_codel::exists("noSuchInterface", EGRESS_ROOT));
+
+ EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ // This test uses a common handle throughout
+ const Handle handle = Handle(1, 0);
+
+ // Interface exists but does not have an fq_codel qdisc.
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Interfaces without qdisc established no data.
+ EXPECT_NONE(fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create an fq_codel qdisc on a nonexistent interface.
+ EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, handle));
+
+ // Create an fq_codel qdisc on an existing interface.
+ EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+ // Interface exists and has an fq_codel qdisc.
+ EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Interfaces which exist return at least the core statisitcs.
+ Result<hashmap<string, uint64_t>> stats =
+ fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+ ASSERT_SOME(stats);
+ EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+ EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+ EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+ EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+ EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+ EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+ EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+ EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+ // Interface without fq_codel qdisc returns no data.
+ EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create a second fq_codel qdisc on an existing interface.
+ EXPECT_SOME_FALSE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+ EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Remove the fq_codel qdisc.
+ EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to remove it from a nonexistent interface.
+ EXPECT_SOME_FALSE(fq_codel::remove("noSuchInterface", EGRESS_ROOT));
+
+ // Remove the fq_codel qdisc when it does not exist.
+ EXPECT_SOME_FALSE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Try to create an fq_codel qdisc on a nonexistent interface and
+ // default handle.
+ EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, None()));
+
+ // Create an fq_codel qdisc on an existing interface.
+ EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+ // Interface exists and has an fq_codel qdisc.
+ EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+ // Remove the fq_codel qdisc.
+ EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+ EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodelClassifier)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ const Handle handle = Handle(1, 0);
+ ASSERT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ handle,
+ ETH_P_ALL,
+ None(),
+ Handle(handle, 0)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ALL));
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ handle,
+ ETH_P_ARP,
+ None(),
+ Handle(handle, 0)));
+
+ // There is a kernel bug which could cause this test fail. Please
+ // make sure your kernel, if newer than 3.14, has commit:
+ // b057df24a7536cce6c372efe9d0e3d1558afedf4
+ // (https://git.kernel.org/cgit/linux/kernel/git/davem/net.git).
+ // Please fix your kernel if you see this failure.
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ARP));
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ handle,
+ icmp::Classifier(None()),
+ None(),
+ Handle(handle, 0)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ handle,
+ icmp::Classifier(None())));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+ ASSERT_SOME(sourcePorts);
+
+ Try<ip::PortRange> destinationPorts =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+ ASSERT_SOME(destinationPorts);
+
+ ip::Classifier classifier =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts.get(),
+ destinationPorts.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ handle,
+ classifier,
+ None(),
+ Handle(handle, 1)));
+
+ EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, handle, classifier));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreateDuplicated)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ set<string> links;
+ links.insert(TEST_PEER_LINK);
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ None(),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+ EXPECT_SOME_FALSE(basic::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterRemove)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ set<string> links;
+ links.insert(TEST_PEER_LINK);
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ None(),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+ EXPECT_SOME_TRUE(basic::remove(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+ EXPECT_SOME_FALSE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterUpdate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ set<string> links;
+ links.insert(TEST_PEER_LINK);
+
+ EXPECT_SOME_FALSE(basic::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(basic::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+ EXPECT_SOME_TRUE(basic::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ETH_P_ARP,
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip),
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip)));
+
+ Result<vector<icmp::Classifier> > classifiers =
+ icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(1u, classifiers.get().size());
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateDuplicated)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ set<string> links;
+ links.insert(TEST_PEER_LINK);
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ None(),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+
+ EXPECT_SOME_FALSE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ None(),
+ action::Mirror(links)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateMultiple)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ net::IP ip1 = net::IP(0x01020304); // 1.2.3.4
+ net::IP ip2 = net::IP(0x05060708); // 5.6.7.8
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip1),
+ Priority(1, 1),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip2),
+ Priority(1, 2),
+ action::Redirect(TEST_PEER_LINK)));
+
+ Result<vector<icmp::Classifier> > classifiers =
+ icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(2u, classifiers.get().size());
+ EXPECT_SOME_EQ(ip1, classifiers.get().front().destinationIP);
+ EXPECT_SOME_EQ(ip2, classifiers.get().back().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterRemove)
+{
+ ASSERT_SOME(link::create(
+ TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+
+ EXPECT_SOME_TRUE(icmp::remove(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+
+ EXPECT_SOME_FALSE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ set<string> links;
+ links.insert(TEST_PEER_LINK);
+
+ EXPECT_SOME_FALSE(icmp::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(icmp::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+
+ EXPECT_SOME_FALSE(icmp::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(icmp::update(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None()),
+ action::Mirror(links)));
+
+ EXPECT_SOME_TRUE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(None())));
+
+ EXPECT_SOME_FALSE(icmp::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ icmp::Classifier(ip)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts);
+
+ Try<ip::PortRange> destinationPorts =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts);
+
+ ip::Classifier classifier =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts.get(),
+ destinationPorts.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(1u, classifiers.get().size());
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+ EXPECT_SOME_EQ(
+ sourcePorts.get(),
+ classifiers.get().front().sourcePorts);
+
+ EXPECT_SOME_EQ(
+ destinationPorts.get(),
+ classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate2)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ net::IP ip(0x12345678);
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ip::Classifier(None(), ip, None(), None()),
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ ip::Classifier(None(), ip, None(), None())));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(1u, classifiers.get().size());
+ EXPECT_NONE(classifiers.get().front().destinationMAC);
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+ EXPECT_NONE(classifiers.get().front().sourcePorts);
+ EXPECT_NONE(classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateDuplicated)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts);
+
+ Try<ip::PortRange> destinationPorts =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts);
+
+ ip::Classifier classifier =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts.get(),
+ destinationPorts.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+ EXPECT_SOME_FALSE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateMultiple)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts1 =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts1);
+
+ Try<ip::PortRange> destinationPorts1 =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts1);
+
+ Try<ip::PortRange> sourcePorts2 =
+ ip::PortRange::fromBeginEnd(3024, 3025);
+
+ ASSERT_SOME(sourcePorts2);
+
+ Try<ip::PortRange> destinationPorts2 =
+ ip::PortRange::fromBeginEnd(4000, 4003);
+
+ ASSERT_SOME(destinationPorts2);
+
+ ip::Classifier classifier1 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts1.get(),
+ destinationPorts1.get());
+
+ ip::Classifier classifier2 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts2.get(),
+ destinationPorts2.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier1,
+ Priority(2, 1),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier2,
+ Priority(2, 2),
+ action::Redirect(TEST_PEER_LINK)));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ ASSERT_EQ(2u, classifiers.get().size());
+
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+ EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+ EXPECT_SOME_EQ(
+ sourcePorts1.get(),
+ classifiers.get().front().sourcePorts);
+
+ EXPECT_SOME_EQ(
+ destinationPorts1.get(),
+ classifiers.get().front().destinationPorts);
+
+ EXPECT_SOME_EQ(mac.get(), classifiers.get().back().destinationMAC);
+ EXPECT_SOME_EQ(ip, classifiers.get().back().destinationIP);
+
+ EXPECT_SOME_EQ(
+ sourcePorts2.get(),
+ classifiers.get().back().sourcePorts);
+
+ EXPECT_SOME_EQ(
+ destinationPorts2.get(),
+ classifiers.get().back().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterRemove)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts1 =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts1);
+
+ Try<ip::PortRange> destinationPorts1 =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts1);
+
+ Try<ip::PortRange> sourcePorts2 =
+ ip::PortRange::fromBeginEnd(3024, 3025);
+
+ ASSERT_SOME(sourcePorts2);
+
+ Try<ip::PortRange> destinationPorts2 =
+ ip::PortRange::fromBeginEnd(4000, 4003);
+
+ ASSERT_SOME(destinationPorts2);
+
+ ip::Classifier classifier1 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts1.get(),
+ destinationPorts1.get());
+
+ ip::Classifier classifier2 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts2.get(),
+ destinationPorts2.get());
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier1,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier2,
+ None(),
+ action::Redirect(TEST_PEER_LINK)));
+
+ EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+ EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+
+ EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+ EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+
+ Result<vector<ip::Classifier> > classifiers =
+ ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+ ASSERT_SOME(classifiers);
+ EXPECT_EQ(0u, classifiers.get().size());
+}
+
+
+// Test the workaround introduced for MESOS-1617.
+TEST_F(RoutingVethTest, ROOT_HandleGeneration)
+{
+ ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+ EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+ EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+ ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+ Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+ ASSERT_SOME(mac);
+
+ net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+ Try<ip::PortRange> sourcePorts1 =
+ ip::PortRange::fromBeginEnd(1024, 1027);
+
+ ASSERT_SOME(sourcePorts1);
+
+ Try<ip::PortRange> destinationPorts1 =
+ ip::PortRange::fromBeginEnd(2000, 2000);
+
+ ASSERT_SOME(destinationPorts1);
+
+ Try<ip::PortRange> sourcePorts2 =
+ ip::PortRange::fromBeginEnd(3024, 3025);
+
+ ASSERT_SOME(sourcePorts2);
+
+ Try<ip::PortRange> destinationPorts2 =
+ ip::PortRange::fromBeginEnd(4000, 4003);
+
+ ASSERT_SOME(destinationPorts2);
+
+ ip::Classifier classifier1 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts1.get(),
+ destinationPorts1.get());
+
+ ip::Classifier classifier2 =
+ ip::Classifier(
+ mac.get(),
+ ip,
+ sourcePorts2.get(),
+ destinationPorts2.get());
+
+ // Use handle 800:00:fff for the first filter.
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier1,
+ Priority(2, 1),
+ U32Handle(0x800, 0x0, 0xfff),
+ action::Redirect(TEST_PEER_LINK)));
+
+ // With the workaround, this filter should be assigned a handle
+ // different than 800:00:fff.
+ EXPECT_SOME_TRUE(ip::create(
+ TEST_VETH_LINK,
+ ingress::HANDLE,
+ classifier2,
+ Priority(2, 1),
+ action::Redirect(TEST_PEER_LINK)));
+
+ // Try to remove the second filter. If we don't have the workaround,
+ // removing the second filter will return false since the kernel
+ // will find the handle matches the first filter.
+ EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/sched_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/sched_tests.cpp b/src/tests/containerizer/sched_tests.cpp
new file mode 100644
index 0000000..00723d0
--- /dev/null
+++ b/src/tests/containerizer/sched_tests.cpp
@@ -0,0 +1,96 @@
+/**
+ * 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 "linux/sched.hpp"
+
+#include <process/gtest.hpp>
+#include <process/reap.hpp>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+
+using sched::Policy;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// TODO(idownes): Test the priority and preemption behavior for
+// running competing SCHED_OTHER and SCHED_IDLE tasks.
+
+TEST(SchedTest, ROOT_PolicySelf)
+{
+ Try<Policy> original = sched::policy::get();
+ ASSERT_SOME(original);
+
+ Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+ : Policy::OTHER);
+
+ // Change our own scheduling policy.
+ EXPECT_SOME(sched::policy::set(different));
+ EXPECT_SOME_EQ(different, sched::policy::get());
+
+ // Change it back.
+ EXPECT_SOME(sched::policy::set(original.get()));
+ EXPECT_SOME_EQ(original.get(), sched::policy::get());
+}
+
+
+// Change the scheduling policy of a different process (our child).
+TEST(SchedTest, ROOT_PolicyChild)
+{
+ Try<Policy> original = sched::policy::get();
+ ASSERT_SOME(original);
+
+ Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+ : Policy::OTHER);
+
+ pid_t pid = ::fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ // Child.
+ sleep(10);
+
+ ABORT("Child process should not reach here");
+ }
+
+ // Continue in parent.
+ // Check the child has inherited our policy.
+ EXPECT_SOME_EQ(original.get(), sched::policy::get(pid));
+
+ // Check we can change the child's policy.
+ EXPECT_SOME(sched::policy::set(different, pid));
+ EXPECT_SOME_EQ(different, sched::policy::get(pid));
+
+ process::Future<Option<int>> status = process::reap(pid);
+
+ // Kill the child process.
+ ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+ // Wait for the child process.
+ AWAIT_READY(status);
+ ASSERT_SOME(status.get());
+ EXPECT_TRUE(WIFSIGNALED(status.get().get()));
+ EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.cpp b/src/tests/containerizer/setns_test_helper.cpp
new file mode 100644
index 0000000..ec68b08
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.cpp
@@ -0,0 +1,63 @@
+/**
+ * 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 <set>
+#include <string>
+
+#include <stout/foreach.hpp>
+#include <stout/subcommand.hpp>
+#include <stout/try.hpp>
+
+#include "linux/ns.hpp"
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+using std::set;
+using std::string;
+
+const char SetnsTestHelper::NAME[] = "SetnsTestHelper";
+
+int SetnsTestHelper::execute()
+{
+ // Get all the available namespaces.
+ set<string> namespaces = ns::namespaces();
+
+ // Note: /proc has not been remounted so we can look up pid 1's
+ // namespaces, even if we're in a separate pid namespace.
+ foreach (const string& ns, namespaces) {
+ if (ns == "pid") {
+ // ns::setns() does not (currently) support pid namespaces so
+ // this should return an error.
+ Try<Nothing> setns = ns::setns(1, ns);
+ if (!setns.isError()) {
+ return 1;
+ }
+ } else if (ns == "user") {
+ // ns::setns() will also fail with user namespaces, so we skip
+ // for now. See MESOS-3083.
+ continue;
+ } else {
+ Try<Nothing> setns = ns::setns(1, ns);
+ if (!setns.isSome()) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.hpp b/src/tests/containerizer/setns_test_helper.hpp
new file mode 100644
index 0000000..51d6378
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.hpp
@@ -0,0 +1,35 @@
+/**
+ * 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 __SETNS_TEST_HELPER_HPP__
+#define __SETNS_TEST_HELPER_HPP__
+
+#include <stout/subcommand.hpp>
+
+class SetnsTestHelper : public Subcommand
+{
+public:
+ static const char NAME[];
+
+ SetnsTestHelper() : Subcommand(NAME) {}
+
+protected:
+ virtual int execute();
+};
+
+#endif // __SETNS_TEST_HELPER_HPP__
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper_main.cpp b/src/tests/containerizer/setns_test_helper_main.cpp
new file mode 100644
index 0000000..c8e270a
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper_main.cpp
@@ -0,0 +1,30 @@
+/**
+ * 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 <stout/subcommand.hpp>
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+int main(int argc, char** argv)
+{
+ return Subcommand::dispatch(
+ None(),
+ argc,
+ argv,
+ new SetnsTestHelper());
+}
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer_tests.cpp b/src/tests/containerizer_tests.cpp
deleted file mode 100644
index 9508613..0000000
--- a/src/tests/containerizer_tests.cpp
+++ /dev/null
@@ -1,731 +0,0 @@
-/**
- * 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 <list>
-#include <map>
-#include <string>
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include <mesos/mesos.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/strings.hpp>
-
-#include "slave/flags.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/isolator.hpp"
-#include "tests/launcher.hpp"
-#include "tests/mesos.hpp"
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::Launcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::MesosContainerizerProcess;
-using mesos::internal::slave::PosixLauncher;
-using mesos::internal::slave::Provisioner;
-using mesos::internal::slave::Slave;
-
-using mesos::internal::slave::state::ExecutorState;
-using mesos::internal::slave::state::FrameworkState;
-using mesos::internal::slave::state::RunState;
-using mesos::internal::slave::state::SlaveState;
-
-using mesos::slave::ExecutorLimitation;
-using mesos::slave::ExecutorRunState;
-using mesos::slave::Isolator;
-using mesos::slave::IsolatorProcess;
-
-using std::list;
-using std::map;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Invoke;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class MesosContainerizerIsolatorPreparationTest :
- public TemporaryDirectoryTest
-{
-public:
- // Construct a MesosContainerizer with TestIsolator(s) which use the provided
- // 'prepare' command(s).
- Try<MesosContainerizer*> CreateContainerizer(
- Fetcher* fetcher,
- const vector<Option<CommandInfo>>& prepares)
- {
- vector<Owned<Isolator>> isolators;
-
- foreach (const Option<CommandInfo>& prepare, prepares) {
- Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
- if (isolator.isError()) {
- return Error(isolator.error());
- }
-
- isolators.push_back(Owned<Isolator>(isolator.get()));
- }
-
- slave::Flags flags;
- flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
- Try<Launcher*> launcher = PosixLauncher::create(flags);
- if (launcher.isError()) {
- return Error(launcher.error());
- }
-
- return new MesosContainerizer(
- flags,
- false,
- fetcher,
- Owned<Launcher>(launcher.get()),
- isolators,
- hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
- }
-
- Try<MesosContainerizer*> CreateContainerizer(
- Fetcher* fetcher,
- const Option<CommandInfo>& prepare)
- {
- vector<Option<CommandInfo>> prepares;
- prepares.push_back(prepare);
-
- return CreateContainerizer(fetcher, prepares);
- }
-};
-
-
-// The isolator has a prepare command that succeeds.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
-{
- string directory = os::getcwd(); // We're inside a temporary sandbox.
- string file = path::join(directory, "child.script.executed");
-
- Fetcher fetcher;
-
- Try<MesosContainerizer*> containerizer = CreateContainerizer(
- &fetcher,
- CREATE_COMMAND_INFO("touch " + file));
-
- CHECK_SOME(containerizer);
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- CREATE_EXECUTOR_INFO("executor", "exit 0"),
- directory,
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- // Wait until the launch completes.
- AWAIT_READY(launch);
-
- // Wait for the child (preparation script + executor) to complete.
- Future<containerizer::Termination> wait =
- containerizer.get()->wait(containerId);
-
- AWAIT_READY(wait);
-
- // Check the child exited correctly.
- EXPECT_TRUE(wait.get().has_status());
- EXPECT_EQ(0, wait.get().status());
-
- // Check the preparation script actually ran.
- EXPECT_TRUE(os::exists(file));
-
- // Destroy the container.
- containerizer.get()->destroy(containerId);
-
- delete containerizer.get();
-}
-
-
-// The isolator has a prepare command that fails.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
-{
- string directory = os::getcwd(); // We're inside a temporary sandbox.
- string file = path::join(directory, "child.script.executed");
-
- Fetcher fetcher;
-
- Try<MesosContainerizer*> containerizer = CreateContainerizer(
- &fetcher,
- CREATE_COMMAND_INFO("touch " + file + " && exit 1"));
-
- CHECK_SOME(containerizer);
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- CREATE_EXECUTOR_INFO("executor", "exit 0"),
- directory,
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- // Wait until the launch completes.
- AWAIT_READY(launch);
-
- // Wait for the child (preparation script + executor) to complete.
- Future<containerizer::Termination> wait =
- containerizer.get()->wait(containerId);
-
- AWAIT_READY(wait);
-
- // Check the child failed to exit correctly.
- EXPECT_TRUE(wait.get().has_status());
- EXPECT_NE(0, wait.get().status());
-
- // Check the preparation script actually ran.
- EXPECT_TRUE(os::exists(file));
-
- // Destroy the container.
- containerizer.get()->destroy(containerId);
-
- delete containerizer.get();
-}
-
-
-// There are two isolators, one with a prepare command that succeeds
-// and another that fails. The execution order is not defined but the
-// launch should fail from the failing prepare command.
-TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
-{
- string directory = os::getcwd(); // We're inside a temporary sandbox.
- string file1 = path::join(directory, "child.script.executed.1");
- string file2 = path::join(directory, "child.script.executed.2");
-
- vector<Option<CommandInfo>> prepares;
-
- // This isolator prepare command one will succeed if called first,
- // otherwise it won't get run.
- prepares.push_back(CREATE_COMMAND_INFO("touch " + file1 + " && exit 0"));
-
- // This will fail, either first or after the successful command.
- prepares.push_back(CREATE_COMMAND_INFO("touch " + file2 + " && exit 1"));
-
- Fetcher fetcher;
-
- Try<MesosContainerizer*> containerizer =
- CreateContainerizer(&fetcher, prepares);
-
- CHECK_SOME(containerizer);
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- CREATE_EXECUTOR_INFO("executor", "exit 0"),
- directory,
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- // Wait until the launch completes.
- AWAIT_READY(launch);
-
- // Wait for the child (preparation script(s) + executor) to complete.
- Future<containerizer::Termination> wait =
- containerizer.get()->wait(containerId);
- AWAIT_READY(wait);
-
- // Check the child failed to exit correctly.
- EXPECT_TRUE(wait.get().has_status());
- EXPECT_NE(0, wait.get().status());
-
- // Check the failing preparation script has actually ran.
- EXPECT_TRUE(os::exists(file2));
-
- // Destroy the container.
- containerizer.get()->destroy(containerId);
-
- delete containerizer.get();
-}
-
-
-class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
-
-
-TEST_F(MesosContainerizerExecuteTest, IoRedirection)
-{
- string directory = os::getcwd(); // We're inside a temporary sandbox.
-
- slave::Flags flags;
- flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
- Fetcher fetcher;
-
- // Use local=false so std{err,out} are redirected to files.
- Try<MesosContainerizer*> containerizer =
- MesosContainerizer::create(flags, false, &fetcher);
-
- ASSERT_SOME(containerizer);
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- string errMsg = "this is stderr";
- string outMsg = "this is stdout";
- string command =
- "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
-
- Future<bool> launch = containerizer.get()->launch(
- containerId,
- CREATE_EXECUTOR_INFO("executor", command),
- directory,
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- // Wait for the launch to complete.
- AWAIT_READY(launch);
-
- // Wait on the container.
- Future<containerizer::Termination> wait =
- containerizer.get()->wait(containerId);
-
- AWAIT_READY(wait);
-
- // Check the executor exited correctly.
- EXPECT_TRUE(wait.get().has_status());
- EXPECT_EQ(0, wait.get().status());
-
- // Check that std{err, out} was redirected.
- // NOTE: Fetcher uses GLOG, which outputs extra information to
- // stderr.
- Try<string> stderr = os::read(path::join(directory, "stderr"));
- ASSERT_SOME(stderr);
- EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
-
- EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
-
- delete containerizer.get();
-}
-
-
-class MesosContainerizerDestroyTest : public MesosTest {};
-
-
-class MockMesosContainerizerProcess : public MesosContainerizerProcess
-{
-public:
- MockMesosContainerizerProcess(
- const slave::Flags& flags,
- bool local,
- Fetcher* fetcher,
- const Owned<Launcher>& launcher,
- const vector<Owned<Isolator>>& isolators,
- const hashmap<ContainerInfo::Image::Type,
- Owned<Provisioner>>& provisioners)
- : MesosContainerizerProcess(
- flags,
- local,
- fetcher,
- launcher,
- isolators,
- provisioners)
- {
- // NOTE: See TestContainerizer::setup for why we use
- // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
- // 'ON_CALL' and 'WillByDefault'.
- EXPECT_CALL(*this, exec(_, _))
- .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
- }
-
- MOCK_METHOD2(
- exec,
- Future<bool>(
- const ContainerID& containerId,
- int pipeWrite));
-
- Future<bool> _exec(
- const ContainerID& containerId,
- int pipeWrite)
- {
- return MesosContainerizerProcess::exec(
- containerId,
- pipeWrite);
- }
-};
-
-
-class MockIsolator : public mesos::slave::Isolator
-{
-public:
- MockIsolator()
- {
- EXPECT_CALL(*this, watch(_))
- .WillRepeatedly(Return(watchPromise.future()));
-
- EXPECT_CALL(*this, isolate(_, _))
- .WillRepeatedly(Return(Nothing()));
-
- EXPECT_CALL(*this, cleanup(_))
- .WillRepeatedly(Return(Nothing()));
-
- EXPECT_CALL(*this, prepare(_, _, _, _, _))
- .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
- }
-
- MOCK_METHOD2(
- recover,
- Future<Nothing>(
- const list<ExecutorRunState>&,
- const hashset<ContainerID>&));
-
- MOCK_METHOD5(
- prepare,
- Future<Option<CommandInfo>>(
- const ContainerID&,
- const ExecutorInfo&,
- const string&,
- const Option<string>&,
- const Option<string>&));
-
- virtual Future<Option<CommandInfo>> _prepare(
- const ContainerID& containerId,
- const ExecutorInfo& executorInfo,
- const string& directory,
- const Option<string>& rootfs,
- const Option<string>& user)
- {
- return None();
- }
-
- MOCK_METHOD2(
- isolate,
- Future<Nothing>(const ContainerID&, pid_t));
-
- MOCK_METHOD1(
- watch,
- Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
-
- MOCK_METHOD2(
- update,
- Future<Nothing>(const ContainerID&, const Resources&));
-
- MOCK_METHOD1(
- usage,
- Future<ResourceStatistics>(const ContainerID&));
-
- MOCK_METHOD1(
- cleanup,
- Future<Nothing>(const ContainerID&));
-
- Promise<mesos::slave::ExecutorLimitation> watchPromise;
-};
-
-
-// Destroying a mesos containerizer while it is fetching should
-// complete without waiting for the fetching to finish.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
-{
- slave::Flags flags = CreateSlaveFlags();
-
- Try<Launcher*> launcher = PosixLauncher::create(flags);
- ASSERT_SOME(launcher);
-
- Fetcher fetcher;
-
- MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
- flags,
- true,
- &fetcher,
- Owned<Launcher>(launcher.get()),
- vector<Owned<Isolator>>(),
- hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
- Future<Nothing> exec;
- Promise<bool> promise;
-
- // Letting exec hang to simulate a long fetch.
- EXPECT_CALL(*process, exec(_, _))
- .WillOnce(DoAll(FutureSatisfy(&exec),
- Return(promise.future())));
-
- MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- TaskInfo taskInfo;
- CommandInfo commandInfo;
- taskInfo.mutable_command()->MergeFrom(commandInfo);
-
- containerizer.launch(
- containerId,
- taskInfo,
- CREATE_EXECUTOR_INFO("executor", "exit 0"),
- os::getcwd(),
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
- AWAIT_READY(exec);
-
- containerizer.destroy(containerId);
-
- // The container should still exit even if fetch didn't complete.
- AWAIT_READY(wait);
-}
-
-
-// Destroying a mesos containerizer while it is preparing should wait
-// until isolators are finished preparing before destroying.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
-{
- slave::Flags flags = CreateSlaveFlags();
-
- Try<Launcher*> launcher = PosixLauncher::create(flags);
- ASSERT_SOME(launcher);
-
- MockIsolator* isolator = new MockIsolator();
-
- Future<Nothing> prepare;
- Promise<Option<CommandInfo>> promise;
-
- // Simulate a long prepare from the isolator.
- EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
- .WillOnce(DoAll(FutureSatisfy(&prepare),
- Return(promise.future())));
-
- Fetcher fetcher;
-
- MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
- flags,
- true,
- &fetcher,
- Owned<Launcher>(launcher.get()),
- {Owned<Isolator>(isolator)},
- hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
- MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- TaskInfo taskInfo;
- CommandInfo commandInfo;
- taskInfo.mutable_command()->MergeFrom(commandInfo);
-
- containerizer.launch(
- containerId,
- taskInfo,
- CREATE_EXECUTOR_INFO("executor", "exit 0"),
- os::getcwd(),
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
- AWAIT_READY(prepare);
-
- containerizer.destroy(containerId);
-
- // The container should not exit until prepare is complete.
- ASSERT_TRUE(wait.isPending());
-
- // Need to help the compiler to disambiguate between overloads.
- Option<CommandInfo> option = commandInfo;
- promise.set(option);
-
- AWAIT_READY(wait);
-
- containerizer::Termination termination = wait.get();
-
- EXPECT_EQ(
- "Container destroyed while preparing isolators",
- termination.message());
-
- EXPECT_TRUE(termination.killed());
- EXPECT_FALSE(termination.has_status());
-}
-
-
-// This action destroys the container using the real launcher and
-// waits until the destroy is complete.
-ACTION_P(InvokeDestroyAndWait, launcher)
-{
- Future<Nothing> destroy = launcher->real->destroy(arg0);
- AWAIT_READY(destroy);
-}
-
-
-// This test verifies that when a container destruction fails the
-// 'container_destroy_errors' metric is updated.
-TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
-{
- // Create a TestLauncher backed by PosixLauncher.
- slave::Flags flags = CreateSlaveFlags();
-
- Try<Launcher*> launcher_ = PosixLauncher::create(flags);
- ASSERT_SOME(launcher_);
-
- TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
-
- Fetcher fetcher;
-
- MesosContainerizerProcess* process = new MesosContainerizerProcess(
- flags,
- true,
- &fetcher,
- Owned<Launcher>(launcher),
- vector<Owned<Isolator>>(),
- hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
- MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
- ContainerID containerId;
- containerId.set_value("test_container");
-
- TaskInfo taskInfo;
- CommandInfo commandInfo;
- taskInfo.mutable_command()->MergeFrom(commandInfo);
-
- // Destroy the container using the PosixLauncher but return a failed
- // future to the containerizer.
- EXPECT_CALL(*launcher, destroy(_))
- .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
- Return(Failure("Destroy failure"))));
-
- Future<bool> launch = containerizer.launch(
- containerId,
- taskInfo,
- CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
- os::getcwd(),
- None(),
- SlaveID(),
- PID<Slave>(),
- false);
-
- AWAIT_READY(launch);
-
- Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
- containerizer.destroy(containerId);
-
- // The container destroy should fail.
- AWAIT_FAILED(wait);
-
- // We settle the clock here to ensure that the processing of
- // 'MesosContainerizerProcess::__destroy()' is complete and the
- // metric is updated.
- Clock::pause();
- Clock::settle();
- Clock::resume();
-
- // Ensure that the metric is updated.
- JSON::Object metrics = Metrics();
- ASSERT_EQ(
- 1u,
- metrics.values.count("containerizer/mesos/container_destroy_errors"));
- ASSERT_EQ(
- 1u,
- metrics.values["containerizer/mesos/container_destroy_errors"]);
-}
-
-
-class MesosContainerizerRecoverTest : public MesosTest {};
-
-
-// This test checks that MesosContainerizer doesn't recover executors
-// that were started by another containerizer (e.g: Docker).
-TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
-{
- slave::Flags flags = CreateSlaveFlags();
- Fetcher fetcher;
-
- Try<MesosContainerizer*> containerizer =
- MesosContainerizer::create(flags, true, &fetcher);
-
- ASSERT_SOME(containerizer);
-
- ExecutorID executorId;
- executorId.set_value(UUID::random().toString());
-
- ContainerID containerId;
- containerId.set_value(UUID::random().toString());
-
- ExecutorInfo executorInfo;
- executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
-
- ExecutorState executorState;
- executorState.info = executorInfo;
- executorState.latest = containerId;
-
- RunState runState;
- runState.id = containerId;
- executorState.runs.put(containerId, runState);
-
- FrameworkState frameworkState;
- frameworkState.executors.put(executorId, executorState);
-
- SlaveState slaveState;
- FrameworkID frameworkId;
- frameworkId.set_value(UUID::random().toString());
- slaveState.frameworks.put(frameworkId, frameworkState);
-
- Future<Nothing> recover = containerizer.get()->recover(slaveState);
- AWAIT_READY(recover);
-
- Future<hashset<ContainerID>> containers = containerizer.get()->containers();
- AWAIT_READY(containers);
- EXPECT_EQ(0u, containers.get().size());
-
- delete containerizer.get();
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {