# Path: contrib/namespaces.py
# class LinuxNamespace:
#     """Combine several namespaces into one.
#     This gets a list of namespace types to create and combine into one. The
#     combined namespace can be used as a context manager to enter all the
#     created namespaces and exit them at the end.
#     """
# 
#     def __init__(self, *namespaces):
#         self.namespaces = namespaces
#         for ns in namespaces:
#             assert ns in NAMESPACE_FLAGS
# 
#         # Get a pipe to signal the future child to exit
#         self.pipe = os.pipe()
# 
#         # First, create a child in the given namespaces
#         child = ctypes.CFUNCTYPE(ctypes.c_int)(self.child)
#         child_stack = ctypes.create_string_buffer(STACKSIZE)
#         child_stack_pointer = ctypes.c_void_p(
#             ctypes.cast(child_stack,
#                         ctypes.c_void_p).value + STACKSIZE)
#         flags = signal.SIGCHLD
#         for ns in namespaces:
#             flags |= NAMESPACE_FLAGS[ns]
#         pid = libc.clone(child, child_stack_pointer, flags)
#         if pid == -1:
#             e = ctypes.get_errno()
#             raise OSError(e, os.strerror(e))
# 
#         # If a user namespace, map UID 0 to the current one
#         if 'user' in namespaces:
#             uid_map = '0 {} 1'.format(os.getuid())
#             gid_map = '0 {} 1'.format(os.getgid())
#             with open('/proc/{}/uid_map'.format(pid), 'w') as f:
#                 f.write(uid_map)
#             with open('/proc/{}/setgroups'.format(pid), 'w') as f:
#                 f.write('deny')
#             with open('/proc/{}/gid_map'.format(pid), 'w') as f:
#                 f.write(gid_map)
# 
#         # Retrieve a file descriptor to this new namespace
#         self.next = [os.open('/proc/{}/ns/{}'.format(pid, x),
#                              os.O_RDONLY) for x in namespaces]
# 
#         # Keep a file descriptor to our old namespaces
#         self.previous = [os.open('/proc/self/ns/{}'.format(x),
#                                  os.O_RDONLY) for x in namespaces]
# 
#         # Tell the child all is done and let it die
#         os.close(self.pipe[0])
#         if 'pid' not in namespaces:
#             os.close(self.pipe[1])
#             self.pipe = None
#             os.waitpid(pid, 0)
# 
#     def __del__(self):
#         pass
# 
#     def child(self):
#         """Cloned child.
#         Just be here until our parent extract the file descriptor from
#         us.
#         """
#         os.close(self.pipe[1])
# 
#         while True:
#             try:
#                 os.read(self.pipe[0], 1)
#             except OSError as e:
#                 if e.errno in [errno.EAGAIN, errno.EINTR]:
#                     continue
#             break
# 
#         os._exit(0)  # Adopted code. pylint: disable=protected-access
# 
#     def fd(self, namespace):
#         """Return the file descriptor associated to a namespace"""
#         assert namespace in self.namespaces
#         return self.next[self.namespaces.index(namespace)]
# 
#     def __enter__(self):
#         with keep_directory():
#             for n in self.next:
#                 if libc.setns(n, 0) == -1:
#                     ns = self.namespaces[self.next.index(n)]  # noqa Adopted code. pylint: disable=unused-variable
#                     e = ctypes.get_errno()
#                     raise OSError(e, os.strerror(e))
# 
#     def __exit__(self, *exc):
#         pass
# 
#     def __repr__(self):
#         return 'Namespace({})'.format(", ".join(self.namespaces))


import glob
import os
import re
import pytest
import yaml
from collections import namedtuple
from contrib.namespaces import LinuxNamespace

def pytest_addoption(parser):
    parser.addoption("--config", action="append", help="path to Deckard configuration .yaml file")
    parser.addoption("--scenarios", action="append", help="directory with .rpl files")
    parser.addoption("--retries", action="store", help=("number of retries per"
                                                        "test when Deckard is under load"))


def pytest_generate_tests(metafunc):
    """This is pytest weirdness to parametrize the test over all the *.rpl files.
    See https://docs.pytest.org/en/latest/parametrize.html#basic-pytest-generate-tests-example
    for more info."""

    if 'scenario' in metafunc.fixturenames:
        if metafunc.config.option.config is None:
            configs = []
        else:
            configs = metafunc.config.option.config

        if metafunc.config.option.scenarios is None:
            paths = ["sets/resolver"] * len(configs)
        else:
            paths = metafunc.config.option.scenarios

        metafunc.parametrize("scenario", scenarios(paths, configs), ids=str)
    if 'rpl_path' in metafunc.fixturenames:
        paths = metafunc.config.option.scenarios
        metafunc.parametrize("rpl_path", rpls(paths), ids=str)
    if 'max_retries' in metafunc.fixturenames:
        max_retries = metafunc.config.option.retries

