Prior to this commit, the functional tests used LXC.  LXD is
better for configuring a shared device between the host and
container that can be written to by the container.  This
feature is critical for tracking code coverage in the
functional tests.

Signed-off-by: Tom Hromatka <tom.hroma...@oracle.com>
---
 .travis.yml               |   1 +
 tests/ftests/cgroup.py    |   4 +-
 tests/ftests/config.py    |   2 +-
 tests/ftests/consts.py    |  10 +--
 tests/ftests/container.py | 140 +++++++++++++-------------------------
 tests/ftests/ftests.py    |  72 +++++++++++++++-----
 tests/ftests/run.py       |   2 +-
 7 files changed, 114 insertions(+), 117 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 055f375..4b24840 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,6 +37,7 @@ addons:
   apt:
     packages:
       - lxc
+      - lxd
 
 # perform the build and fail immediately on error
 install:
diff --git a/tests/ftests/cgroup.py b/tests/ftests/cgroup.py
index 20495af..c8bffa5 100644
--- a/tests/ftests/cgroup.py
+++ b/tests/ftests/cgroup.py
@@ -27,8 +27,8 @@ class Cgroup(object):
     @staticmethod
     def build_cmd_path(in_container, cmd):
         if in_container:
-            return os.path.join('/', consts.LIBCG_MOUNT_POINT,
-                                'src/tools/.libs/{}'.format(cmd))
+            return os.path.join(consts.LIBCG_MOUNT_POINT,
+                                'src/tools/{}'.format(cmd))
         else:
             return cmd
 
diff --git a/tests/ftests/config.py b/tests/ftests/config.py
index 2d32aaf..5cdc21d 100644
--- a/tests/ftests/config.py
+++ b/tests/ftests/config.py
@@ -32,7 +32,7 @@ class Config(object):
         else:
             # Use the default container settings
             self.container = Container(name=consts.DEFAULT_CONTAINER_NAME,
-                stop_timeout=args.timeout, arch=None, cfg_path=args.config,
+                stop_timeout=args.timeout, arch=None,
                 distro=args.distro, release=args.release)
 
         self.ftest_dir = os.path.dirname(os.path.abspath(__file__))
diff --git a/tests/ftests/consts.py b/tests/ftests/consts.py
index 9a1b92b..f1621b4 100644
--- a/tests/ftests/consts.py
+++ b/tests/ftests/consts.py
@@ -28,11 +28,13 @@ LOG_WARNING = 5
 LOG_DEBUG = 8
 DEFAULT_LOG_LEVEL = 5
 
-LIBCG_MOUNT_POINT = 'libcg'
+ftest_dir = os.path.dirname(os.path.abspath(__file__))
+tests_dir = os.path.dirname(ftest_dir)
+LIBCG_MOUNT_POINT = os.path.dirname(tests_dir)
 
