Added `linux/devices` isolator whitelist tests. Added test to verify that the `linux/devices` isolator supports populating devices that are whitelisted by the `allowed_devices` agent flag.
Review: https://reviews.apache.org/r/67145/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/ee6c6cfc Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/ee6c6cfc Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/ee6c6cfc Branch: refs/heads/master Commit: ee6c6cfcbe2b91eaf540afa38bab4521d23b747f Parents: 68db3f9 Author: James Peach <[email protected]> Authored: Fri May 25 13:38:14 2018 -0700 Committer: James Peach <[email protected]> Committed: Fri May 25 13:38:14 2018 -0700 ---------------------------------------------------------------------- src/Makefile.am | 1 + src/tests/CMakeLists.txt | 1 + .../linux_devices_isolator_tests.cpp | 231 +++++++++++++++++++ 3 files changed, 233 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/ee6c6cfc/src/Makefile.am ---------------------------------------------------------------------- diff --git a/src/Makefile.am b/src/Makefile.am index da0d683..b7184ce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2666,6 +2666,7 @@ mesos_tests_SOURCES += \ tests/containerizer/cgroups_tests.cpp \ tests/containerizer/cni_isolator_tests.cpp \ tests/containerizer/docker_volume_isolator_tests.cpp \ + tests/containerizer/linux_devices_isolator_tests.cpp \ tests/containerizer/linux_filesystem_isolator_tests.cpp \ tests/containerizer/fs_tests.cpp \ tests/containerizer/memory_pressure_tests.cpp \ http://git-wip-us.apache.org/repos/asf/mesos/blob/ee6c6cfc/src/tests/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 1fef060..b9c906d 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -224,6 +224,7 @@ if (LINUX) containerizer/docker_volume_isolator_tests.cpp containerizer/fs_tests.cpp containerizer/linux_capabilities_isolator_tests.cpp + containerizer/linux_devices_isolator_tests.cpp containerizer/linux_filesystem_isolator_tests.cpp containerizer/memory_pressure_tests.cpp containerizer/nested_mesos_containerizer_tests.cpp http://git-wip-us.apache.org/repos/asf/mesos/blob/ee6c6cfc/src/tests/containerizer/linux_devices_isolator_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/linux_devices_isolator_tests.cpp b/src/tests/containerizer/linux_devices_isolator_tests.cpp new file mode 100644 index 0000000..efaa43b --- /dev/null +++ b/src/tests/containerizer/linux_devices_isolator_tests.cpp @@ -0,0 +1,231 @@ +// 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 <map> +#include <ostream> +#include <string> + +#include <process/future.hpp> +#include <process/gtest.hpp> +#include <process/owned.hpp> + +#include <stout/gtest.hpp> +#include <stout/option.hpp> +#include <stout/path.hpp> +#include <stout/strings.hpp> + +#include "common/parse.hpp" + +#include "tests/cluster.hpp" +#include "tests/mesos.hpp" + +#include "slave/containerizer/mesos/containerizer.hpp" + +#include "tests/containerizer/docker_archive.hpp" + +using process::Future; +using process::Owned; + +using std::map; +using std::ostream; +using std::string; + +using mesos::internal::slave::Containerizer; +using mesos::internal::slave::Fetcher; +using mesos::internal::slave::MesosContainerizer; + +using mesos::slave::ContainerTermination; + +namespace mesos { +namespace internal { +namespace tests { + +struct DevicesTestParam +{ + DevicesTestParam( + const string& _containerCheck, + const string& _allowedDevices) + : containerCheck(_containerCheck), + allowedDevices(_allowedDevices) {} + + const string containerCheck; + const string allowedDevices; +}; + + +ostream& operator<<(ostream& stream, const DevicesTestParam& param) +{ + return stream << param.containerCheck; +} + + +class LinuxDevicesIsolatorTest + : public MesosTest, + public ::testing::WithParamInterface<DevicesTestParam> +{ +public: + LinuxDevicesIsolatorTest() + : param(GetParam()) {} + + DevicesTestParam param; +}; + + +TEST_P(LinuxDevicesIsolatorTest, ROOT_PopulateWhitelistedDevices) +{ + // Verify that all the necessary devices are present on the host. + // All reasonable Linux configuration should have these devices. + ASSERT_TRUE(os::exists("/dev/kmsg")); + ASSERT_TRUE(os::exists("/dev/loop-control")); + + slave::Flags flags = CreateSlaveFlags(); + + flags.isolation = "linux/devices,filesystem/linux,docker/runtime"; + flags.docker_registry = path::join(sandbox.get(), "registry"); + flags.docker_store_dir = path::join(sandbox.get(), "store"); + flags.image_providers = "docker"; + + flags.allowed_devices = + flags::parse<DeviceWhitelist>(param.allowedDevices).get(); + + AWAIT_READY(DockerArchive::create(flags.docker_registry, "test_image")); + + Fetcher fetcher(flags); + + Try<MesosContainerizer*> create = + MesosContainerizer::create(flags, true, &fetcher); + + ASSERT_SOME(create); + + Owned<Containerizer> containerizer(create.get()); + + ContainerID containerId; + containerId.set_value(id::UUID::random().toString()); + + ExecutorInfo executor = createExecutorInfo( + "test_executor", + strings::join(";", "ls -l /dev", param.containerCheck)); + + executor.mutable_container()->CopyFrom(createContainerInfo("test_image")); + + string directory = path::join(flags.work_dir, "sandbox"); + ASSERT_SOME(os::mkdir(directory)); + + // Launch the container check command as the non-root user. All the + // check commands are testing for device file access, but root will + // always have access. + Future<Containerizer::LaunchResult> launch = containerizer->launch( + containerId, + createContainerConfig( + None(), executor, directory, os::getenv("SUDO_USER").get()), + map<string, string>(), + None()); + + AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); + + Future<Option<ContainerTermination>> wait = containerizer->wait(containerId); + + AWAIT_READY(wait); + ASSERT_SOME(wait.get()); + ASSERT_TRUE(wait->get().has_status()); + EXPECT_WEXITSTATUS_EQ(0, wait->get().status()); +} + + +INSTANTIATE_TEST_CASE_P( + DevicesTestParam, + LinuxDevicesIsolatorTest, + ::testing::Values( + // Test that /dev/loop-control is a character device and that + // /dev/kmsg doesn't exist. The latter test ensures that we + // won't succeed by accidentally running on the host. + DevicesTestParam( + "test -c /dev/loop-control && test ! -e /dev/kmsg", + R"~({ + "allowed_devices": [ + { + "device": { + "path": "/dev/loop-control" + }, + "access": {} + } + ] + })~"), + // Test that a device in a subdirectory is populated. + DevicesTestParam( + "test -r /dev/cpu/0/cpuid", + R"~({ + "allowed_devices": [ + { + "device": { + "path": "/dev/cpu/0/cpuid" + }, + "access": { + "read": true + } + } + ] + })~"), + // Test that read-only devices are populated in read-only mode. + DevicesTestParam( + "test -r /dev/loop-control && test ! -w /dev/loop-control", + R"~({ + "allowed_devices": [ + { + "device": { + "path": "/dev/loop-control" + }, + "access": { + "read": true + } + } + ] + })~"), + // Test that write-only devices are populated in write-only mode. + DevicesTestParam( + "test -w /dev/loop-control && test ! -r /dev/loop-control", + R"~({ + "allowed_devices": [ + { + "device": { + "path": "/dev/loop-control" + }, + "access": { + "write": true + } + } + ] + })~"), + // Test that read-write devices are populated in read-write mode. + DevicesTestParam( + "test -w /dev/loop-control && test -r /dev/loop-control", + R"~({ + "allowed_devices": [ + { + "device": { + "path": "/dev/loop-control" + }, + "access": { + "read": true, + "write": true + } + } + ] + })~"))); + +} // namespace tests { +} // namespace internal { +} // namespace mesos {
