def test_create_service_optional(self): """Checks optional components of create service. """ pwd.getpwnam.return_value = self.mock_pwrow('test_shell', 'test_home') svc_dir = supervisor.create_scan_dir(self.root, 5000) supervisor.create_service(svc_dir, 'xx', 'ls -al', userid='proid1', monitor_policy={ 'limit': 5, 'interval': 60, 'tombstone': { 'uds': True, 'path': '/run/tm_ctl/tombstone', 'id': 'xx' } }, environ={'b': 'test2'}, trace={ 'instanceid': 'xx', 'uniqueid': 'ID1234', 'service': 'xx', 'path': '/run/tm_ctl/appevents' }) service_dir = os.path.join(self.root, 'xx') self.assertTrue(os.path.isdir(service_dir)) data_dir = os.path.join(service_dir, 'data') self.assertTrue(os.path.isfile(os.path.join(data_dir, 'app_start'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'run'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'finish'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'env/b')))
def test_create_service(self): """Checks various options when creating the service. """ pwd.getpwnam.return_value = self.mock_pwrow('test_shell', 'test_home') supervisor.create_service(self.root, 'xx', 'ls -al', userid='proid1', downed=True) service_dir = os.path.join(self.root, 'xx') self.assertTrue(os.path.isdir(service_dir)) data_dir = os.path.join(service_dir, 'data') self.assertTrue(os.path.isfile(os.path.join(data_dir, 'app_start'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'run'))) self.assertFalse(os.path.isfile(os.path.join(service_dir, 'finish'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'down'))) # Do not create down file. supervisor.create_service(self.root, 'bar', 'proid1', 'ls -al', downed=False) service_dir = os.path.join(self.root, 'bar') self.assertFalse(os.path.exists(os.path.join(service_dir, 'down')))
def _configure_locker(tkt_spool_dir, scandir, cell, celluser): """Configure ticket forwarding service.""" if os.path.exists(os.path.join(scandir, cell)): return _LOGGER.info('Configuring ticket locker: %s/%s', scandir, cell) name = cell realms = krb5.get_host_realm(sysinfo.hostname()) krb5ccname = 'FILE:{tkt_spool_dir}/{celluser}@{realm}'.format( tkt_spool_dir=tkt_spool_dir, celluser=celluser, realm=realms[0], ) supervisor.create_service( scandir, name=name, app_run_script=( '{treadmill}/bin/treadmill sproc ' 'tickets locker --tkt-spool-dir {tkt_spool_dir}'.format( treadmill=subproc.resolve('treadmill'), tkt_spool_dir=tkt_spool_dir)), userid='root', environ_dir=os.path.join(scandir, name, 'env'), environ={ 'KRB5CCNAME': krb5ccname, 'TREADMILL_CELL': cell, }, downed=False, trace=None, monitor_policy=None)
def _add_cleanup_app(self, path): """Configure a new cleanup app. """ name = os.path.basename(path) if name.startswith('.'): _LOGGER.warning('Ignore %s', name) return cleaning_link = os.path.join(self.tm_env.cleaning_dir, name) if os.path.islink(cleaning_link): _LOGGER.warning('Cleaning app already configured %s', name) return cleanup_link = os.path.join(self.tm_env.cleanup_dir, name) if not os.path.islink(cleanup_link): _LOGGER.info('Ignore - not a link: %s', cleanup_link) return _LOGGER.info('Configure cleaning app: %s', name) bin_name = 'scripts' if os.name == 'nt' else 'bin' command = ('{treadmill}/{bin}/treadmill sproc cleanup instance' ' --approot {tm_root}' ' {instance}').format( treadmill=subproc.resolve('treadmill'), bin=bin_name, tm_root=self.tm_env.root, instance=name) if os.name == 'posix': command = 'exec ' + command supervisor.create_service( self.tm_env.cleanup_apps_dir, name=name, app_run_script=command, userid='root', monitor_policy={ 'limit': 5, 'interval': 60, 'tombstone': { 'path': self.tm_env.cleanup_tombstone_dir, 'id': name, }, 'skip_path': os.path.join(self.tm_env.cleanup_dir, name) }, log_run_script=None, ) fs.symlink_safe(cleaning_link, os.path.join(self.tm_env.cleanup_apps_dir, name)) _LOGGER.debug('Cleanup app %s ready', name) self._refresh_supervisor()
def test_create_service_optional(self): """Checks optional components of create service. """ pwd.getpwnam.return_value = self.mock_pwrow('test_shell', 'test_home') svc_dir = supervisor.create_scan_dir(self.root, 5000) supervisor.create_service(svc_dir, 'xx', 'proid1', 'ls -al', monitor_policy={'a': 'test1'}, environ={'b': 'test2'}, trace={'c': 'test3'}) service_dir = os.path.join(self.root, 'xx') self.assertTrue(os.path.isdir(service_dir)) data_dir = os.path.join(service_dir, 'data') self.assertTrue(os.path.isfile(os.path.join(data_dir, 'app_start'))) self.assertTrue(os.path.isfile(os.path.join(data_dir, 'trace'))) self.assertTrue(os.path.isfile(os.path.join(data_dir, 'policy.json'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'run'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'finish'))) self.assertTrue(os.path.isfile(os.path.join(service_dir, 'env/b')))
def test_create_service(self): """Checks various options when creating the service.""" supervisor.create_service(self.root, 'proid1', 'proid1_home', 'proid1_shell', 'xx', 'ls -al', env='dev') service_dir = os.path.join(self.root, 'xx') self.assertTrue(os.path.isdir(service_dir)) self.assertTrue(os.path.isfile(service_dir + '/app_start')) self.assertTrue(os.path.isfile(service_dir + '/run')) self.assertTrue(os.path.isfile(service_dir + '/finish')) self.assertTrue(os.path.isfile(service_dir + '/down')) # as_root False, proid is None => Use current UID supervisor.create_service(self.root, None, 'home', 'shell', 'bla', 'ls -al', env='dev', as_root=False) service_dir = os.path.join(self.root, 'bla') # Do not create down file. supervisor.create_service(self.root, 'proid1', 'proid1_home', 'proid1_shell', 'bar', 'ls -al', env='dev', down=False) service_dir = os.path.join(self.root, 'bar') self.assertFalse(os.path.exists(service_dir + '/down'))
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 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 _install_services(scan_dir, package, src_dir, dst_dir, params, prefix_len, rec=None): """Expand services in scan directory and install. """ package_name = package.__name__ contents = pkg_resources.resource_listdir(package_name, src_dir) for item in contents: if item in (_CONTROL_DIR_NAME, _CONTROL_DIR_FILE): continue resource_path = os.path.join(src_dir, item) if pkg_resources.resource_isdir(package_name, os.path.join(src_dir, item)): dst_path = os.path.join(dst_dir, resource_path[prefix_len:]) fs.mkdir_safe(dst_path) if rec: rec.write('%s\n' % os.path.join(dst_path, '')) _install( package, os.path.join(src_dir, item), dst_dir, params, prefix_len=prefix_len, rec=rec ) elif resource_path.endswith('.yml'): dst_path = os.path.join(dst_dir, resource_path[prefix_len:-4]) name = os.path.basename(dst_path) _LOGGER.info('Expand service (%s): %s => %s', name, resource_path, dst_path) fs.mkdir_safe(dst_path) if rec: rec.write('%s\n' % os.path.join(dst_path, '')) service_conf_file = pkg_resources.resource_string( package_name, resource_path ) if not service_conf_file: _LOGGER.warning('Service def was empty: %s', resource_path) continue service_conf = yaml.load(service_conf_file.decode('utf8')) service_conf = bootstrap.interpolate_service_conf( resource_path, service_conf, name, params) svc = supervisor.create_service( scan_dir, service_conf['name'], service_conf['command'], userid=service_conf['userid'], downed=service_conf['downed'], environ_dir=service_conf['environ_dir'], environ=service_conf['environ'], monitor_policy=service_conf['monitor_policy'], notification_fd=service_conf['notification_fd'], call_before_run=service_conf['call_before_run'], call_before_finish=service_conf['call_before_finish'], logger_args=service_conf['logger_args'], ionice_prio=0, ) for file in service_conf['data_dir']: permission = 0o644 if file['executable']: permission = 0o755 fs.write_safe( os.path.join(svc.data_dir, file['path']), lambda f, file=file: f.write( file['content'] ), mode='w', permission=permission )
def configure(tm_env, event, runtime): """Creates directory necessary for starting the application. This operation is idem-potent (it can be repeated). The directory layout is:: - (treadmill root)/ - apps/ - (app unique name)/ - data/ - app_start - app.json - manifest.yml env/ - TREADMILL_* run finish log/ - run The 'run' script is responsible for creating container environment and starting the container. The 'finish' script is invoked when container terminates and will deallocate any resources (NAT rules, etc) that were allocated for the container. """ # Load the app from the event try: manifest_data = load_runtime_manifest(tm_env, event, runtime) except IOError: # File is gone. Nothing to do. _LOGGER.exception('No event to load: %r', event) return None # Freeze the app data into a namedtuple object app = utils.to_obj(manifest_data) # Generate a unique name for the app uniq_name = appcfg.app_unique_name(app) # Write the actual container start script if os.name == 'nt': run_script = '{treadmill}/scripts/treadmill sproc run .'.format( treadmill=subproc.resolve('treadmill'), ) else: run_script = 'exec {treadmill}/bin/treadmill sproc run ../'.format( treadmill=subproc.resolve('treadmill'), ) # Create the service for that container container_svc = supervisor.create_service( tm_env.apps_dir, name=uniq_name, app_run_script=run_script, userid='root', downed=False, monitor_policy={ 'limit': 0, 'interval': 60, 'tombstone': { 'uds': False, 'path': tm_env.running_tombstone_dir, 'id': app.name } }, environ={}, environment=app.environment ) data_dir = container_svc.data_dir # Copy the original event as 'manifest.yml' in the container dir try: shutil.copyfile( event, os.path.join(data_dir, 'manifest.yml') ) except IOError as err: # File is gone, cleanup. if err.errno == errno.ENOENT: shutil.rmtree(container_svc.directory) _LOGGER.exception('Event gone: %r', event) return None else: raise # Store the app.json in the container directory fs.write_safe( os.path.join(data_dir, appcfg.APP_JSON), lambda f: f.writelines( utils.json_genencode(manifest_data) ), mode='w', permission=0o644 ) appevents.post( tm_env.app_events_dir, events.ConfiguredTraceEvent( instanceid=app.name, uniqueid=app.uniqueid ) ) return container_svc.directory
def _add_cleanup_app(self, path): """Configure a new cleanup app. """ name = os.path.basename(path) if name.startswith('.'): _LOGGER.warning('Ignore %s', name) return cleaning_link = os.path.join(self.tm_env.cleaning_dir, name) if os.path.islink(cleaning_link): _LOGGER.warning('Cleaning app already configured %s', name) return cleanup_link = os.path.join(self.tm_env.cleanup_dir, name) if not os.path.islink(cleanup_link): _LOGGER.info('Ignore - not a link: %s', cleanup_link) return _LOGGER.info('Configure cleaning app: %s', name) if os.name == 'posix': command = ( 'exec {tm} sproc cleanup instance' ' --approot {tm_root}' ' {instance}' ).format( tm=dist.TREADMILL_BIN, tm_root=self.tm_env.root, instance=name ) else: command = ( '{python} -m treadmill.ms sproc cleanup instance' ' --approot {tm_root}' ' {instance}' ).format( python=sys.executable, tm_root=self.tm_env.root, instance=name ) supervisor.create_service( self.tm_env.cleanup_apps_dir, name=name, app_run_script=command, userid='root', monitor_policy={ 'limit': 5, 'interval': 60, }, log_run_script=None, ) fs.symlink_safe( cleaning_link, os.path.join(self.tm_env.cleanup_apps_dir, name) ) _LOGGER.debug('Cleanup app %s ready', name) self._refresh_supervisor()
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)
def configure(tm_env, event, runtime): """Creates directory necessary for starting the application. This operation is idem-potent (it can be repeated). The directory layout is:: - (treadmill root)/ - apps/ - (app unique name)/ - data/ - app_start - app.json - manifest.yml - policy.json env/ - TREADMILL_* run finish log/ - run The 'run' script is responsible for creating container environment and starting the container. The 'finish' script is invoked when container terminates and will deallocate any resources (NAT rules, etc) that were allocated for the container. """ # Load the app from the event try: manifest_data = app_manifest.load(tm_env, event, runtime) except IOError: # File is gone. Nothing to do. _LOGGER.exception('No event to load: %r', event) return # Freeze the app data into a namedtuple object app = utils.to_obj(manifest_data) # Generate a unique name for the app uniq_name = appcfg.app_unique_name(app) # Write the actual container start script if os.name == 'nt': run_script = ' '.join( [sys.executable, '-m', 'treadmill.ms', 'sproc', 'run', '.']) else: run_script = ' '.join( ['exec', dist.TREADMILL_BIN, 'sproc', 'run', '../']) # Create the service for that container container_svc = supervisor.create_service(tm_env.apps_dir, name=uniq_name, app_run_script=run_script, userid='root', downed=False, monitor_policy={ 'limit': 0, 'interval': 60 }, environ={}, environment=app.environment) data_dir = container_svc.data_dir # Copy the original event as 'manifest.yml' in the container dir shutil.copyfile(event, os.path.join(data_dir, 'manifest.yml')) # Store the app.json in the container directory fs.write_safe(os.path.join(data_dir, appcfg.APP_JSON), lambda f: f.writelines(utils.json_genencode(manifest_data)), mode='w', permission=0o644) appevents.post( tm_env.app_events_dir, events.ConfiguredTraceEvent(instanceid=app.name, uniqueid=app.uniqueid)) return container_svc.directory