-DEFAULT_CONTAINER_NAME = 'test_libcg'
-DEFAULT_CONTAINER_DISTRO = 'oracle'
-DEFAULT_CONTAINER_RELEASE = '7'
+DEFAULT_CONTAINER_NAME = 'TestLibcg'
+DEFAULT_CONTAINER_DISTRO = 'ubuntu'
+DEFAULT_CONTAINER_RELEASE = '18.04'
 DEFAULT_CONTAINER_ARCH = 'amd64'
 DEFAULT_CONTAINER_STOP_TIMEOUT = 5
 DEFAULT_CONTAINER_CFG_PATH=os.path.join(
diff --git a/tests/ftests/container.py b/tests/ftests/container.py
index 6d4a100..948018e 100644
--- a/tests/ftests/container.py
+++ b/tests/ftests/container.py
@@ -41,11 +41,6 @@ class Container(object):
         else:
             self.arch = consts.DEFAULT_CONTAINER_ARCH
 
-        if cfg_path:
-            self.cfg_path = cfg_path
-        else:
-            self.cfg_path = consts.DEFAULT_CONTAINER_CFG_PATH
-
         if distro:
             self.distro = distro
         else:
@@ -58,125 +53,93 @@ class Container(object):
 
         ftest_dir = os.path.dirname(os.path.abspath(__file__))
         tests_dir = os.path.dirname(ftest_dir)
-        libcg_dir = os.path.dirname(tests_dir)
-
-        self.tmp_cfg_path = os.path.join(ftest_dir, 
consts.TEMP_CONTAINER_CFG_FILE)
-        try:
-            Run.run(['rm', '-f', self.tmp_cfg_path])
-        except:
-            pass
+        # save off the path to the libcgroup source code
+        self.libcg_dir = os.path.dirname(tests_dir)
 
-        Run.run(['cp', self.cfg_path, self.tmp_cfg_path])
-
-        conf_line = 'lxc.arch = {}'.format(self.arch)
-        Run.run(['echo', conf_line, '>>', self.tmp_cfg_path], shell_bool=True)
-
-        conf_line = 'lxc.mount.entry = {} {} none bind,ro 0 0'.format(
-                    libcg_dir, consts.LIBCG_MOUNT_POINT)
-        Run.run(['echo', conf_line, '>>', self.tmp_cfg_path], shell_bool=True)
-
-        if not self.privileged:
-            conf_line = 'lxc.idmap = u 0 100000 65536'
-            Run.run(['echo', conf_line, '>>', self.tmp_cfg_path], 
shell_bool=True)
-            conf_line = 'lxc.idmap = g 0 100000 65536'
-            Run.run(['echo', conf_line, '>>', self.tmp_cfg_path], 
shell_bool=True)
 
     def __str__(self):
         out_str = "{}".format(self.name)
         out_str += "\n\tdistro = {}".format(self.distro)
         out_str += "\n\trelease = {}".format(self.release)
         out_str += "\n\tarch = {}".format(self.arch)
-        out_str += "\n\tcfg_path = {}".format(self.cfg_path)
         out_str += "\n\tstop_timeout = {}".format(self.stop_timeout)
 
         return out_str
 
-    def create(self):
-        cmd = list()
-
-        if self.privileged:
-            cmd.append('sudo')
-
-        cmd.append('lxc-create')
-        cmd.append('-t')
-        cmd.append( 'download')
+    # configure the container to meet our needs
+    def config(self):
+        ftest_dir = os.path.dirname(os.path.abspath(__file__))
+        tests_dir = os.path.dirname(ftest_dir)
+        libcg_dir = os.path.dirname(tests_dir)
 
-        cmd.append('-n')
-        cmd.append(self.name)
+        # map our UID and GID to the same UID/GID in the container
+        cmd = 'printf "uid {} 1000\ngid {} 1000" | sudo lxc config set {} 
raw.idmap -'.format(
+              os.getuid(), os.getgid(), self.name)
+        Run.run(cmd, shell_bool=True)
 
+        # add the libcgroup root directory (where we did the build) into
+        # the container
+        cmd2 = list()
         if self.privileged:
-            cmd.append('sudo')
-        cmd.append('-f')
-        cmd.append(self.tmp_cfg_path)
-
-        cmd.append('--')
-
-        cmd.append('-d')
-        cmd.append(self.distro)
-
-        cmd.append('-r')
-        cmd.append(self.release)
-
-        cmd.append('-a')
-        cmd.append(self.arch)
-
-        return Run.run(cmd)
+            cmd2.append('sudo')
+        cmd2.append('lxc')
+        cmd2.append('config')
+        cmd2.append('device')
+        cmd2.append('add')
+        cmd2.append(self.name)
+        cmd2.append('libcgsrc') # arbitrary name of device
+        cmd2.append('disk')
+        # to appease gcov, mount the libcgroup source at the same path as we
+        # built it.  This can be worked around someday by using
+        # GCOV_PREFIX_STRIP, but that was more difficult to setup than just
+        # doing this initially
+        cmd2.append('source={}'.format(self.libcg_dir))
+        cmd2.append('path={}'.format(self.libcg_dir))
+
+        return Run.run(cmd2)
 
-    def destroy(self):
+    def create(self):
         cmd = list()
 
         if self.privileged:
             cmd.append('sudo')
 
-        cmd.append('lxc-destroy')
+        cmd.append('lxc')
+        cmd.append('init')
+
+        cmd.append('{}:{}'.format(self.distro, self.release))
 
