LxcDomain class with tests 01/29101/4
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Tue, 21 Oct 2014 09:19:58 +0000 (11:19 +0200)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Wed, 22 Oct 2014 09:51:28 +0000 (11:51 +0200)
[Bug/Feature]   N/A
[Cause]         N/A
[Solution]      N/A
[Verification]  N/A

Change-Id: Ibfd0593d92cb9cd4bc52430e4207b93a1a53ecf8

common/lxc/domain.cpp [new file with mode: 0644]
common/lxc/domain.hpp [new file with mode: 0644]
common/lxc/exception.hpp [new file with mode: 0644]
packaging/security-containers.spec
server/CMakeLists.txt
tests/unit_tests/CMakeLists.txt
tests/unit_tests/lxc/templates/CMakeLists.txt [new file with mode: 0644]
tests/unit_tests/lxc/templates/minimal.sh [new file with mode: 0755]
tests/unit_tests/lxc/ut-domain.cpp [new file with mode: 0644]

diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp
new file mode 100644 (file)
index 0000000..2e70eee
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed 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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Lxc domain
+ */
+
+#include "config.hpp"
+#include "logger/logger.hpp"
+#include "lxc/domain.hpp"
+#include "lxc/exception.hpp"
+
+#include <lxc/lxccontainer.h>
+#include <sys/stat.h>
+
+namespace security_containers {
+namespace lxc {
+
+
+LxcDomain::LxcDomain(const std::string& lxcPath, const std::string& domainName)
+  : mContainer(nullptr)
+{
+    mContainer = lxc_container_new(domainName.c_str(), lxcPath.c_str());
+    if (!mContainer) {
+        LOGE("Could not initialize lxc domain " << domainName << " in path " << lxcPath);
+        throw LxcException("Could not initialize lxc domain");
+    }
+}
+
+LxcDomain::~LxcDomain()
+{
+    lxc_container_put(mContainer);
+}
+
+std::string LxcDomain::getName() const
+{
+    return mContainer->name;
+}
+
+std::string LxcDomain::getConfigItem(const std::string& key)
+{
+    char buffer[1024];
+    int len = mContainer->get_config_item(mContainer, key.c_str(), buffer, sizeof(buffer));
+    if (len < 0) {
+        LOGE("Key '" + key + "' not found in domain " + getName());
+        throw LxcException("Key not found");
+    }
+    return buffer;
+}
+
+bool LxcDomain::isDefined()
+{
+    return mContainer->is_defined(mContainer);
+}
+
+bool LxcDomain::isRunning()
+{
+    return mContainer->is_running(mContainer);
+}
+
+std::string LxcDomain::getState()
+{
+    return mContainer->state(mContainer);
+}
+
+void LxcDomain::create(const std::string& templatePath)
+{
+    if (!mContainer->create(mContainer, templatePath.c_str(), NULL, NULL, 0, NULL)) {
+        LOGE("Could not create domain " + getName());
+        throw LxcException("Could not create domain");
+    }
+}
+
+void LxcDomain::destroy()
+{
+    if (!mContainer->destroy(mContainer)) {
+        LOGE("Could not destroy domain " + getName());
+        throw LxcException("Could not destroy domain");
+    }
+}
+
+void LxcDomain::start(const char* argv[])
+{
+    if (!mContainer->start(mContainer, false, const_cast<char**>(argv))) {
+        LOGE("Could not start domain " + getName());
+        throw LxcException("Could not start domain");
+    }
+}
+
+void LxcDomain::stop()
+{
+    if (!mContainer->stop(mContainer)) {
+        LOGE("Could not stop domain " + getName());
+        throw LxcException("Stop domain failed");
+    }
+}
+
+void LxcDomain::reboot()
+{
+    if (!mContainer->reboot(mContainer)) {
+        LOGE("Could not reboot domain " + getName());
+        throw LxcException("Reboot domain failed");
+    }
+}
+
+void LxcDomain::shutdown(int timeout)
+{
+    if (!mContainer->shutdown(mContainer, timeout)) {
+        LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s");
+        throw LxcException("Shutdown domain failed");
+    }
+}
+
+} // namespace lxc
+} // namespace security_containers
diff --git a/common/lxc/domain.hpp b/common/lxc/domain.hpp
new file mode 100644 (file)
index 0000000..53049a0
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed 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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Lxc domain
+ */
+
+#ifndef COMMON_LXC_DOMAIN_HPP
+#define COMMON_LXC_DOMAIN_HPP
+
+#include <string>
+
+// fwd declaration of lxc internals
+struct lxc_container;
+
+namespace security_containers {
+namespace lxc {
+
+
+/**
+ * A class wwapping lxc container
+ */
+class LxcDomain {
+public:
+    LxcDomain(const std::string& lxcPath, const std::string& domainName);
+    ~LxcDomain();
+
+    LxcDomain(const LxcDomain&) = delete;
+    LxcDomain& operator=(const LxcDomain&) = delete;
+
+    std::string getName() const;
+
+    std::string getConfigItem(const std::string& key);
+
+    bool isDefined();
+    bool isRunning();
+
+    std::string getState();
+
+    void create(const std::string& templatePath);
+    void destroy();
+
+    void start(const char* argv[]);
+    void stop();
+    void reboot();
+    void shutdown(int timeout);
+private:
+    lxc_container* mContainer;
+};
+
+
+} // namespace lxc
+} // namespace security_containers
+
+
+#endif // COMMON_LXC_DOMAIN_HPP
diff --git a/common/lxc/exception.hpp b/common/lxc/exception.hpp
new file mode 100644 (file)
index 0000000..31248b5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed 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
+ */
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Lxc exception
+ */
+
+
+#ifndef COMMON_LXC_EXCEPTION_HPP
+#define COMMON_LXC_EXCEPTION_HPP
+
+#include "base-exception.hpp"
+
+
+namespace security_containers {
+
+
+/**
+ * Base class for exceptions in utils
+ */
+struct LxcException: public SecurityContainersException {
+
+    LxcException(const std::string& error) : SecurityContainersException(error) {}
+};
+
+
+}
+
+
+#endif // COMMON_UTILS_EXCEPTION_HPP
index 137f02b..1ef5da4 100644 (file)
@@ -19,17 +19,15 @@ Group:          Security/Other
 Summary:        Daemon for managing containers
 BuildRequires:  cmake
 BuildRequires:  boost-devel
