def test_mount_bind_dir(self): """Tests fs.mount_bind directory binding behavior""" # test binding directory in / container_dir = os.path.join(self.root, 'container') os.makedirs(container_dir) # test binding directory in / foo_dir = os.path.join(self.root, 'foo') os.makedirs(foo_dir) fs.mount_bind(container_dir, foo_dir) treadmill.subproc.check_call.assert_called_with([ 'mount', '-n', '--rbind', foo_dir, os.path.join(container_dir, foo_dir[1:]), ]) self.assertTrue(os.path.isdir(os.path.join(container_dir, foo_dir[1:]))) treadmill.subproc.check_call.reset_mock() # test binding directory with subdirs bar_dir = os.path.join(self.root, 'bar') os.makedirs(os.path.join(bar_dir, 'baz')) fs.mount_bind(container_dir, bar_dir) treadmill.subproc.check_call.assert_called_with([ 'mount', '-n', '--rbind', bar_dir, os.path.join(container_dir, bar_dir[1:]) ]) self.assertTrue(os.path.isdir(os.path.join(container_dir, bar_dir[1:]))) treadmill.subproc.check_call.reset_mock()
def configure(self, container_dir, app): root_dir = os.path.join(container_dir, 'root') newroot_norm = fs.norm_safe(root_dir) mounts = [ ] emptydirs = [ '/u', '/var/account', '/var/empty', '/var/lock', '/var/log', '/var/run', ] stickydirs = [ '/opt', ] for mount in mounts: if os.path.exists(mount): fs.mount_bind(newroot_norm, mount) for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)
def configure(_approot, newroot, _app): """Configure layout in chroot.""" newroot_norm = fs.norm_safe(newroot) mounts = [ ] emptydirs = [ '/u', '/var/account', '/var/empty', '/var/lock', '/var/log', '/var/run', ] stickydirs = [ '/opt', ] for mount in mounts: if os.path.exists(mount): fs.mount_bind(newroot_norm, mount) for directory in emptydirs: fs.mkdir_safe(newroot_norm + directory) for directory in stickydirs: os.chmod(newroot_norm + directory, 0777 | stat.S_ISVTX)
def test_mount_bind_dir(self): """Tests fs.mount_bind directory binding behavior""" # test binding directory in / fs.mount_bind(self.root, '/bin') treadmill.subproc.check_call.assert_called_with( [ 'mount', '-n', '--rbind', '/bin', os.path.join(self.root, 'bin'), ] ) treadmill.subproc.check_call.reset_mock() self.assertTrue(os.path.isdir('%s/bin' % (self.root))) # test binding directory with subdirs fs.mount_bind(self.root, '/var/spool/tickets') treadmill.subproc.check_call.assert_called_with( [ 'mount', '-n', '--rbind', '/var/spool/tickets', os.path.join(self.root, 'var/spool/tickets'), ] ) treadmill.subproc.check_call.reset_mock() self.assertTrue(os.path.isdir('%s/var/spool/tickets' % (self.root)))
def test_mount_bind_file(self): """Verifies correct mount options for files vs dirs.""" fs.mount_bind(self.root, '/bin/ls') treadmill.subproc.check_call.assert_called_with( [ 'mount', '-n', '--bind', '/bin/ls', os.path.join(self.root, 'bin/ls'), ] ) treadmill.subproc.check_call.reset_mock() self.assertTrue(os.path.isfile('%s/bin/ls' % (self.root))) fs.mount_bind(self.root, '/lib/libc.so.6') treadmill.subproc.check_call.assert_called_with( [ 'mount', '-n', '--bind', '/lib/libc.so.6', os.path.join(self.root, 'lib/libc.so.6'), ] ) treadmill.subproc.check_call.reset_mock() self.assertTrue(os.path.isfile('%s/lib/libc.so.6' % (self.root)))
def _bind(src, tgt): """Helper function to bind source to target in the same root""" # FIXME(boysson): This name mount_bind() have counter-intuitive # arguments ordering. src_path = os.path.join(root_dir, src) if os.path.exists(src_path): fs.mount_bind(root_dir, tgt, target=src_path, bind_opt='--bind')
def share_cgroup_info(app, root_dir): """Shares subset of cgroup tree with the container.""" # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory # of the container. unique_name = appcfg.app_unique_name(app) cgrp = os.path.join('treadmill', 'apps', unique_name) # 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.mount_bind(root_dir, os.path.join('/cgroup', subsystem), cgroups.makepath(subsystem, cgrp))
def test_mount_bind_file(self): """Verifies correct mount options for files vs dirs.""" container_dir = os.path.join(self.root, 'container') os.makedirs(container_dir) # test binding a file foo_file = os.path.join(self.root, 'foo') with open(os.path.join(self.root, 'foo'), 'w'): pass fs.mount_bind(container_dir, foo_file) treadmill.subproc.check_call.assert_called_with([ 'mount', '-n', '--bind', foo_file, os.path.join(container_dir, foo_file[1:]) ]) self.assertTrue( os.path.isfile(os.path.join(container_dir, foo_file[1:]))) treadmill.subproc.check_call.reset_mock()
def _bind_etc_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/host-aliases', 'etc/ld.so.preload', 'etc/pam.d/sshd', 'etc/resolv.conf' ]: fs.mount_bind(root_dir, os.path.join('/', overlay_file), target=os.path.join(overlay_dir, overlay_file), bind_opt='--bind') # Also override resolv.conf in the current mount namespace so that # system services have access to out resolver. fs.mount_bind('/', '/etc/resolv.conf', target=os.path.join(overlay_dir, 'etc/resolv.conf'), bind_opt='--bind')
def create_supervision_tree(container_dir, app): """Creates s6 supervision tree.""" # Disable R0915: Too many statements # pylint: disable=R0915 root_dir = os.path.join(container_dir, 'root') # Services and sys directories will be restored when container restarts # with data retention on existing volume. # # Sys directories will be removed. Services directory will stay, which # present a danger of accumulating restart counters in finished files. # # TODO: # # It is rather arbitrary how restart counts should work when data is # restored, but most likely services are "restart always" policy, so it # will not affect them. services_dir = os.path.join(container_dir, 'services') sys_dir = os.path.join(container_dir, 'sys') if os.path.exists(sys_dir): _LOGGER.info('Deleting existing sys dir: %s', sys_dir) shutil.rmtree(sys_dir) app_json = os.path.join(root_dir, 'app.json') # Create /services directory for the supervisor svcdir = os.path.join(root_dir, 'services') fs.mkdir_safe(svcdir) fs.mkdir_safe(services_dir) fs.mount_bind(root_dir, '/services', services_dir) root_pw = pwd.getpwnam('root') proid_pw = pwd.getpwnam(app.proid) # Create .s6-svscan directories for svscan finish sys_svscandir = os.path.join(sys_dir, '.s6-svscan') fs.mkdir_safe(sys_svscandir) svc_svscandir = os.path.join(services_dir, '.s6-svscan') fs.mkdir_safe(svc_svscandir) # svscan finish scripts to wait on all services utils.create_script(os.path.join(sys_svscandir, 'finish'), 'svscan.finish', timeout=6000) utils.create_script(os.path.join(svc_svscandir, 'finish'), 'svscan.finish', timeout=5000) for svc in app.services: if getattr(svc, 'root', False): svc_user = '******' svc_home = root_pw.pw_dir svc_shell = root_pw.pw_shell else: svc_user = app.proid svc_home = proid_pw.pw_dir svc_shell = proid_pw.pw_shell supervisor.create_service( services_dir, svc_user, svc_home, svc_shell, svc.name, svc.command, env=app.environment, down=True, envdirs=['/environ/app', '/environ/sys'], as_root=True, ) _create_logrun(os.path.join(services_dir, svc.name)) for svc in app.system_services: supervisor.create_service( services_dir, 'root', root_pw.pw_dir, root_pw.pw_shell, svc.name, svc.command, env=app.environment, down=False, envdirs=['/environ/sys'], as_root=True, ) _create_logrun(os.path.join(services_dir, svc.name)) # Vring services for cell in app.vring.cells: fs.mkdir_safe(os.path.join(sys_dir, 'vring.%s' % cell)) cmd = '%s sproc --zookeeper - --cell %s vring %s' % ( treadmill.TREADMILL_BIN, cell, app_json) utils.create_script(os.path.join(sys_dir, 'vring.%s' % cell, 'run'), 'supervisor.run_sys', cmd=cmd) _create_logrun(os.path.join(sys_dir, 'vring.%s' % cell)) # Create endpoint presence service presence_monitor_cmd = '%s sproc presence monitor %s %s' % ( treadmill.TREADMILL_BIN, app_json, container_dir) presence_register_cmd = '%s sproc presence register %s %s' % ( treadmill.TREADMILL_BIN, app_json, container_dir) shadow_etc = os.path.join(container_dir, 'overlay', 'etc') host_aliases_cmd = '%s sproc host-aliases --aliases-dir %s %s %s' % ( treadmill.TREADMILL_BIN, os.path.join(shadow_etc, 'host-aliases'), os.path.join(shadow_etc, 'hosts.original'), os.path.join(shadow_etc, 'hosts'), ) _create_sysrun(sys_dir, 'monitor', presence_monitor_cmd) _create_sysrun(sys_dir, 'register', presence_register_cmd) _create_sysrun(sys_dir, 'hostaliases', host_aliases_cmd) cmd = None args = None if hasattr(app, 'command'): cmd = app.command if hasattr(app, 'args'): args = app.args if not cmd: cmd = subproc.resolve('s6_svscan') if not args: args = ['/services'] _create_sysrun(sys_dir, 'start_container', '%s %s %s -m -p -i %s %s' % (subproc.resolve('chroot'), root_dir, subproc.resolve('pid1'), cmd, ' '.join(args)), down=True)
def make_fsroot(root, proid): """Initializes directory structure for the container in a new root. - 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/logs (new) - /var/spool - create empty with dirs. - Bind everything in /var, skipping /spool/tickets """ newroot_norm = fs.norm_safe(root) mounts = [ '/bin', '/common', '/dev', '/etc', '/home', '/lib', '/lib64', '/mnt', '/proc', '/sbin', '/srv', '/sys', '/usr', '/var/tmp/treadmill/env', '/var/tmp/treadmill/spool', ] + glob.glob('/opt/*') emptydirs = [ '/tmp', '/opt', '/var/empty', '/var/run', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', '/var/tmp', '/var/tmp/cores', ] stickydirs = [ '/tmp', '/opt', '/var/spool/keytabs', '/var/spool/tickets', '/var/spool/tokens', '/var/tmp', '/var/tmp/cores/', ] for directory in emptydirs: _LOGGER.debug('Creating empty dir: %s', directory) 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.mount_bind(newroot_norm, mount) # Mount .../tickets .../keytabs on tempfs, so that they will be cleaned # up when the container exits. # # TODO: Do we need to have a single mount for all tmpfs dirs? for tmpfsdir in [ '/var/spool/tickets', '/var/spool/keytabs', '/var/spool/tokens' ]: fs.mount_tmpfs(newroot_norm, tmpfsdir, '4M')