-        cmd.append('-n')
         cmd.append(self.name)
 
         return Run.run(cmd)
 
-    def info(self, cfgname):
+    def delete(self):
         cmd = list()
 
         if self.privileged:
             cmd.append('sudo')
 
-        cmd.append('lxc-info')
-
-        cmd.append('--config={}'.format(cfgname))
+        cmd.append('lxc')
+        cmd.append('delete')
 
-        cmd.append('-n')
         cmd.append(self.name)
 
         return Run.run(cmd)
 
-    def rootfs(self):
-        # try to read lxc.rootfs.path first
-        ret = self.info('lxc.rootfs.path')
-        if len(ret.strip()) > 0:
-            return ret.decode()
-
-        # older versions of lxc used lxc.rootfs.  Try that.
-        ret = self.info('lxc.rootfs')
-        if len(ret.strip()) == 0:
-            # we failed to get the rootfs
-            raise ContainerError('Failed to get the rootfs')
-        return ret.decode()
-
     def run(self, cntnr_cmd):
         cmd = list()
 
         if self.privileged:
             cmd.append('sudo')
 
-        cmd.append('lxc-attach')
+        cmd.append('lxc')
+        cmd.append('exec')
 
-        cmd.append('-n')
         cmd.append(self.name)
 
         cmd.append('--')
 
-        # concatenate the lxc-attach command with the command to be run
+        # concatenate the lxc exec command with the command to be run
         # inside the container
         if isinstance(cntnr_cmd, str):
             cmd.append(cntnr_cmd)
@@ -193,37 +156,32 @@ class Container(object):
         if self.privileged:
             cmd.append('sudo')
 
-        cmd.append('lxc-start')
-        cmd.append('-d')
+        cmd.append('lxc')
+        cmd.append('start')
 
-        cmd.append('-n')
         cmd.append(self.name)
 
         return Run.run(cmd)
 
-    def stop(self, kill=True):
+    def stop(self, force=True):
         cmd = list()
 
         if self.privileged:
             cmd.append('sudo')
 
-        cmd.append('lxc-stop')
+        cmd.append('lxc')
+        cmd.append('stop')
 
-        cmd.append('-n')
         cmd.append(self.name)
 
-        if kill:
-            cmd.append('-k')
+        if force:
+            cmd.append('-f')
         else:
-            cmd.append('-t')
+            cmd.append('--timeout')
             cmd.append(str(self.stop_timeout))
 
         return Run.run(cmd)
 
-    def version(self):
-        cmd = ['lxc-create', '--version']
-        return Run.run(cmd)
-
 class ContainerError(Exception):
     def __init__(self, message, ret):
         super(RunError, self).__init__(message)
diff --git a/tests/ftests/ftests.py b/tests/ftests/ftests.py
index 8f11ed6..54eb658 100755
--- a/tests/ftests/ftests.py
+++ b/tests/ftests/ftests.py
@@ -41,9 +41,6 @@ def parse_args():
     parser.add_argument('-n', '--name',
                         help='name of the container',
                         required=False, type=str, default=None)
-    parser.add_argument('-f', '--config',
-                        help='initial configuration file',
-                        required=False, type=str, default=None)
     parser.add_argument('-d', '--distro',
                         help='linux distribution to use as a template',
                         required=False, type=str, default=None)
@@ -70,9 +67,6 @@ def parse_args():
     parser.add_argument('-s', '--suite',
                         help='Test suite to run, e.g. cpuset', required=False,
                         default=consts.TESTS_RUN_ALL_SUITES, type=str)
-    parser.add_argument('-u', '--unpriv',
-                        help='Run the tests in an unprivileged container',
-                        required=False, action="store_true")
     parser.add_argument('-v', '--verbose',
                         help='Print all information about this test run',
                         default=True, required=False, action="store_false")
@@ -83,12 +77,55 @@ def parse_args():
         log.log_level = config.args.loglevel
     if config.args.logfile:
         log.log_file = config.args.logfile
-    if config.args.unpriv:
-        raise ValueError('Unprivileged containers are not currently supported')
-        config.container.privileged = False
 
     return config
 