-BuildRequires:  libvirt-devel
 BuildRequires:  libjson-devel >= 0.10
 BuildRequires:  libcap-ng-devel
+BuildRequires:  lxc-devel
 BuildRequires:  pkgconfig(libConfig)
 BuildRequires:  pkgconfig(libLogger)
 BuildRequires:  pkgconfig(libSimpleDbus)
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(libsystemd-journal)
-BuildRequires:  pkgconfig(libvirt-glib-1.0)
 BuildRequires:  pkgconfig(sqlite3)
-Requires:       libvirt-daemon >= 1.2.4
 Requires(post): libcap-tools
 
 %description
@@ -207,6 +205,7 @@ Group:            Development/Libraries
 Requires:         security-containers = %{version}-%{release}
 Requires:         security-containers-client = %{version}-%{release}
 Requires:         python
+Requires:         python-xml
 Requires:         boost-test
 
 %description tests
@@ -220,6 +219,7 @@ Unit tests for both: server and client and integration tests.
 %attr(755,root,root) %{script_dir}/sc_int_tests.py
 %attr(755,root,root) %{script_dir}/sc_launch_test.py
 %{script_dir}/sc_test_parser.py
-%{_datadir}/security-containers
+%{_datadir}/security-containers/tests
+%attr(755,root,root) %{_datadir}/security-containers/lxc-templates
 %{python_sitelib}/sc_integration_tests
 /etc/dbus-1/system.d/org.tizen.containers.tests.conf
index 3ffefb6..0eab83b 100644 (file)
@@ -29,7 +29,7 @@ ADD_EXECUTABLE(${SERVER_CODENAME} ${project_SRCS} ${common_SRCS})
 
 ## Link libraries ##############################################################
 FIND_PACKAGE(Boost COMPONENTS program_options system filesystem regex)
