def configure(self, container_dir, app): root_dir = os.path.join(container_dir, 'root') newroot_norm = fs.norm_safe(root_dir) emptydirs = [ '/opt/s6', '/opt/treadmill', '/opt/treadmill-bind', ] stickydirs = [] mounts = [ '/opt/s6', '/opt/treadmill', '/opt/treadmill-bind', ] for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX) for mount in mounts: if os.path.exists(mount): fs_linux.mount_bind(newroot_norm, mount, recursive=True, read_only=True)
def make_fsroot(root_dir, emptydirs, stickydirs, mounts): """Initializes directory structure for the container in a new root. """ _LOGGER.info('Creating fs root in: %s', root_dir) for directory in sorted(emptydirs): fs.mkdir_safe(root_dir + directory) for directory in sorted(stickydirs): os.chmod(root_dir + directory, 0o777 | stat.S_ISVTX) # Make shared directories/files readonly to container reserved = {'/run', '/sys/fs', '/var/spool/tickets', '/var/spool/keytabs', '/var/spool/tokens'} for mount, args in mounts.items(): # These are reserved, mounted on memory in make_osroot. if mount in reserved: continue if not args: fs_linux.mount_bind( root_dir, mount, recursive=True, read_only=True ) else: fs_linux.mount_bind(root_dir, mount, **args)
def make_osroot(root_dir, app, data): """Creates mandatory system directories in the container chroot.""" _LOGGER.info('Creating os root in: %s', root_dir) # Mount .../tickets .../keytabs on tempfs, so that they will be cleaned # up when the container exits. # for tmpfsdir in [ '/var/spool/tickets', '/var/spool/keytabs', '/var/spool/tokens' ]: fs_linux.mount_tmpfs(root_dir, tmpfsdir) # /var/empty must be owned by root and not group or world-writable. os.chmod(os.path.join(root_dir, 'var/empty'), 0o711) # Mount a new sysfs for the container, bring in the /sys/fs subtree from # the host. fs_linux.mount_sysfs(root_dir) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'sys', 'fs'), recursive=True, read_only=False) make_dev(root_dir) # Passthrough node devices per the node config data extra_devices = data.get('runtime', {}).get('passthrough_devices', []) make_extra_dev(root_dir, extra_devices, app.proid) # Per FHS3 /var/run should be a symlink to /run which should be tmpfs fs.symlink_safe(os.path.join(root_dir, 'var', 'run'), '/run') # We create an unbounded tmpfs mount so that runtime data can be written to # it, counting against the memory limit of the container. fs_linux.mount_tmpfs(root_dir, '/run') # /etc/docker is a file neceesary for docker daemon _docker.prepare_docker_daemon_path(root_dir, app, data)
def make_dev(newroot_norm): """Make /dev. """ fs_linux.mount_tmpfs(newroot_norm, '/dev', nodev=False, noexec=False, nosuid=True, relatime=False, mode='0755') devices = [ ('/dev/null', 0o666, 1, 3), ('/dev/zero', 0o666, 1, 5), ('/dev/full', 0o666, 1, 7), ('/dev/tty', 0o666, 5, 0), ('/dev/random', 0o444, 1, 8), ('/dev/urandom', 0o444, 1, 9), ] prev_umask = os.umask(0000) for device, permissions, major, minor in devices: os.mknod(newroot_norm + device, permissions | stat.S_IFCHR, os.makedev(major, minor)) os.umask(prev_umask) st = os.stat('/dev/tty') os.chown(newroot_norm + '/dev/tty', st.st_uid, st.st_gid) symlinks = [ ('/dev/fd', '/proc/self/fd'), ('/dev/stdin', '/proc/self/fd/0'), ('/dev/stdout', '/proc/self/fd/1'), ('/dev/stderr', '/proc/self/fd/2'), ('/dev/core', '/proc/kcore'), ] for link, target in symlinks: fs.symlink_safe(newroot_norm + link, target) for directory in ['/dev/shm', '/dev/pts', '/dev/mqueue']: fs.mkdir_safe(newroot_norm + directory) fs_linux.mount_tmpfs(newroot_norm, '/dev/shm', nodev=True, noexec=False, nosuid=True, relatime=False) fs_linux.mount_devpts(newroot_norm, '/dev/pts', gid=st.st_gid, mode='0620', ptmxmode='0666') fs.symlink_safe(newroot_norm + '/dev/ptmx', 'pts/ptmx') fs_linux.mount_mqueue(newroot_norm, '/dev/mqueue') # Passthrough container log to host system logger. fs_linux.mount_bind(newroot_norm, '/dev/log', read_only=False)
def remount_cgroup(container_root, cgroup, root_cgroup): """remount cgroups path to put service cgroup as container root cgroup """ _LOGGER.info('Remounting cgroup paths') remount = _get_remount_cgroup(container_root, cgroup, root_cgroup) for (target_path, app_subsystem_path) in remount: # unmount & mount from /sys/fs/cgroups/<subsystem>/<group> to root dir fs_linux.umount_filesystem(app_subsystem_path) # dockerd needs to write cgroup path fs_linux.mount_bind('/', app_subsystem_path, target_path, read_only=False)
def _bind_overlay_docker(container_dir, root_dir): """Mount etc/hosts for docker container """ # FIXME: This path is mounted as RW because ro volume in treadmill # container can not be mounted in docker 'Error response from # daemon: chown /etc/hosts: read-only file system.' overlay_dir = os.path.join(container_dir, 'overlay') fs_linux.mount_bind(root_dir, os.path.join(os.sep, _CONTAINER_DOCKER_ETC_DIR, 'hosts'), source=os.path.join(overlay_dir, 'etc/hosts'), recursive=False, read_only=False)
def create_environ_dir(container_dir, root_dir, app): """Creates environ dir for s6-envdir.""" env_dir = os.path.join(container_dir, _CONTAINER_ENV_DIR) env = { 'TREADMILL_APP': app.app, 'TREADMILL_CELL': app.cell, 'TREADMILL_CPU': app.cpu, 'TREADMILL_DISK': app.disk, 'TREADMILL_HOST_IP': app.network.external_ip, 'TREADMILL_IDENTITY': app.identity, 'TREADMILL_IDENTITY_COUNT': app.identity_count, 'TREADMILL_IDENTITY_GROUP': app.identity_group, 'TREADMILL_INSTANCEID': app.task, 'TREADMILL_MEMORY': app.memory, 'TREADMILL_PROID': app.proid, 'TREADMILL_ENV': app.environment, } for endpoint in app.endpoints: envname = 'TREADMILL_ENDPOINT_{0}'.format(endpoint.name.upper()) env[envname] = endpoint.real_port env['TREADMILL_EPHEMERAL_TCP_PORTS'] = ' '.join( [str(port) for port in app.ephemeral_ports.tcp] ) env['TREADMILL_EPHEMERAL_UDP_PORTS'] = ' '.join( [str(port) for port in app.ephemeral_ports.udp] ) env['TREADMILL_CONTAINER_IP'] = app.network.vip env['TREADMILL_GATEWAY_IP'] = app.network.gateway if app.shared_ip: env['TREADMILL_SERVICE_IP'] = app.network.external_ip supervisor.create_environ_dir(env_dir, env) # Bind the environ directory in the container volume fs.mkdir_safe(os.path.join(root_dir, _CONTAINER_ENV_DIR)) fs_linux.mount_bind( root_dir, os.path.join(os.sep, _CONTAINER_ENV_DIR), source=os.path.join(container_dir, _CONTAINER_ENV_DIR), recursive=False, read_only=True ) # envs variables that will be applied in docker container _docker.create_docker_environ_dir(container_dir, root_dir, app)
def share_cgroup_info(root_dir, cgrp): """Shares subset of cgroup tree with the container.""" # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory # of the container. # FIXME: This should be removed and proper cgroups should be # exposed (readonly). This is so that tools that # (correctly) read /proc/self/cgroups can access cgroup # data. shared_subsystems = ['memory'] for subsystem in shared_subsystems: fs.mkdir_safe(os.path.join(root_dir, 'cgroup', subsystem)) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'cgroup', subsystem), source=cgroups.makepath(subsystem, cgrp), recursive=True, read_only=False)
def create_docker_environ_dir(container_dir, root_dir, app): """Creates environ dir for docker""" env_dir = os.path.join(container_dir, _CONTAINER_DOCKER_ENV_DIR) env = {} if app.ephemeral_ports.tcp or app.ephemeral_ports.udp: env['LD_PRELOAD'] = os.path.join(_manifest.TREADMILL_BIND_PATH, '$LIB', 'treadmill_bind_preload.so') supervisor.create_environ_dir(env_dir, env) # Bind the environ directory in the container volume fs.mkdir_safe(os.path.join(root_dir, _CONTAINER_DOCKER_ENV_DIR)) fs_linux.mount_bind(root_dir, os.path.join(os.sep, _CONTAINER_DOCKER_ENV_DIR), source=os.path.join(container_dir, _CONTAINER_DOCKER_ENV_DIR), recursive=False, read_only=True)
def _bind_overlay(container_dir, root_dir): """Create the overlay in the container.""" # Overlay overrides container configs # - /etc/resolv.conf, so that container always uses dnscache. # - pam.d sshd stack with special sshd pam that unshares network. # - /etc/ld.so.preload to enforce necessary system hooks # overlay_dir = os.path.join(container_dir, 'overlay') for overlay_file in [ 'etc/hosts', 'etc/krb5.keytab', 'etc/ld.so.preload', 'etc/pam.d/sshd', 'etc/resolv.conf' ]: fs_linux.mount_bind(root_dir, os.path.join(os.sep, overlay_file), source=os.path.join(overlay_dir, overlay_file), recursive=False, read_only=True) # Mount host-aliases as read-write fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'run', 'host-aliases'), source=os.path.join(overlay_dir, 'run', 'host-aliases'), recursive=False, read_only=False) # Also override resolv.conf in the current mount namespace so that # system services have access to our resolver. fs_linux.mount_bind('/', '/etc/resolv.conf', source=os.path.join(overlay_dir, 'etc/resolv.conf'), recursive=False, read_only=True)
def _bind_overlay(container_dir, root_dir): """Create the overlay in the container. :param ``str`` container_dir: Base directory of container data/config. :param ``str`` root_dir: New root directory of the container. """ # Overlay overrides container configs # - /etc/resolv.conf, so that container always uses dnscache. # - pam.d sshd stack with special sshd pam that unshares network. # - /etc/ld.so.preload to enforce necessary system hooks # overlay_dir = os.path.join(container_dir, 'overlay') etc_overlay_dir = os.path.join(overlay_dir, 'etc') for (basedir, _dirs, files) in os.walk(etc_overlay_dir): # We bind mount read-only all etc overlay files. for file_ in files: overlay_file = os.path.join(basedir, file_) target_file = os.path.relpath(overlay_file, overlay_dir) fs_linux.mount_bind(root_dir, os.path.join(os.sep, target_file), source=overlay_file, recursive=False, read_only=True) # Mount host-aliases as read-write fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'run', 'host-aliases'), source=os.path.join(overlay_dir, 'run', 'host-aliases'), recursive=False, read_only=False) # Also override resolv.conf in the current mount namespace so that # system services have access to our resolver. fs_linux.mount_bind('/', '/etc/resolv.conf', source=os.path.join(overlay_dir, 'etc/resolv.conf'), recursive=False, read_only=True)
def create_supervision_tree(tm_env, container_dir, root_dir, app, cgroups_path): """Creates s6 supervision tree.""" uniq_name = appcfg.app_unique_name(app) ctl_uds = os.path.join(os.sep, 'run', 'tm_ctl') tombstone_ctl_uds = os.path.join(ctl_uds, 'tombstone') sys_dir = os.path.join(container_dir, 'sys') try: old_system_services = [ svc_name for svc_name in os.listdir(sys_dir) if (not svc_name.startswith('.') and os.path.isdir(os.path.join(sys_dir, svc_name))) ] except FileNotFoundError: old_system_services = [] new_system_services = [svc_def.name for svc_def in app.system_services] for svc_name in set(old_system_services) - set(new_system_services): _LOGGER.info('Removing old system service: %s', svc_name) fs.rmtree_safe(os.path.join(sys_dir, svc_name)) sys_scandir = supervisor.create_scan_dir( sys_dir, finish_timeout=6000, wait_cgroups=cgroups_path, ) for svc_def in app.system_services: if svc_def.restart is not None: monitor_policy = { 'limit': svc_def.restart.limit, 'interval': svc_def.restart.interval, 'tombstone': { 'uds': False, 'path': tm_env.services_tombstone_dir, 'id': '{},{}'.format(uniq_name, svc_def.name) } } else: monitor_policy = None supervisor.create_service( sys_scandir, name=svc_def.name, app_run_script=svc_def.command, userid='root', environ_dir=os.path.join(container_dir, _CONTAINER_ENV_DIR), environ={envvar.name: envvar.value for envvar in svc_def.environ}, environment=app.environment, downed=svc_def.downed, trace=None, monitor_policy=monitor_policy) sys_scandir.write() services_dir = os.path.join(container_dir, 'services') services_scandir = supervisor.create_scan_dir(services_dir, finish_timeout=5000) for svc_def in app.services: if svc_def.restart is not None: monitor_policy = { 'limit': svc_def.restart.limit, 'interval': svc_def.restart.interval, 'tombstone': { 'uds': True, 'path': tombstone_ctl_uds, 'id': '{},{}'.format(uniq_name, svc_def.name) } } else: monitor_policy = None if svc_def.trace is not None: trace = { 'instanceid': app.name, 'uniqueid': app.uniqueid, 'service': svc_def.name, 'path': os.path.join(ctl_uds, 'appevents') } else: trace = None logger_template = getattr(svc_def, 'logger', 's6.app-logger.run') _LOGGER.info('Using logger: %s', logger_template) supervisor.create_service( services_scandir, name=svc_def.name, app_run_script=svc_def.command, userid=svc_def.proid, environ_dir='/' + _CONTAINER_ENV_DIR, environ={envvar.name: envvar.value for envvar in svc_def.environ}, environment=app.environment, downed=svc_def.downed, trace=trace if svc_def.trace else None, log_run_script=logger_template, monitor_policy=monitor_policy) services_scandir.write() # Bind the service directory in the container volume fs.mkdir_safe(os.path.join(root_dir, 'services')) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'services'), source=os.path.join(container_dir, 'services'), recursive=False, read_only=False) # Bind the ctrl directory in the container volume which has all the # unix domain sockets to communicate outside the container to treadmill fs.mkdir_safe(os.path.join(root_dir, 'run', 'tm_ctl')) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'run', 'tm_ctl'), source=tm_env.ctl_dir, recursive=False, read_only=False)
def make_fsroot(root_dir, app, data): """Initializes directory structure for the container in a new root. The container uses pretty much a blank a FHS 3 layout. - Bind directories in parent / (with exceptions - see below.) - Skip /tmp, create /tmp in the new root with correct permissions. - Selectively create / bind /var. - /var/tmp (new) - /var/log (new) - /var/spool - create empty with dirs. - Bind everything in /var, skipping /spool/tickets :param ``str`` root_dit: Container root directory. :param app: Container app manifest. :param ``dict`` data: Local configuration data. """ newroot_norm = fs.norm_safe(root_dir) emptydirs = [ '/bin', '/dev', '/etc', '/home', '/lib', '/lib64', '/opt', '/proc', '/root', '/run', '/sbin', '/sys', '/tmp', '/usr', '/var/cache', '/var/empty', '/var/empty/sshd', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/spool', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', # for SSS '/var/lib/sss', ] stickydirs = [ '/opt', '/run', '/tmp', '/var/cache', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', ] # these folders are shared with underlying host and other containers, mounts = [ '/bin', '/etc', # TODO: Add /etc/opt '/lib', '/lib64', '/root', '/sbin', '/usr', # for SSS '/var/lib/sss', # TODO: Remove below once PAM UDS is implemented os.path.expandvars('${TREADMILL_APPROOT}/env'), os.path.expandvars('${TREADMILL_APPROOT}/spool'), ] for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX) # /var/empty must be owned by root and not group or world-writable. os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711) # Mount a new sysfs for the container, bring in the /sys/fs subtree from # the host. fs_linux.mount_sysfs(newroot_norm) fs_linux.mount_bind(newroot_norm, os.path.join(os.sep, 'sys', 'fs'), recursive=True, read_only=False) make_dev(newroot_norm) # Passthrough node devices per the node config data extra_devices = data.get('runtime', {}).get('passthrough_devices', []) make_extra_dev(newroot_norm, extra_devices, app.proid) # Per FHS3 /var/run should be a symlink to /run which should be tmpfs fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run') # We create an unbounded tmpfs mount so that runtime data can be written to # it, counting against the memory limit of the container. fs_linux.mount_tmpfs(newroot_norm, '/run') # Make shared directories/files readonly to container for mount in mounts: if os.path.exists(mount): fs_linux.mount_bind(newroot_norm, mount, recursive=True, read_only=True) # /etc/docker is a file neceesary for docker daemon _docker.mount_docker_daemon_path(newroot_norm, app)
def make_fsroot(root_dir, app): """Initializes directory structure for the container in a new root. The container uses pretty much a blank a FHS 3 layout. - Bind directories in parent / (with exceptions - see below.) - Skip /tmp, create /tmp in the new root with correct permissions. - Selectively create / bind /var. - /var/tmp (new) - /var/log (new) - /var/spool - create empty with dirs. - Bind everything in /var, skipping /spool/tickets tm_env is used to deliver abort events """ newroot_norm = fs.norm_safe(root_dir) emptydirs = [ '/bin', '/dev', '/etc', '/home', '/lib', '/lib64', '/opt', '/proc', '/root', '/run', '/sbin', '/sys', '/tmp', '/usr', '/var/cache', '/var/empty', '/var/empty/sshd', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/spool', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', # for SSS '/var/lib/sss', ] stickydirs = [ '/opt', '/run', '/tmp', '/var/cache', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', ] # these folders are shared with underlying host and other containers, mounts = [ '/bin', '/etc', # TODO: Add /etc/opt '/lib', '/lib64', '/root', '/sbin', '/usr', # for SSS '/var/lib/sss', # TODO: Remove below once PAM UDS is implemented os.path.expandvars('${TREADMILL_APPROOT}/env'), os.path.expandvars('${TREADMILL_APPROOT}/spool'), ] for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX) # /var/empty must be owned by root and not group or world-writable. os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711) fs_linux.mount_bind(newroot_norm, os.path.join(os.sep, 'sys'), source='/sys', recursive=True, read_only=False) make_dev(newroot_norm) # Per FHS3 /var/run should be a symlink to /run which should be tmpfs fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run') # We create an unbounded tmpfs mount so that runtime data can be written to # it, counting against the memory limit of the container. fs_linux.mount_tmpfs(newroot_norm, '/run') # Make shared directories/files readonly to container for mount in mounts: if os.path.exists(mount): fs_linux.mount_bind(newroot_norm, mount, recursive=True, read_only=True) if hasattr(app, 'docker') and app.docker: # If unable to mount docker directory, we throw Aborted events try: _mount_docker_tmpfs(newroot_norm) except FileNotFoundError as err: _LOGGER.error('Failed to mount docker tmpfs: %s', err) # this exception is caught by sproc run to generate abort event raise exc.ContainerSetupError( msg=str(err), reason=app_abort.AbortedReason.UNSUPPORTED, )
def make_fsroot(root_dir): """Initializes directory structure for the container in a new root. The container uses pretty much a blank a FHS 3 layout. - Bind directories in parent / (with exceptions - see below.) - Skip /tmp, create /tmp in the new root with correct permissions. - Selectively create / bind /var. - /var/tmp (new) - /var/log (new) - /var/spool - create empty with dirs. - Bind everything in /var, skipping /spool/tickets """ newroot_norm = fs.norm_safe(root_dir) emptydirs = [ '/bin', '/dev', '/etc', '/home', '/lib', '/lib64', '/opt', '/proc', '/root', '/run', '/sbin', '/sys', '/tmp', '/usr', '/var/cache', '/var/empty', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/spool', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', # for SSS '/var/lib/sss', ] stickydirs = [ '/opt', '/run', '/tmp', '/var/cache', '/var/lib', '/var/lock', '/var/log', '/var/opt', '/var/tmp', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', ] # these folders are shared with underlying host and other containers, mounts = [ '/bin', '/etc', # TODO: Add /etc/opt '/lib', '/lib64', '/root', '/sbin', '/usr', # for SSS '/var/lib/sss', # TODO: Remove below once PAM UDS is implemented '/var/tmp/treadmill/env', '/var/tmp/treadmill/spool', ] # Add everything under /opt mounts += glob.glob('/opt/*') for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX) # /var/empty must be owned by root and not group or world-writable. os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711) fs_linux.mount_bind(newroot_norm, os.path.join(os.sep, 'sys'), source='/sys', recursive=True, read_only=False) # TODO: For security, /dev/ should be minimal and separated to each # container. fs_linux.mount_bind(newroot_norm, os.path.join(os.sep, 'dev'), source='/dev', recursive=True, read_only=False) # Per FHS3 /var/run should be a symlink to /run which should be tmpfs fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run') # We create an unbounded tmpfs mount so that runtime data can be written to # it, counting against the memory limit of the container. fs_linux.mount_tmpfs(newroot_norm, '/run') # Make shared directories/files readonly to container for mount in mounts: if os.path.exists(mount): fs_linux.mount_bind(newroot_norm, mount, recursive=True, read_only=True)
def create_supervision_tree(container_dir, root_dir, app, cgroups_path): """Creates s6 supervision tree.""" sys_dir = os.path.join(container_dir, 'sys') sys_scandir = supervisor.create_scan_dir( sys_dir, finish_timeout=6000, monitor_service='monitor', wait_cgroups=cgroups_path, ) for svc_def in app.system_services: if svc_def.restart is not None: monitor_policy = { 'limit': svc_def.restart.limit, 'interval': svc_def.restart.interval, } else: monitor_policy = None supervisor.create_service( sys_scandir, name=svc_def.name, app_run_script=svc_def.command, userid='root', environ_dir=os.path.join(container_dir, _CONTAINER_ENV_DIR), environ={envvar.name: envvar.value for envvar in svc_def.environ}, environment=app.environment, downed=svc_def.downed, trace=None, monitor_policy=monitor_policy) sys_scandir.write() services_dir = os.path.join(container_dir, 'services') services_scandir = supervisor.create_scan_dir(services_dir, finish_timeout=5000) trace = {'instanceid': app.name, 'uniqueid': app.uniqueid} for svc_def in app.services: if svc_def.restart is not None: monitor_policy = { 'limit': svc_def.restart.limit, 'interval': svc_def.restart.interval, } else: monitor_policy = None supervisor.create_service( services_scandir, name=svc_def.name, app_run_script=svc_def.command, userid=svc_def.proid, environ_dir='/' + _CONTAINER_ENV_DIR, environ={envvar.name: envvar.value for envvar in svc_def.environ}, environment=app.environment, downed=False, trace=trace if svc_def.trace else None, log_run_script='s6.app-logger.run', monitor_policy=monitor_policy) services_scandir.write() # Bind the service directory in the container volume fs.mkdir_safe(os.path.join(root_dir, 'services')) fs_linux.mount_bind(root_dir, os.path.join(os.sep, 'services'), source=os.path.join(container_dir, 'services'), recursive=False, read_only=False)