# Path: contrib/namespaces.py
# class LinuxNamespace:
#     """Combine several namespaces into one.
#     This gets a list of namespace types to create and combine into one. The
#     combined namespace can be used as a context manager to enter all the
#     created namespaces and exit them at the end.
#     """
# 
#     def __init__(self, *namespaces):
#         self.namespaces = namespaces
#         for ns in namespaces:
#             assert ns in NAMESPACE_FLAGS
# 
#         # Get a pipe to signal the future child to exit
#         self.pipe = os.pipe()
# 
#         # First, create a child in the given namespaces
#         child = ctypes.CFUNCTYPE(ctypes.c_int)(self.child)
#         child_stack = ctypes.create_string_buffer(STACKSIZE)
#         child_stack_pointer = ctypes.c_void_p(
#             ctypes.cast(child_stack,
#                         ctypes.c_void_p).value + STACKSIZE)
#         flags = signal.SIGCHLD
#         for ns in namespaces:
#             flags |= NAMESPACE_FLAGS[ns]
#         pid = libc.clone(child, child_stack_pointer, flags)
#         if pid == -1:
#             e = ctypes.get_errno()
#             raise OSError(e, os.strerror(e))
# 
#         # If a user namespace, map UID 0 to the current one
#         if 'user' in namespaces:
#             uid_map = '0 {} 1'.format(os.getuid())
#             gid_map = '0 {} 1'.format(os.getgid())
#             with open('/proc/{}/uid_map'.format(pid), 'w') as f:
#                 f.write(uid_map)
#             with open('/proc/{}/setgroups'.format(pid), 'w') as f:
#                 f.write('deny')
#             with open('/proc/{}/gid_map'.format(pid), 'w') as f:
#                 f.write(gid_map)
# 
#         # Retrieve a file descriptor to this new namespace
#         self.next = [os.open('/proc/{}/ns/{}'.format(pid, x),
#                              os.O_RDONLY) for x in namespaces]
# 
#         # Keep a file descriptor to our old namespaces
#         self.previous = [os.open('/proc/self/ns/{}'.format(x),
#                                  os.O_RDONLY) for x in namespaces]
# 
#         # Tell the child all is done and let it die
#         os.close(self.pipe[0])
#         if 'pid' not in namespaces:
#             os.close(self.pipe[1])
#             self.pipe = None
#             os.waitpid(pid, 0)
# 
#     def __del__(self):
#         pass
# 
#     def child(self):
#         """Cloned child.
#         Just be here until our parent extract the file descriptor from
#         us.
#         """
#         os.close(self.pipe[1])
# 
#         while True:
#             try:
#                 os.read(self.pipe[0], 1)
#             except OSError as e:
#                 if e.errno in [errno.EAGAIN, errno.EINTR]:
#                     continue
#             break
# 
#         os._exit(0)  # Adopted code. pylint: disable=protected-access
# 
#     def fd(self, namespace):
#         """Return the file descriptor associated to a namespace"""
#         assert namespace in self.namespaces
#         return self.next[self.namespaces.index(namespace)]
# 
#     def __enter__(self):
#         with keep_directory():
#             for n in self.next:
#                 if libc.setns(n, 0) == -1:
#                     ns = self.namespaces[self.next.index(n)]  # noqa Adopted code. pylint: disable=unused-variable
#                     e = ctypes.get_errno()
#                     raise OSError(e, os.strerror(e))
# 
#     def __exit__(self, *exc):
#         pass
# 
#     def __repr__(self):
#         return 'Namespace({})'.format(", ".join(self.namespaces))


# Path: conftest.py
import glob
import os
import re
import pytest
import yaml
from collections import namedtuple
from contrib.namespaces import LinuxNamespace


def pytest_addoption(parser):
    parser.addoption("--config", action="append", help="path to Deckard configuration .yaml file")
    parser.addoption("--scenarios", action="append", help="directory with .rpl files")
    parser.addoption("--retries", action="store", help=("number of retries per"
                                                        "test when Deckard is under load"))


def pytest_generate_tests(metafunc):
    """This is pytest weirdness to parametrize the test over all the *.rpl files.
    See https://docs.pytest.org/en/latest/parametrize.html#basic-pytest-generate-tests-example
    for more info."""

    if 'scenario' in metafunc.fixturenames:
        if metafunc.config.option.config is None:
            configs = []
        else:
            configs = metafunc.config.option.config

        if metafunc.config.option.scenarios is None:
            paths = ["sets/resolver"] * len(configs)
        else:
            paths = metafunc.config.option.scenarios

        metafunc.parametrize("scenario", scenarios(paths, configs), ids=str)
    if 'rpl_path' in metafunc.fixturenames:
        paths = metafunc.config.option.scenarios
        metafunc.parametrize("rpl_path", rpls(paths), ids=str)
    if 'max_retries' in metafunc.fixturenames:
        max_retries = metafunc.config.option.retries

        if max_retries is None: