#include <lxc/lxccontainer.h>
#include <sys/stat.h>
+#include <map>
+
namespace security_containers {
namespace lxc {
+namespace {
+ const std::map<std::string, LxcDomain::State> STATE_MAP = {
+ {"STOPPED", LxcDomain::State::STOPPED},
+ {"STARTING", LxcDomain::State::STARTING},
+ {"RUNNING", LxcDomain::State::RUNNING},
+ {"STOPPING", LxcDomain::State::STOPPING},
+ {"ABORTING", LxcDomain::State::ABORTING},
+ {"FREEZING", LxcDomain::State::FREEZING},
+ {"FROZEN", LxcDomain::State::FROZEN},
+ {"THAWED", LxcDomain::State::THAWED}
+ };
+} // namespace
LxcDomain::LxcDomain(const std::string& lxcPath, const std::string& domainName)
: mContainer(nullptr)
return mContainer->is_defined(mContainer);
}
-bool LxcDomain::isRunning()
-{
- return mContainer->is_running(mContainer);
-}
-
-std::string LxcDomain::getState()
+LxcDomain::State LxcDomain::getState()
{
- return mContainer->state(mContainer);
+ const std::string str = mContainer->state(mContainer);
+ return STATE_MAP.at(str);
}
-void LxcDomain::create(const std::string& templatePath)
+bool 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");
+ return false;
}
+ return true;
}
-void LxcDomain::destroy()
+bool LxcDomain::destroy()
{
if (!mContainer->destroy(mContainer)) {
LOGE("Could not destroy domain " + getName());
- throw LxcException("Could not destroy domain");
+ return false;
}
+ return true;
}
-void LxcDomain::start(const char* const* argv)
+bool LxcDomain::start(const char* const* argv)
{
+ if (mContainer->is_running(mContainer)) {
+ LOGE("Already started " + getName());
+ return false;
+ }
+ if (!mContainer->want_daemonize(mContainer, true)) {
+ LOGE("Could not configure domain " + getName());
+ return false;
+ }
if (!mContainer->start(mContainer, false, const_cast<char* const*>(argv))) {
LOGE("Could not start domain " + getName());
- throw LxcException("Could not start domain");
+ return false;
}
+ return true;
}
-void LxcDomain::stop()
+bool LxcDomain::stop()
{
if (!mContainer->stop(mContainer)) {
LOGE("Could not stop domain " + getName());
- throw LxcException("Stop domain failed");
+ return false;
}
+ return true;
}
-void LxcDomain::reboot()
+bool LxcDomain::reboot()
{
if (!mContainer->reboot(mContainer)) {
LOGE("Could not reboot domain " + getName());
- throw LxcException("Reboot domain failed");
+ return false;
}
+ return true;
}
-void LxcDomain::shutdown(int timeout)
+bool LxcDomain::shutdown(int timeout)
{
if (!mContainer->shutdown(mContainer, timeout)) {
LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s");
- throw LxcException("Shutdown domain failed");
+ return false;
}
+ return true;
}
+bool LxcDomain::freeze()
+{
+ if (!mContainer->freeze(mContainer)) {
+ LOGE("Could not freeze domain " + getName());
+ return false;
+ }
+ return true;
+}
+
+bool LxcDomain::unfreeze()
+{
+ if (!mContainer->unfreeze(mContainer)) {
+ LOGE("Could not unfreeze domain " + getName());
+ return false;
+ }
+ return true;
+}
+
+
} // namespace lxc
} // namespace security_containers
*/
class LxcDomain {
public:
+ enum class State {
+ STOPPED,
+ STARTING,
+ RUNNING,
+ STOPPING,
+ ABORTING,
+ FREEZING,
+ FROZEN,
+ THAWED
+ };
+
LxcDomain(const std::string& lxcPath, const std::string& domainName);
~LxcDomain();
std::string getConfigItem(const std::string& key);
bool isDefined();
- bool isRunning();
- std::string getState();
+ State getState();
+
+ bool create(const std::string& templatePath);
+ bool destroy();
- void create(const std::string& templatePath);
- void destroy();
+ bool start(const char* const* argv);
+ bool stop();
+ bool reboot();
+ bool shutdown(int timeout);
- void start(const char* const* argv);
- void stop();
- void reboot();
- void shutdown(int timeout);
+ bool freeze();
+ bool unfreeze();
private:
lxc_container* mContainer;
};
%attr(755,root,root) %{_bindir}/security-containers-server
%dir /etc/security-containers
%dir /etc/security-containers/containers
+%dir /etc/security-containers/lxc-templates
%config /etc/security-containers/daemon.conf
%config /etc/security-containers/containers/*.conf
+%attr(755,root,root) /etc/security-containers/lxc-templates/*.sh
%{_unitdir}/security-containers.service
%{_unitdir}/multi-user.target.wants/security-containers.service
/etc/dbus-1/system.d/org.tizen.containers.host.conf
MESSAGE(STATUS "Installing configs to " ${SC_CONFIG_INSTALL_DIR})
FILE(GLOB container_CONF containers/*.conf)
+FILE(GLOB admin_CONF lxc-templates/*.sh)
## Generate ####################################################################
CONFIGURE_FILE(systemd/security-containers.service.in
INSTALL(FILES ${container_CONF}
DESTINATION ${SC_CONFIG_INSTALL_DIR}/containers)
+INSTALL(PROGRAMS ${admin_CONF}
+ DESTINATION ${SC_CONFIG_INSTALL_DIR}/lxc-templates)
+
INSTALL(FILES ${CMAKE_BINARY_DIR}/systemd/security-containers.service
DESTINATION ${SYSTEMD_UNIT_DIR})
{
"name" : "business",
- "lxcTemplate" : "",
+ "lxcTemplate" : "business.sh",
"initWithArgs" : [],
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 10000,
{
"name" : "private",
- "lxcTemplate" : "",
+ "lxcTemplate" : "private.sh",
"initWithArgs" : [],
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 10000,
"runMountPointPrefix" : "/var/run/containers",
"foregroundId" : "private",
"defaultId" : "private",
- "lxcTemplatePrefix" : "TODO",
- "inputConfig" : {"enabled" : true,
+ "lxcTemplatePrefix" : "/etc/security-containers/lxc-templates",
+ "inputConfig" : {"enabled" : false,
"device" : "gpio_keys.6",
"code" : 139,
"numberOfEvents" : 1,
--- /dev/null
+#!/bin/bash
+
+echo 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
+
+# XXX assume rootfs if mounted from iso
+
+# 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 = /var/run/containers/business/run var/run none rw,bind 0 0
+EOF
+
--- /dev/null
+#!/bin/bash
+
+echo 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
+
+# XXX assume rootfs if mounted from iso
+
+# 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 = /var/run/containers/private/run var/run none rw,bind 0 0
+EOF
+
#include "exception.hpp"
#include "logger/logger.hpp"
-#include "utils/fs.hpp"
#include "utils/paths.hpp"
-#include "utils/latch.hpp"
-#include "utils/callback-wrapper.hpp"
#include <cassert>
-#include <cstring>
-#include <string>
-#include <memory>
-#include <cstdint>
namespace security_containers {
namespace {
// TODO: this should be in container's configuration file
-const int SHUTDOWN_WAIT = 10 * 1000;
+const int SHUTDOWN_WAIT = 10;
class Args {
public:
: mConfig(config),
mDom(containersPath, config.name),
mId(mDom.getName()),
- mDetachOnExit(false),
- mLifecycleCallbackId(-1),
- mRebootCallbackId(-1),
- mNextIdForListener(1)
+ mDetachOnExit(false)
{
LOGD(mId << ": Instantiating ContainerAdmin object");
if (!mDom.isDefined()) {
- std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate, lxcTemplatePrefix);
+ const std::string lxcTemplate = utils::getAbsolutePath(config.lxcTemplate,
+ lxcTemplatePrefix);
LOGI(mId << ": Creating domain from template: " << lxcTemplate);
- mDom.create(lxcTemplate);
+ if (!mDom.create(lxcTemplate)) {
+ throw ContainerOperationException("Could not create domain");
+ }
}
-
-// // ContainerAdmin owns those callbacks
-// mLifecycleCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
-// mDom.get(),
-// VIR_DOMAIN_EVENT_ID_LIFECYCLE,
-// VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtLifecycleCallback),
-// utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
-// &utils::deleteCallbackWrapper<ContainerAdmin*>);
-//
-// if (mLifecycleCallbackId < 0) {
-// LOGE(mId << ": Failed to register a libvirt lifecycle callback");
-// throw ContainerOperationException(mId + ": Failed to register a libvirt lifecycle callback");
-// }
-//
-// LOGT(mId << ": registered lifecycle callback");
-//
-// mRebootCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
-// mDom.get(),
-// VIR_DOMAIN_EVENT_ID_REBOOT,
-// VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtRebootCallback),
-// utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
-// &utils::deleteCallbackWrapper<ContainerAdmin*>);
-//
-// if (mRebootCallbackId < 0) {
-// LOGE(mId << ": Failed to register a libvirt reboot callback");
-// virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-// mLifecycleCallbackId);
-// throw ContainerOperationException(mId + ": Failed to register a libvirt reboot callback");
-// }
-//
-// LOGT(mId << ": registered reboot callback");
}
{
LOGD(mId << ": Destroying ContainerAdmin object...");
-// // Deregister callbacks
-// if (mLifecycleCallbackId >= 0) {
-// virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-// mLifecycleCallbackId);
-// }
-// if (mRebootCallbackId >= 0) {
-// virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
-// mRebootCallbackId);
-// }
-//
- // Try to forcefully stop
if (!mDetachOnExit) {
- try {
- destroy();
- } catch (ServerException&) {
- LOGE(mId << ": Failed to destroy the container");
+ // Try to forcefully stop
+ if (!mDom.stop()) {
+ LOGE(mId << ": Failed to stop the container");
}
}
return;
}
- Args args(mConfig.initWithArgs);
+ const Args args(mConfig.initWithArgs);
+ bool result;
if (args.empty()) {
- mDom.start(NULL);
+ result = mDom.start(NULL);
} else {
LOGD(mId << ": Init: " << args);
- mDom.start(args.getAsCArray());
+ result = mDom.start(args.getAsCArray());
+ }
+
+ if (!result) {
+ throw ContainerOperationException("Could not start container");
}
-// // In order to update daemon without shutting down the containers
-// // autodestroy option must NOT be set. It's best to create domain
-// // without any flags.
-// u_int flags = VIR_DOMAIN_NONE;
-//
-// if (virDomainCreateWithFlags(mDom.get(), flags) < 0) {
-// LOGE(mId << ": Failed to start the container\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
LOGD(mId << ": Started");
}
return;
}
- mDom.stop();
+ if (!mDom.shutdown(SHUTDOWN_WAIT)) {
+ // force stop
+ if (!mDom.stop()) {
+ throw ContainerOperationException("Could not stop container");
+ }
+ }
-// utils::Latch stoppedOccured;
-//
-// LifecycleListener setStopped = [&](const int eventId, const int detailId) {
-// if (eventId == VIR_DOMAIN_EVENT_STOPPED) {
-// if (detailId != VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-// LOGW(mId << ": shutdown requested, but the container stopped with a different status: "
-// << libvirt::libvirtEventDetailToString(eventId, detailId));
-// }
-// stoppedOccured.set();
-// }
-// };
-//
-// ListenerId id = registerLifecycleListener(setStopped, nullptr);
-// shutdown();
-// bool stopped = stoppedOccured.wait(SHUTDOWN_WAIT);
-// removeListener(id);
-//
-// if (!stopped) {
-// LOGW(mId << ": Gracefull shutdown timed out, the container is still running, destroying");
-// destroy();
-// }
-//
LOGD(mId << ": Stopping procedure ended");
}
void ContainerAdmin::destroy()
{
- LOGD(mId << ": Destroying...");
- if (isStopped()) {
- LOGD(mId << ": Already crashed/down/off - nothing to do");
- return;
- }
-
- mDom.stop();//TODO
-
-// setSchedulerLevel(SchedulerLevel::FOREGROUND);
-//
-// // Forceful termination of the guest
-// u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
-//
-// if (virDomainDestroyFlags(mDom.get(), flags) < 0) {
-// LOGE(mId << ": Error while destroying the container:\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
- LOGD(mId << ": Destroyed");
-}
+ LOGD(mId << ": Destroying procedure started...");
-
-void ContainerAdmin::shutdown()
-{
- LOGD(mId << ": Shutting down...");
- if (isStopped()) {
- LOGD(mId << ": Already crashed/down/off - nothing to do");
- return;
+ if (!mDom.destroy()) {
+ throw ContainerOperationException("Could not destroy container");
}
- mDom.stop(); //TODO
-
-// setSchedulerLevel(SchedulerLevel::FOREGROUND);
-//
-// if (virDomainShutdownFlags(mDom.get(), VIR_DOMAIN_SHUTDOWN_SIGNAL) < 0) {
-// LOGE(mId << ": Error while shutting down the container:\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
- LOGD(mId << ": Shut down initiated (async)");
+ LOGD(mId << ": Destroying procedure ended");
}
bool ContainerAdmin::isRunning()
{
- return mDom.isRunning();
+ return mDom.getState() == lxc::LxcDomain::State::RUNNING;
}
bool ContainerAdmin::isStopped()
{
- return !mDom.isRunning();//TODO
+ return mDom.getState() == lxc::LxcDomain::State::STOPPED;
}
void ContainerAdmin::suspend()
{
-// assert(mDom);
-//
-// LOGD(mId << ": Pausing...");
-// if (isPaused()) {
-// LOGD(mId << ": Already paused - nothing to do...");
-// return;
-// }
-//
-// if (virDomainSuspend(mDom.get()) < 0) {
-// LOGE(mId << ": Error while suspending the container:\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
-// LOGD(mId << ": Paused");
+ LOGD(mId << ": Pausing...");
+ if (!mDom.freeze()) {
+ throw ContainerOperationException("Could not pause container");
+ }
+ LOGD(mId << ": Paused");
}
void ContainerAdmin::resume()
{
-// assert(mDom);
-//
-// LOGD(mId << ": Resuming...");
-// if (!isPaused()) {
-// LOGD(mId << ": Is not paused - nothing to do...");
-// return;
-// }
-//
-// if (virDomainResume(mDom.get()) < 0) {
-// LOGE(mId << ": Error while resuming the container:\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
-// LOGD(mId << ": Resumed");
+ LOGD(mId << ": Resuming...");
+ if (!mDom.unfreeze()) {
+ throw ContainerOperationException("Could not resume container");
+ }
+ LOGD(mId << ": Resumed");
}
bool ContainerAdmin::isPaused()
{
-// return getState() == VIR_DOMAIN_PAUSED;
- return false;//TODO
-}
-
-
-int ContainerAdmin::getState()
-{
-// assert(mDom);
-//
-// int state;
-//
-// if (virDomainGetState(mDom.get(), &state, NULL, 0)) {
-// LOGE(mId << ": Error while getting the container's state:\n"
-// << libvirt::libvirtFormatError());
-// throw ContainerOperationException();
-// }
-//
-// return state;
- return 0;
+ return mDom.getState() == lxc::LxcDomain::State::FROZEN;
}
return 0;
}
-ContainerAdmin::ListenerId ContainerAdmin::registerLifecycleListener(const ContainerAdmin::LifecycleListener& listener,
- const utils::CallbackGuard::Tracker& tracker)
-{
-
- utils::CallbackWrapper<LifecycleListener> wrap(listener, tracker);
-
- std::unique_lock<std::mutex> lock(mListenerMutex);
- unsigned int id = mNextIdForListener++;
- mLifecycleListeners.insert(LifecycleListenerMap::value_type(id, std::move(wrap)));
-
- return id;
-}
-
-ContainerAdmin::ListenerId ContainerAdmin::registerRebootListener(const ContainerAdmin::RebootListener& listener,
- const utils::CallbackGuard::Tracker& tracker)
-{
-
- utils::CallbackWrapper<RebootListener> wrap(listener, tracker);
-
- std::unique_lock<std::mutex> lock(mListenerMutex);
- unsigned int id = mNextIdForListener++;
- mRebootListeners.insert(RebootListenerMap::value_type(id, std::move(wrap)));
-
- return id;
-}
-
-void ContainerAdmin::removeListener(const ContainerAdmin::ListenerId id)
-{
- std::unique_lock<std::mutex> lock(mListenerMutex);
- mLifecycleListeners.erase(id);
- mRebootListeners.erase(id);
-}
-
-//int ContainerAdmin::libvirtLifecycleCallback(virConnectPtr /*con*/,
-// virDomainPtr /*dom*/,
-// int event,
-// int detail,
-// void* opaque)
-//{
-// ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
-//
-// LOGI(thisPtr->getId()
-// << ": Lifecycle event: "
-// << libvirt::libvirtEventToString(event)
-// << ": "
-// << libvirt::libvirtEventDetailToString(event, detail));
-//
-// std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
-// for (auto& it : thisPtr->mLifecycleListeners) {
-// LifecycleListener f = it.second.get();
-// f(event, detail);
-// }
-//
-// // ignored, libvirt's legacy
-// return 0;
-//}
-//
-//void ContainerAdmin::libvirtRebootCallback(virConnectPtr /*con*/,
-// virDomainPtr /*dom*/,
-// void* opaque)
-//{
-// ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
-//
-// LOGI(thisPtr->getId() << ": Reboot event");
-//
-// std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
-// for (auto& it : thisPtr->mRebootListeners) {
-// RebootListener f = it.second.get();
-// f();
-// }
-//}
-
} // namespace security_containers
#define SERVER_CONTAINER_ADMIN_HPP
#include "container-config.hpp"
-
-#include "utils/callback-guard.hpp"
-#include "utils/callback-wrapper.hpp"
#include "lxc/domain.hpp"
-#include <map>
-#include <mutex>
-#include <string>
-#include <cstdint>
-
namespace security_containers {
class ContainerAdmin {
public:
- /**
- * A listener ID type.
- */
- typedef unsigned int ListenerId;
-
- /**
- * An invalid ListenerId value.
- */
- static const ListenerId LISTENER_ID_INVALID = 0;
-
- /**
- * A function type used for the lifecycle listener
- */
- typedef std::function<void(const int eventId, const int detailId)> LifecycleListener;
-
- /**
- * A function type used for the reboot listener
- */
- typedef std::function<void()> RebootListener;
/**
* ContainerAdmin constructor
void start();
/**
- * Try to shutdown the container, if failed, destroy it.
+ * Try to shutdown the container, if failed, kill it.
*/
void stop();
/**
- * Forcefully stop the container.
+ * Destroy stopped container. In particular it removes whole containers rootfs.
*/
void destroy();
/**
- * Gracefully shutdown the container.
- * This method will NOT block until container is shut down.
- */
- void shutdown();
-
- /**
* @return Is the container running?
*/
bool isRunning();
/**
* Check if the container is stopped. It's NOT equivalent to !isRunning,
- * because it checks different internal libvirt's states. There are other states,
+ * because it checks different internal lxc states. There are other states,
* (e.g. paused) when the container isn't running nor stopped.
*
* @return Is the container stopped?
*/
std::int64_t getSchedulerQuota();
- /**
- * Sets a listener for a lifecycle event.
- * It's a caller's responsibility to remove the listener
- * prior to destroying the object.
- *
- * @return listener ID that can be used to remove.
- */
- ListenerId registerLifecycleListener(const LifecycleListener& listener,
- const utils::CallbackGuard::Tracker& tracker);
-
- /**
- * Sets a listener for a reboot event.
- * It's a caller's responsibility to remove the listener
- * prior to destroying the object.
- *
- * @return listener ID that can be used to remove.
- */
- ListenerId registerRebootListener(const RebootListener& listener,
- const utils::CallbackGuard::Tracker& tracker);
-
- /**
- * Remove a previously registered listener.
- */
- void removeListener(const ListenerId id);
-
private:
const ContainerConfig& mConfig;
lxc::LxcDomain mDom;
const std::string mId;
bool mDetachOnExit;
- int getState(); // get the libvirt's domain state
void setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota);
-
- // for handling libvirt callbacks
- utils::CallbackGuard mLibvirtGuard;
- int mLifecycleCallbackId;
- int mRebootCallbackId;
-
-// // virConnectDomainEventCallback
-// static int libvirtLifecycleCallback(virConnectPtr con,
-// virDomainPtr dom,
-// int event,
-// int detail,
-// void* opaque);
-//
-// // virConnectDomainEventGenericCallback
-// static void libvirtRebootCallback(virConnectPtr con,
-// virDomainPtr dom,
-// void* opaque);
-
- // for handling external listeners triggered from libvirt callbacks
- // TODO, the Listener type might not be unique, reimplement using proper listeners
- typedef std::map<ListenerId, utils::CallbackWrapper<LifecycleListener>> LifecycleListenerMap;
- typedef std::map<ListenerId, utils::CallbackWrapper<RebootListener>> RebootListenerMap;
-
- std::mutex mListenerMutex;
- unsigned int mNextIdForListener;
- LifecycleListenerMap mLifecycleListeners;
- RebootListenerMap mRebootListeners;
};
{
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
if (lxc.isDefined()) {
- if (lxc.isRunning()) {
+ if (lxc.getState() != LxcDomain::State::STOPPED) {
lxc.stop();
}
lxc.destroy();
}
}
+
+ void waitForInit()
+ {
+ // wait for init fully started (wait for bash to be able to trap SIGTERM)
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ }
};
} // namespace
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
BOOST_CHECK(!lxc.isDefined());
- lxc.create(TEMPLATE);
+ BOOST_CHECK(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.destroy());
BOOST_CHECK(!lxc.isDefined());
}
{
{
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
- lxc.create(TEMPLATE);
+ BOOST_CHECK(lxc.create(TEMPLATE));
}
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
- BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
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_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ waitForInit();
+ BOOST_CHECK(lxc.shutdown(2));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
}
BOOST_AUTO_TEST_CASE(StartStopTest)
{
{
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
- lxc.create(TEMPLATE);
+ BOOST_CHECK(lxc.create(TEMPLATE));
+ }
+ LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+ const char* argv[] = {
+ "/bin/sh",
+ NULL
+ };
+ BOOST_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ BOOST_CHECK(!lxc.shutdown(1));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ BOOST_CHECK(lxc.stop());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(StartHasStoppedTest)
+{
+ {
+ LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+ BOOST_CHECK(lxc.create(TEMPLATE));
}
LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
- BOOST_CHECK_EQUAL("STOPPED", lxc.getState());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+ const char* argv[] = {
+ "/bin/sh",
+ "-c",
+ "echo",
+ NULL
+ };
+ BOOST_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ waitForInit();
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(FreezeUnfreezeTest)
+{
+ LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+ BOOST_CHECK(lxc.create(TEMPLATE));
+ const char* argv[] = {
+ "/bin/sh",
+ "-c",
+ "trap exit SIGTERM; read",
+ NULL
+ };
+ BOOST_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ waitForInit();
+ BOOST_CHECK(lxc.freeze());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+ BOOST_CHECK(lxc.unfreeze());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ BOOST_CHECK(lxc.shutdown(2));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(FreezeStopTest)
+{
+ LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+ BOOST_CHECK(lxc.create(TEMPLATE));
+ const char* argv[] = {
+ "/bin/sh",
+ "-c",
+ "trap exit SIGTERM; read",
+ NULL
+ };
+ BOOST_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ waitForInit();
+ BOOST_CHECK(lxc.freeze());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+ BOOST_CHECK(!lxc.shutdown(1));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+ BOOST_CHECK(lxc.stop());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
+}
+
+BOOST_AUTO_TEST_CASE(RepeatTest)
+{
+ LxcDomain lxc(LXC_PATH, DOMAIN_NAME);
+ BOOST_CHECK(lxc.create(TEMPLATE));
+ BOOST_CHECK(!lxc.create(TEMPLATE));// forbidden
const char* argv[] = {
"/bin/sh",
+ "-c",
+ "trap exit SIGTERM; read",
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_CHECK(lxc.start(argv));
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ waitForInit();
+ BOOST_CHECK(!lxc.start(argv)); // forbidden
+ BOOST_CHECK(lxc.freeze());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+ BOOST_CHECK(lxc.freeze()); // repeat is nop
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::FROZEN);
+ BOOST_CHECK(lxc.unfreeze());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ BOOST_CHECK(lxc.unfreeze()); // repeat is nop
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::RUNNING);
+ BOOST_CHECK(lxc.stop());
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+ BOOST_CHECK(lxc.stop()); // repeat is nop
+ BOOST_CHECK(lxc.getState() == LxcDomain::State::STOPPED);
+
+ BOOST_CHECK(lxc.destroy());
+ BOOST_CHECK(!lxc.isDefined());
+ BOOST_CHECK(!lxc.destroy()); // forbidden (why?)
}
BOOST_AUTO_TEST_SUITE_END()
${CMAKE_BINARY_DIR}/ut-server/buggy-daemon.conf @ONLY)
FILE(GLOB server_manager_CONF_GEN ${CMAKE_BINARY_DIR}/ut-server/*.conf)
-CONFIGURE_FILE(ut-container-admin/containers/buggy.conf.in
- ${CMAKE_BINARY_DIR}/ut-container-admin/containers/buggy.conf @ONLY)
-CONFIGURE_FILE(ut-container-admin/containers/test.conf.in
- ${CMAKE_BINARY_DIR}/ut-container-admin/containers/test.conf @ONLY)
-CONFIGURE_FILE(ut-container-admin/containers/test-no-shutdown.conf.in
- ${CMAKE_BINARY_DIR}/ut-container-admin/containers/test-no-shutdown.conf @ONLY)
-FILE(GLOB admin_container_CONF_GEN ${CMAKE_BINARY_DIR}/ut-container-admin/containers/*.conf)
-
CONFIGURE_FILE(ut-network-admin/containers/test.conf.in
${CMAKE_BINARY_DIR}/ut-network-admin/containers/test.conf @ONLY)
CONFIGURE_FILE(ut-network-admin/containers/buggy.conf.in
INSTALL(FILES ${admin_container_CONF}
DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-admin/containers)
-INSTALL(FILES ${admin_container_CONF_GEN}
- DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-admin/containers)
INSTALL(FILES ${network_container_CONF}
DESTINATION ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-network-admin/containers)
{
+ "name" : "ut-container-admin-test",
+ "lxcTemplate" : "minimal.sh",
+ "initWithArgs" : ["/foo"],
"privilege" : 10,
"vt" : -1,
"switchToDefaultAfterTimeout" : true,
{
+ "name" : "ut-container-admin-test",
+ "lxcTemplate" : "missing.sh",
+ "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"],
"privilege" : 10,
"vt" : -1,
"switchToDefaultAfterTimeout" : true,
{
+ "name" : "ut-container-admin-test",
+ "lxcTemplate" : "minimal.sh",
+ "initWithArgs" : ["/bin/sh"],
"privilege" : 10,
"vt" : -1,
"switchToDefaultAfterTimeout" : true,
{
+ "name" : "ut-container-admin-test",
+ "lxcTemplate" : "minimal.sh",
+ "initWithArgs" : ["/bin/sh", "-c", "trap exit SIGTERM; read"],
"privilege" : 10,
"vt" : -1,
"switchToDefaultAfterTimeout" : true,
* @brief Unit tests of the ContainerAdmin class
*/
-//#include "config.hpp"
-//#include "ut.hpp"
-//
-//#include "container-admin.hpp"
-//#include "exception.hpp"
-//
-//#include "utils/latch.hpp"
-//#include "utils/glib-loop.hpp"
-//#include "utils/exception.hpp"
-//#include "utils/callback-guard.hpp"
-//#include "libvirt/exception.hpp"
-//#include "config/manager.hpp"
-//
-//#include <memory>
-//#include <string>
-//#include <thread>
-//#include <chrono>
-//
-//
-//using namespace security_containers;
-//
-//namespace {
-//
-//const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test.conf";
-//const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test-no-shutdown.conf";
-//const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/buggy.conf";
-//const std::string MISSING_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/missing.conf";
-//const unsigned int WAIT_TIMEOUT = 5 * 1000;
-//const unsigned int WAIT_STOP_TIMEOUT = 15 * 1000;
-//
-//void ensureStarted()
-//{
-// std::this_thread::sleep_for(std::chrono::milliseconds(200));
-//}
-//
-//struct Fixture {
-// utils::ScopedGlibLoop mLoop;
-// utils::CallbackGuard mGuard;
-//};
-//
-//} // namespace
-//
-//
-//BOOST_FIXTURE_TEST_SUITE(ContainerAdminSuite, Fixture)
-//
-//BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
-//{
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// std::unique_ptr<ContainerAdmin> admin;
-// BOOST_REQUIRE_NO_THROW(admin.reset(new ContainerAdmin(config)));
-// BOOST_REQUIRE_NO_THROW(admin.reset());
-//}
-//
-//BOOST_AUTO_TEST_CASE(BuggyConfigTest)
-//{
-// ContainerConfig config;
-// config::loadFromFile(BUGGY_CONFIG_PATH, config);
-// BOOST_REQUIRE_THROW(ContainerAdmin ca(config), LibvirtOperationException);
-//}
-//
-//BOOST_AUTO_TEST_CASE(MissingConfigTest)
-//{
-// ContainerConfig config;
-// config::loadFromFile(MISSING_CONFIG_PATH, config);
-// BOOST_REQUIRE_THROW(ContainerAdmin ca(config), UtilsException);
-//}
-//
-//BOOST_AUTO_TEST_CASE(StartTest)
-//{
-// utils::Latch booted;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener bootedListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_STARTED && detail == VIR_DOMAIN_EVENT_STARTED_BOOTED) {
-// booted.set();
-// }
-// };
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(bootedListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-//
-// BOOST_CHECK(booted.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(ca.isRunning());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(ShutdownTest)
-//{
-// utils::Latch shutdown;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener shutdownListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-// shutdown.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(shutdownListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.shutdown());
-// BOOST_CHECK(shutdown.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(!ca.isRunning());
-// BOOST_CHECK(ca.isStopped());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(DestroyTest)
-//{
-// utils::Latch destroyed;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener destroyedListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_DESTROYED) {
-// destroyed.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(destroyedListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.destroy());
-// BOOST_CHECK(destroyed.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(!ca.isRunning());
-// BOOST_CHECK(ca.isStopped());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(StopShutdownTest)
-//{
-// utils::Latch shutdown;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener shutdownListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
-// shutdown.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(shutdownListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.stop());
-// BOOST_CHECK(shutdown.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(!ca.isRunning());
-// BOOST_CHECK(ca.isStopped());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//// This test needs to wait for a shutdown timer in stop() method. This takes 10s+.
-//BOOST_AUTO_TEST_CASE(StopDestroyTest)
-//{
-// utils::Latch destroyed;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_NO_SHUTDOWN_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener destroyedListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_STOPPED && detail == VIR_DOMAIN_EVENT_STOPPED_DESTROYED) {
-// destroyed.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(destroyedListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.stop());
-// BOOST_CHECK(destroyed.wait(WAIT_STOP_TIMEOUT));
-// BOOST_CHECK(!ca.isRunning());
-// BOOST_CHECK(ca.isStopped());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(SuspendTest)
-//{
-// utils::Latch paused;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener pausedListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_SUSPENDED && detail == VIR_DOMAIN_EVENT_SUSPENDED_PAUSED) {
-// paused.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start())
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(pausedListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.suspend());
-// BOOST_CHECK(paused.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(!ca.isRunning());
-// BOOST_CHECK(ca.isPaused());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
-//BOOST_AUTO_TEST_CASE(ResumeTest)
-//{
-// utils::Latch unpaused;
-// ContainerAdmin::ListenerId id = ContainerAdmin::LISTENER_ID_INVALID;
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-//
-// ContainerAdmin::LifecycleListener unpausedListener = [&](const int event, const int detail) {
-// if (event == VIR_DOMAIN_EVENT_RESUMED && detail == VIR_DOMAIN_EVENT_RESUMED_UNPAUSED) {
-// unpaused.set();
-// }
-// };
-//
-// BOOST_REQUIRE_NO_THROW(ca.start());
-// ensureStarted();
-// BOOST_REQUIRE(ca.isRunning());
-// BOOST_REQUIRE_NO_THROW(ca.suspend())
-// BOOST_REQUIRE(ca.isPaused());
-// BOOST_REQUIRE_NO_THROW(id = ca.registerLifecycleListener(unpausedListener, mGuard.spawn()));
-//
-// BOOST_REQUIRE_NO_THROW(ca.resume());
-// BOOST_CHECK(unpaused.wait(WAIT_TIMEOUT));
-// BOOST_CHECK(!ca.isPaused());
-// BOOST_CHECK(ca.isRunning());
-//
-// BOOST_REQUIRE_NO_THROW(ca.removeListener(id));
-//}
-//
+#include "config.hpp"
+#include "ut.hpp"
+
+#include "container-admin.hpp"
+#include "exception.hpp"
+
+#include "utils/glib-loop.hpp"
+#include "utils/scoped-dir.hpp"
+#include "config/manager.hpp"
+
+using namespace security_containers;
+
+namespace {
+
+const std::string TEST_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test.conf";
+const std::string TEST_NO_SHUTDOWN_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/test-no-shutdown.conf";
+const std::string BUGGY_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/buggy.conf";
+const std::string MISSING_CONFIG_PATH = SC_TEST_CONFIG_INSTALL_DIR "/server/ut-container-admin/containers/missing.conf";
+const std::string CONTAINERS_PATH = "/tmp/ut-containers";
+const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR;
+
+struct Fixture {
+ utils::ScopedGlibLoop mLoop;
+ utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH;
+
+ ContainerConfig mConfig;
+
+ std::unique_ptr<ContainerAdmin> create(const std::string& configPath)
+ {
+ config::loadFromFile(configPath, mConfig);
+ return std::unique_ptr<ContainerAdmin>(new ContainerAdmin(CONTAINERS_PATH,
+ LXC_TEMPLATES_PATH,
+ mConfig));
+ }
+
+ void ensureStarted()
+ {
+ // wait for containers init to fully start
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ }
+};
+
+} // namespace
+
+
+BOOST_FIXTURE_TEST_SUITE(ContainerAdminSuite, Fixture)
+
+BOOST_AUTO_TEST_CASE(ConstructorDestructorTest)
+{
+ auto admin = create(TEST_CONFIG_PATH);
+ admin.reset();
+}
+
+BOOST_AUTO_TEST_CASE(MissingConfigTest)
+{
+ BOOST_REQUIRE_THROW(create(MISSING_CONFIG_PATH), ContainerOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(StartTest)
+{
+ auto admin = create(TEST_CONFIG_PATH);
+
+ admin->start();
+ ensureStarted();
+
+ BOOST_CHECK(admin->isRunning());
+}
+
+BOOST_AUTO_TEST_CASE(StartBuggyTest)
+{
+ auto admin = create(BUGGY_CONFIG_PATH);
+ BOOST_REQUIRE_THROW(admin->start(), ContainerOperationException);
+}
+
+BOOST_AUTO_TEST_CASE(StopShutdownTest)
+{
+ auto admin = create(TEST_CONFIG_PATH);
+
+ admin->start();
+ ensureStarted();
+ BOOST_REQUIRE(admin->isRunning());
+
+ admin->stop();
+ BOOST_CHECK(!admin->isRunning());
+ BOOST_CHECK(admin->isStopped());
+}
+
+// This test needs to wait for a shutdown timer in stop() method. This takes 10s+.
+BOOST_AUTO_TEST_CASE(StopDestroyTest)
+{
+ auto admin = create(TEST_NO_SHUTDOWN_CONFIG_PATH);
+
+ admin->start();
+ ensureStarted();
+ BOOST_REQUIRE(admin->isRunning());
+
+ admin->stop();
+ BOOST_CHECK(!admin->isRunning());
+ BOOST_CHECK(admin->isStopped());
+}
+
+BOOST_AUTO_TEST_CASE(SuspendResumeTest)
+{
+ auto admin = create(TEST_NO_SHUTDOWN_CONFIG_PATH);
+
+ admin->start();
+ ensureStarted();
+ BOOST_REQUIRE(admin->isRunning());
+
+ admin->suspend();
+ BOOST_CHECK(!admin->isRunning());
+ BOOST_CHECK(!admin->isStopped());
+ BOOST_CHECK(admin->isPaused());
+
+ admin->resume();
+ BOOST_CHECK(!admin->isPaused());
+ BOOST_CHECK(!admin->isStopped());
+ BOOST_CHECK(admin->isRunning());
+}
+
//BOOST_AUTO_TEST_CASE(SchedulerLevelTest)
//{
-// ContainerConfig config;
-// config::loadFromFile(TEST_CONFIG_PATH, config);
-// ContainerAdmin ca(config);
-// BOOST_REQUIRE_NO_THROW(ca.start());
+// auto admin = create(TEST_CONFIG_PATH);
+//
+// admin->start();
// ensureStarted();
-// BOOST_REQUIRE_NO_THROW(ca.setSchedulerLevel(SchedulerLevel::FOREGROUND));
-// BOOST_REQUIRE(ca.getSchedulerQuota() == config.cpuQuotaForeground);
-// BOOST_REQUIRE_NO_THROW(ca.setSchedulerLevel(SchedulerLevel::BACKGROUND));
-// BOOST_REQUIRE(ca.getSchedulerQuota() == config.cpuQuotaBackground);
+// BOOST_REQUIRE_NO_THROW(admin->setSchedulerLevel(SchedulerLevel::FOREGROUND));
+// BOOST_REQUIRE(admin->getSchedulerQuota() == config.cpuQuotaForeground);
+// BOOST_REQUIRE_NO_THROW(admin->setSchedulerLevel(SchedulerLevel::BACKGROUND));
+// BOOST_REQUIRE(admin->getSchedulerQuota() == config.cpuQuotaBackground);
//}
-//
-//BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE_END()
const std::string CONTAINERS_PATH = "/tmp/ut-containers";
const std::string LXC_TEMPLATES_PATH = SC_TEST_LXC_TEMPLATES_INSTALL_DIR;
-void ensureStarted()
-{
- std::this_thread::sleep_for(std::chrono::milliseconds(200));
-}
-
struct Fixture {
utils::ScopedGlibLoop mLoop;
utils::ScopedDir mContainersPathGuard = CONTAINERS_PATH;
LXC_TEMPLATES_PATH,
""));
}
+
+ void ensureStarted()
+ {
+ // wait for containers init to fully start
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ }
};
} // namespace
BOOST_AUTO_TEST_CASE(BuggyConfigTest)
{
- BOOST_REQUIRE_THROW(create(BUGGY_CONFIG_PATH), std::exception);//TODO which one?
+ BOOST_REQUIRE_THROW(create(BUGGY_CONFIG_PATH), ContainerOperationException);
}
BOOST_AUTO_TEST_CASE(MissingConfigTest)