+# this function maps the container UID to the host UID.  By doing
+# this, we can write to a bind-mounted device - and thus generate
+# code coverage data in the LXD container
+def update_host_subuid():
+    subuid_line1 = 'lxd:{}:1'.format(os.getuid())
+    subuid_line2 = 'root:{}:1'.format(os.getuid())
+    found_line1 = False
+    found_line2 = False
+
+    with open('/etc/subuid') as ufile:
+        for line in ufile.readlines():
+            if line.strip() == subuid_line1:
+                found_line1 = True
+            elif line.strip() == subuid_line2:
+                found_line2 = True
+
+    if not found_line1:
+        Run.run('sudo sh -c "echo {} >> /etc/subuid"'.format(
+                subuid_line1), shell_bool=True)
+    if not found_line2:
+        Run.run('sudo sh -c "echo {} >> /etc/subuid"'.format(
+                subuid_line2), shell_bool=True)
+
+# this function maps the container GID to the host GID.  By doing
+# this, we can write to a bind-mounted device - and thus generate
+# code coverage data in the LXD container
+def update_host_subgid():
+    subgid_line1 = 'lxd:{}:1'.format(os.getgid())
+    subgid_line2 = 'root:{}:1'.format(os.getgid())
+    found_line1 = False
+    found_line2 = False
+
+    with open('/etc/subgid') as ufile:
+        for line in ufile.readlines():
+            if line.strip() == subgid_line1:
+                found_line1 = True
+            elif line.strip() == subgid_line2:
+                found_line2 = True
+
+    if not found_line1:
+        Run.run('sudo sh -c "echo {} >> /etc/subgid"'.format(
+                subgid_line1), shell_bool=True)
+    if not found_line2:
+        Run.run('sudo sh -c "echo {} >> /etc/subgid"'.format(
+                subgid_line2), shell_bool=True)
+
 def setup(config, do_teardown=True, record_time=False):
     global setup_time
     start_time = time.time()
@@ -101,19 +138,18 @@ def setup(config, do_teardown=True, record_time=False):
             # log but ignore all exceptions
             Log.log_debug(e)
 
-    config.container.create()
-
-    # make the /libcg directory in the container's rootfs
-    rootfs = config.container.rootfs()
-    container_rootfs_path = rootfs.split('=')[1].strip()
-    Run.run(['sudo', 'mkdir', os.path.join(container_rootfs_path,
-                                   consts.LIBCG_MOUNT_POINT)])
+    # this command initializes the lxd storage, networking, etc.
+    Run.run(['sudo', 'lxd', 'init', '--auto'])
+    update_host_subuid()
+    update_host_subgid()
 
+    config.container.create()
+    config.container.config()
     config.container.start()
 
     # add the libcgroup library to the container's ld
     echo_cmd = ['bash', '-c', 'echo {} >> 
/etc/ld.so.conf.d/libcgroup.conf'.format(
-               os.path.join('/', consts.LIBCG_MOUNT_POINT, 'src/.libs'))]
+               os.path.join(consts.LIBCG_MOUNT_POINT, 'src/.libs'))]
     config.container.run(echo_cmd)
     config.container.run('ldconfig')
     if record_time:
@@ -240,7 +276,7 @@ def teardown(config, record_time=False):
         # log but ignore all exceptions
         Log.log_debug(e)
     try:
-        config.container.destroy()
+        config.container.delete()
     except Exception as e:
         # log but ignore all exceptions
         Log.log_debug(e)
diff --git a/tests/ftests/run.py b/tests/ftests/run.py
index 0b4e068..419f38f 100644
--- a/tests/ftests/run.py
+++ b/tests/ftests/run.py
@@ -51,7 +51,7 @@ class Run(object):
         else:
             Log.log_debug(
                 "run:\n\tcommand = {}\n\tret = {}\n\tstdout = {}\n\tstderr = 
{}".format(
-                ''.join(command), ret, out, err))
+                ' '.join(command), ret, out, err))
 
         if ret != 0:
             raise RunError("Command '{}' failed".format(''.join(command)),
-- 
2.21.0



_______________________________________________
Libcg-devel mailing list
Libcg-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libcg-devel

Reply via email to