-PKG_CHECK_MODULES(SERVER_DEPS REQUIRED json gio-2.0 libsystemd-journal
+PKG_CHECK_MODULES(SERVER_DEPS REQUIRED lxc json gio-2.0 libsystemd-journal
                   libcap-ng libLogger libSimpleDbus libConfig)
 
 INCLUDE_DIRECTORIES(${COMMON_FOLDER})
index 939b6c1..38d268a 100644 (file)
@@ -35,7 +35,7 @@ ADD_EXECUTABLE(${UT_SERVER_CODENAME} ${project_SRCS} ${common_SRCS} ${server_SRC
 ## Link libraries ##############################################################
 FIND_PACKAGE (Boost COMPONENTS unit_test_framework system filesystem regex)
 
-PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED json gio-2.0
+PKG_CHECK_MODULES(UT_SERVER_DEPS REQUIRED lxc json gio-2.0
                   libsystemd-journal libcap-ng libLogger libSimpleDbus libConfig)
 INCLUDE_DIRECTORIES(${COMMON_FOLDER} ${SERVER_FOLDER} ${UNIT_TESTS_FOLDER} ${CLIENT_FOLDER})
 INCLUDE_DIRECTORIES(SYSTEM ${UT_SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
@@ -44,12 +44,15 @@ TARGET_LINK_LIBRARIES(${UT_SERVER_CODENAME} ${UT_SERVER_DEPS_LIBRARIES} ${Boost_
 
 ## Subdirectories ##############################################################
 SET(SC_TEST_CONFIG_INSTALL_DIR ${SC_DATA_INSTALL_DIR}/tests)
+SET(SC_TEST_LXC_TEMPLATES_INSTALL_DIR ${SC_DATA_INSTALL_DIR}/lxc-templates/tests)
 ADD_DEFINITIONS(-DSC_TEST_CONFIG_INSTALL_DIR="${SC_TEST_CONFIG_INSTALL_DIR}")
+ADD_DEFINITIONS(-DSC_TEST_LXC_TEMPLATES_INSTALL_DIR="${SC_TEST_LXC_TEMPLATES_INSTALL_DIR}")
 
 ADD_SUBDIRECTORY(dbus/configs)
 ADD_SUBDIRECTORY(server/configs)
 ADD_SUBDIRECTORY(utils/configs)
 ADD_SUBDIRECTORY(client/configs)
+ADD_SUBDIRECTORY(lxc/templates)
 
 
 ## Install #####################################################################
diff --git a/tests/unit_tests/lxc/templates/CMakeLists.txt b/tests/unit_tests/lxc/templates/CMakeLists.txt
new file mode 100644 (file)
index 0000000..139cf49
--- /dev/null
@@ -0,0 +1,23 @@
+# Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#    Licensed 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.
+#
+#
+# @file
+# @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+#
+
+FILE(GLOB LXC_TEMPLATES *.sh)
+
+INSTALL(PROGRAMS     ${LXC_TEMPLATES}
+        DESTINATION  ${SC_TEST_LXC_TEMPLATES_INSTALL_DIR})
diff --git a/tests/unit_tests/lxc/templates/minimal.sh b/tests/unit_tests/lxc/templates/minimal.sh
new file mode 100755 (executable)
index 0000000..64f6da7
--- /dev/null
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+echo UnitTest LXC template, args: $@
+
+options=$(getopt -o p:n: -l rootfs:,path:,name: -- "$@")
+if [ $? -ne 0 ]; then
+    exit 1
+fi
+eval set -- "$options"
+
+while true
+do
+    case "$1" in
+        -p|--path)      path=$2; shift 2;;
+        --rootfs)       rootfs=$2; shift 2;;
+        -n|--name)      name=$2; shift 2;;
+        --)             shift 1; break ;;
+        *)              break ;;
+    esac
+done
+
+# Prepare container rootfs
+ROOTFS_DIRS="\
+${rootfs}/bin \
+${rootfs}/dev \
+${rootfs}/etc \
+${rootfs}/home \
+${rootfs}/lib \
+${rootfs}/lib64 \
+${rootfs}/proc \
+${rootfs}/root \
+${rootfs}/run \
+${rootfs}/sbin \
+${rootfs}/sys \
+${rootfs}/tmp \
+${rootfs}/usr
+"
+/bin/mkdir ${ROOTFS_DIRS}
+
+# Prepare container configuration file
+> ${path}/config
+cat <<EOF >> ${path}/config
+lxc.utsname = ${name}
+lxc.rootfs = ${rootfs}
+
+lxc.haltsignal = SIGTERM
+
+lxc.pts = 256
+lxc.tty = 0
+
+lxc.mount.auto = proc sys cgroup
+lxc.mount.entry = /bin bin none ro,bind 0 0
+lxc.mount.entry = /etc etc none ro,bind 0 0
+lxc.mount.entry = /lib lib none ro,bind 0 0
+lxc.mount.entry = /sbin sbin none ro,bind 0 0
+lxc.mount.entry = /usr usr none ro,rbind 0 0
+lxc.mount.entry = devtmpfs dev devtmpfs rw,relatime,mode=755 0 0
+EOF
+
+if [ "$(uname -m)" = "x86_64" ]; then
+cat <<EOF >> $path/config
+lxc.mount.entry = /lib64 lib64 none ro,bind 0 0
+EOF
+fi
+
diff --git a/tests/unit_tests/lxc/ut-domain.cpp b/tests/unit_tests/lxc/ut-domain.cpp
new file mode 100644 (file)
index 0000000..0e1b863
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  Licensed 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
+ */
+
+
+/**
+ * @file
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Unit tests of LxcDomain class
+ */
+
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "lxc/domain.hpp"
+#include "lxc/exception.hpp"
+
+#include <thread>
+#include <chrono>
+#include <boost/filesystem.hpp>
+
+namespace {
+
+using namespace security_containers;
+using namespace security_containers::lxc;
+namespace fs = boost::filesystem;
+
+const std::string LXC_PATH = "/tmp/ut-lxc/";
+const std::string DOMAIN_NAME = "ut-domain";
+const std::string TEMPLATE = SC_TEST_LXC_TEMPLATES_INSTALL_DIR "/minimal.sh";
+
+struct Fixture {
+    Fixture()
+    {
+        fs::create_directory(LXC_PATH);
+        cleanup();
+    }
+
+    ~Fixture()
+    {
+        cleanup();
+        fs::remove_all(LXC_PATH);
+    }
+
+    void cleanup()
+    {
+        LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+        if (lxc.isDefined()) {
+            if (lxc.isRunning()) {
+                lxc.stop();
+            }
+            lxc.destroy();
+        }
+    }
+};
+
+} // namespace
+
+BOOST_FIXTURE_TEST_SUITE(LxcDomainSuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
+{
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(CreateDestroyTest)
+{
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK(!lxc.isDefined());
+
+    lxc.create(TEMPLATE);
+
+    BOOST_CHECK(lxc.isDefined());
+    BOOST_CHECK_EQUAL(lxc.getConfigItem("lxc.rootfs"), LXC_PATH + DOMAIN_NAME + "/rootfs");
+    BOOST_CHECK_THROW(lxc.getConfigItem("xxx"), LxcException);
+
+    lxc.destroy();
+
+    BOOST_CHECK(!lxc.isDefined());
+}
+
+BOOST_AUTO_TEST_CASE(StartShutdownTest)
+{
+    {
+        LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+        lxc.create(TEMPLATE);
+    }
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+    const char* argv[] = {
+        "/bin/sh",
+        "-c",
+        "trap exit SIGTERM; read",
+        NULL
+    };
+    lxc.start(argv);
+    // wait for bash to be able to trap SIGTERM
+    std::this_thread::sleep_for(std::chrono::milliseconds(200));
+    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
+    lxc.shutdown(2);
+    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+
+    lxc.destroy();
+}
+
+BOOST_AUTO_TEST_CASE(StartStopTest)
+{
+    {
+        LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+        lxc.create(TEMPLATE);
+    }
+    LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+    const char* argv[] = {
+        "/bin/sh",
+        NULL
+    };
+    lxc.start(argv);
+    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
+    BOOST_CHECK_THROW(lxc.shutdown(1), LxcException);
+    BOOST_CHECK_EQUAL("RUNNING", lxc.getState());
+    lxc.stop();
+    BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+
+    lxc.destroy();
+}
+
+BOOST_AUTO_TEST_SUITE_END()