Added filesystem isolator tests to test volumes from sandbox. Review: https://reviews.apache.org/r/37237
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/512f2bca Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/512f2bca Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/512f2bca Branch: refs/heads/master Commit: 512f2bca2eb01d64c5c2c1d2f350862eaaae17f5 Parents: f55e36a Author: Jie Yu <[email protected]> Authored: Fri Aug 7 16:29:10 2015 -0700 Committer: Jie Yu <[email protected]> Committed: Wed Aug 12 12:20:56 2015 -0700 ---------------------------------------------------------------------- src/Makefile.am | 2 + .../containerizer/filesystem_isolator_tests.cpp | 239 +++++++++++++++++++ src/tests/containerizer/provisioner.hpp | 105 ++++++++ 3 files changed, 346 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/512f2bca/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index a27cde2..111aed9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -738,6 +738,7 @@ libmesos_no_3rdparty_la_SOURCES += \ tests/containerizer/isolator.hpp \ tests/containerizer/launcher.hpp \ tests/containerizer/memory_test_helper.hpp \ + tests/containerizer/provisioner.hpp \ tests/containerizer/rootfs.hpp \ tests/containerizer/setns_test_helper.hpp \ usage/usage.hpp \ @@ -1637,6 +1638,7 @@ mesos_tests_DEPENDENCIES = # Initialized to allow += below. if OS_LINUX mesos_tests_SOURCES += tests/containerizer/cgroups_isolator_tests.cpp mesos_tests_SOURCES += tests/containerizer/cgroups_tests.cpp + mesos_tests_SOURCES += tests/containerizer/filesystem_isolator_tests.cpp mesos_tests_SOURCES += tests/containerizer/fs_tests.cpp mesos_tests_SOURCES += tests/containerizer/launch_tests.cpp mesos_tests_SOURCES += tests/containerizer/memory_pressure_tests.cpp http://git-wip-us.apache.org/repos/asf/mesos/blob/512f2bca/src/tests/containerizer/filesystem_isolator_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/filesystem_isolator_tests.cpp b/src/tests/containerizer/filesystem_isolator_tests.cpp new file mode 100644 index 0000000..7b1f1a3 --- /dev/null +++ b/src/tests/containerizer/filesystem_isolator_tests.cpp @@ -0,0 +1,239 @@ +/** + * 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 <vector> + +#include <mesos/mesos.hpp> + +#include <process/owned.hpp> +#include <process/gtest.hpp> + +#include <stout/error.hpp> +#include <stout/foreach.hpp> +#include <stout/gtest.hpp> +#include <stout/os.hpp> +#include <stout/path.hpp> +#include <stout/uuid.hpp> + +#ifdef __linux__ +#include "slave/containerizer/linux_launcher.hpp" + +#include "slave/containerizer/isolators/filesystem/linux.hpp" +#endif + +#include "slave/containerizer/mesos/containerizer.hpp" + +#include "tests/flags.hpp" +#include "tests/mesos.hpp" + +#include "tests/containerizer/provisioner.hpp" +#include "tests/containerizer/rootfs.hpp" + +using namespace process; + +using std::string; +using std::vector; + +using mesos::internal::slave::Fetcher; +using mesos::internal::slave::Launcher; +#ifdef __linux__ +using mesos::internal::slave::LinuxFilesystemIsolatorProcess; +using mesos::internal::slave::LinuxLauncher; +#endif +using mesos::internal::slave::MesosContainerizer; +using mesos::internal::slave::Provisioner; +using mesos::internal::slave::Slave; + +using mesos::slave::Isolator; + +namespace mesos { +namespace internal { +namespace tests { + +#ifdef __linux__ +class LinuxFilesystemIsolatorTest: public MesosTest +{ +public: + // This helper creates a MesosContainerizer instance that uses the + // LinuxFilesystemIsolator. The filesystem isolator takes a + // TestProvisioner which provision APPC images by copying files from + // the host filesystem. + Try<Owned<MesosContainerizer>> createContainerizer(const slave::Flags& flags) + { + Try<Owned<Rootfs>> rootfs = + LinuxRootfs::create(path::join(os::getcwd(), "rootfs")); + + if (rootfs.isError()) { + return Error("Failed to create LinuxRootfs: " + rootfs.error()); + } + + // NOTE: TestProvisioner provisions APPC images. + hashmap<Image::Type, Owned<Provisioner>> provisioners; + provisioners.put( + Image::APPC, + Owned<Provisioner>(new TestProvisioner(rootfs.get().share()))); + + Try<Isolator*> _isolator = + LinuxFilesystemIsolatorProcess::create(flags, provisioners); + + if (_isolator.isError()) { + return Error( + "Failed to create LinuxFilesystemIsolatorProcess: " + + _isolator.error()); + } + + Owned<Isolator> isolator(_isolator.get()); + + Try<Launcher*> _launcher = + LinuxLauncher::create(flags, isolator->namespaces().get().get()); + + if (_launcher.isError()) { + return Error("Failed to create LinuxLauncher: " + _launcher.error()); + } + + Owned<Launcher> launcher(_launcher.get()); + + return Owned<MesosContainerizer>( + new MesosContainerizer( + flags, + true, + &fetcher, + launcher, + {isolator})); + } + + ContainerInfo createContainerInfo( + const vector<Volume>& volumes = vector<Volume>(), + bool hasImage = true) + { + ContainerInfo info; + info.set_type(ContainerInfo::MESOS); + + if (hasImage) { + info.mutable_mesos()->mutable_image()->set_type(Image::APPC); + } + + foreach (const Volume& volume, volumes) { + info.add_volumes()->CopyFrom(volume); + } + + return info; + } + +private: + Fetcher fetcher; +}; + + +// This test verifies that the root filesystem of the container is +// properly changed to the one that's provisioned by the provisioner. +TEST_F(LinuxFilesystemIsolatorTest, ROOT_ChangeRootFilesystem) +{ + slave::Flags flags = CreateSlaveFlags(); + + Try<Owned<MesosContainerizer>> containerizer = createContainerizer(flags); + ASSERT_SOME(containerizer); + + ContainerID containerId; + containerId.set_value(UUID::random().toString()); + + ExecutorInfo executor = CREATE_EXECUTOR_INFO( + "test_executor", + "[ ! -d '" + os::getcwd() + "' ]"); + + executor.mutable_container()->CopyFrom(createContainerInfo()); + + string directory = path::join(os::getcwd(), "sandbox"); + ASSERT_SOME(os::mkdir(directory)); + + Future<bool> launch = containerizer.get()->launch( + containerId, + executor, + 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()); +} + + +// This test verifies that a volume with a relative host path is +// properly created in the container's sandbox and is properly mounted +// in the container's mount namespace. +TEST_F(LinuxFilesystemIsolatorTest, ROOT_VolumeFromSandbox) +{ + slave::Flags flags = CreateSlaveFlags(); + + Try<Owned<MesosContainerizer>> containerizer = createContainerizer(flags); + ASSERT_SOME(containerizer); + + ContainerID containerId; + containerId.set_value(UUID::random().toString()); + + ExecutorInfo executor = CREATE_EXECUTOR_INFO( + "test_executor", + "echo abc > /tmp/file"); + + executor.mutable_container()->CopyFrom(createContainerInfo( + {CREATE_VOLUME("/tmp", "tmp", Volume::RW)})); + + string directory = path::join(os::getcwd(), "sandbox"); + ASSERT_SOME(os::mkdir(directory)); + + Future<bool> launch = containerizer.get()->launch( + containerId, + executor, + 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()); + + EXPECT_SOME_EQ("abc\n", os::read(path::join(directory, "tmp", "file"))); +} +#endif // __linux__ + +} // namespace tests { +} // namespace internal { +} // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/512f2bca/src/tests/containerizer/provisioner.hpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/provisioner.hpp b/src/tests/containerizer/provisioner.hpp new file mode 100644 index 0000000..c4ba467 --- /dev/null +++ b/src/tests/containerizer/provisioner.hpp @@ -0,0 +1,105 @@ +/** + * 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 __TEST_PROVISIONER_HPP__ +#define __TEST_PROVISIONER_HPP__ + +#include <gmock/gmock.h> + +#include <process/shared.hpp> + +#include "slave/containerizer/provisioner.hpp" + +#include "tests/containerizer/rootfs.hpp" + +namespace mesos { +namespace internal { +namespace tests { + +class TestProvisioner : public slave::Provisioner +{ +public: + TestProvisioner(const process::Shared<Rootfs>& _rootfs) + : rootfs(_rootfs) + { + using testing::_; + using testing::DoDefault; + using testing::Invoke; + + ON_CALL(*this, recover(_, _)) + .WillByDefault(Invoke(this, &TestProvisioner::unmocked_recover)); + EXPECT_CALL(*this, recover(_, _)) + .WillRepeatedly(DoDefault()); + + ON_CALL(*this, provision(_, _)) + .WillByDefault(Invoke(this, &TestProvisioner::unmocked_provision)); + EXPECT_CALL(*this, provision(_, _)) + .WillRepeatedly(DoDefault()); + + ON_CALL(*this, destroy(_)) + .WillByDefault(Invoke(this, &TestProvisioner::unmocked_destroy)); + EXPECT_CALL(*this, destroy(_)) + .WillRepeatedly(DoDefault()); + } + + MOCK_METHOD2( + recover, + process::Future<Nothing>( + const std::list<mesos::slave::ContainerState>& states, + const hashset<ContainerID>& orphans)); + + MOCK_METHOD2( + provision, + process::Future<std::string>( + const ContainerID& containerId, + const Image& image)); + + MOCK_METHOD1( + destroy, + process::Future<bool>( + const ContainerID& containerId)); + + process::Future<Nothing> unmocked_recover( + const std::list<mesos::slave::ContainerState>& states, + const hashset<ContainerID>& orphans) + { + return Nothing(); + } + + process::Future<std::string> unmocked_provision( + const ContainerID& containerId, + const Image& image) + { + return rootfs->root; + } + + process::Future<bool> unmocked_destroy( + const ContainerID& containerId) + { + return true; + } + +private: + process::Shared<Rootfs> rootfs; +}; + +} // namespace tests { +} // namespace internal { +} // namespace mesos { + +#endif // __TEST_PROVISIONER_HPP__
