modular_api_cli/modular_cli_group/modular.py (160 lines of code) (raw):

import multiprocessing import bottle import click from modular_api.helpers.decorators import BaseCommand, ResponseDecorator from modular_api.helpers.exceptions import ModularApiBadRequestException from modular_api.helpers.log_helper import init_console_handler, get_logger from modular_api.index import WSGIApplicationBuilder, initialize from modular_api.services import SERVICE_PROVIDER from modular_api.services import SP from modular_api.services.install_service import ( check_and_describe_modules, install_module, uninstall_module, ) from modular_api.version import __version__ from modular_api_cli.modular_cli_group.group import group from modular_api_cli.modular_cli_group.policy import policy from modular_api_cli.modular_cli_group.user import user, user_handler_instance from modular_api_cli.modular_handler.audit_handler import AuditHandler from modular_api_cli.modular_handler.usage_handler import UsageHandler _LOG = get_logger(__name__) DEFAULT_NUMBER_OF_WORKERS = (multiprocessing.cpu_count() * 2) + 1 def audit_handler_instance(): return AuditHandler(audit_service=SERVICE_PROVIDER.audit_service) def stats_handler_instance(): return UsageHandler(usage_service=SERVICE_PROVIDER.usage_service) @click.group() @click.version_option(__version__, '-v', '--version') def modular(): """ Configuration settings for modular-api """ @modular.command(name='run') @click.option('--host', '-h', default='0.0.0.0', required=False, type=str, help='Host to start the server', show_default=True) @click.option('--port', '-p', default=8085, type=int, required=False, help='Host to start the server', show_default=True) @click.option('--prefix', '-pr', type=str, required=False, default='', help='Global api prefix. By default there is no prefix') @click.option('--gunicorn', '-g', is_flag=True, help='Whether to run the server using gunicorn') @click.option('--workers', '-nw', required=False, type=int, default=DEFAULT_NUMBER_OF_WORKERS, show_default=True, help='Number of gunicorn workers. Has effect only if ' '--gunicorn flag is set') @click.option('--worker_timeout', '-wt', default=0, type=int, help='Gunicorn worker timeout in seconds. ' 'By default there is no timeout') @click.option('--swagger', is_flag=True, help='Whether to server swagger') @click.option('--swagger_prefix', type=str, default='/swagger', help='Swagger path prefix', show_default=True) def run(host: str, port: int, prefix: str, gunicorn: bool, workers: int, worker_timeout: int, swagger: bool, swagger_prefix: str): """ Starts modular server """ init_console_handler() initialize() application = WSGIApplicationBuilder( env=SP.env, prefix=prefix, swagger=swagger, swagger_prefix=swagger_prefix ).build() if gunicorn: from modular_api.web_service.app_gunicorn import \ ModularAdminGunicornApplication options = { 'bind': f'{host}:{port}', 'workers': workers, 'timeout': worker_timeout, 'max_requests': 50, # to limit the damage of memory leaks 'max_requests_jitter': 20 } ModularAdminGunicornApplication(application, options).run() else: bottle.run(application, host=host, port=port) @modular.command(cls=BaseCommand) @click.option('--module_path', '-path', type=str, required=True, help='Path to needed tool source files') @ResponseDecorator(click.echo, 'Can not install module') def install(module_path): """ Install module """ return install_module(module_path) @modular.command(cls=BaseCommand) @click.option('--module_name', '-name', type=str, required=True, help='Name of the module to be uninstalled') @ResponseDecorator(click.echo, 'Can not uninstall module') def uninstall(module_name): """ Uninstall module """ return uninstall_module(module_name) @modular.command(cls=BaseCommand, name='describe') @ResponseDecorator(click.echo, 'Can not describe module', custom_view=True) def describe(json, table): """ Describe all available modules versions and Modular-SDK/CLI version. """ return check_and_describe_modules(table_response=table, json_response=json) @modular.command(cls=BaseCommand, name='get_stats') @click.option('--from_month', '-fm', type=str, help='Filter by month from which records are displayed. ' 'Format "yyyy-mm". If not specified - current month will ' 'be used') @click.option('--to_month', '-tm', type=str, help='Filter by month until which records are displayed. ' 'Format yyyy-mm. If not specified - next month will be ' 'used') @click.option('--display_table', '-D', is_flag=True, help='Flag to show report in terminal. If not specified - report ' 'will be stored into the file') @click.option('--path', '-p', type=str, help='Directory path to saving report file. If not specified - ' 'report will be saved in user home directory') @ResponseDecorator(click.echo, 'Can not get statistic') def get_stats(from_month, to_month, display_table, path): """ Saves usage statistic to a file """ return stats_handler_instance().get_stats_handler( from_month=from_month, to_month=to_month, display_table=display_table, path=path) @modular.command(cls=BaseCommand, name='audit') @click.option('--group', '-g', type=str, help='Filter by group name. If "--group" and "--from_date" and ' '"--to_date" parameters not specified - will be shown all ' 'events for the last 7 days') @click.option('--command', '-c', type=str, help='Filter by command name') @click.option('--from_date', '-fd', type=str, help='Filter by date from which records are displayed. ' 'Format yyyy-mm-dd') @click.option('--to_date', '-td', type=str, help='Filter by date until which records are displayed. ' 'Format yyyy-mm-dd') @click.option('--limit', '-l', type=click.IntRange(min=1, max=100), default=10, help='Number of records that will be shown. Default value is 10.' 'Will have no effect if "--group" not specified') @click.option('--invalid', '-I', is_flag=True, help='Flag to show only invalid audit events.') @ResponseDecorator(click.echo, 'Can not describe audit') def audit(group, command, from_date, to_date, limit, invalid): """ Describes audit """ return audit_handler_instance().describe_audit_handler( group=group, command=command, from_date=from_date, to_date=to_date, limit=limit, invalid=invalid, ) @modular.command(cls=BaseCommand, name='policy_simulator') @click.option('--user', '-u', type=str, help='User which permissions will be checked') @click.option('--group', '-g', type=str, help='Group which permissions will be checked') @click.option('--policy', '-p', type=str, help='Policy which permissions will be checked') @click.option('--command', '-cmd', type=str, required=True, help='* Command call string to be checked') @ResponseDecorator(click.echo, 'Can not describe audit') def policy_simulator(user, group, policy, command): """ Policy simulator Usage: 1. Get action status by user and command modular policy_simulator --user $user_name --command "admin aws add_image" 2. Get action status by group and command modular policy_simulator --group $group_name --command "admin aws add_image" 3. Get action status by policy and command modular policy_simulator --policy $policy_name --command "admin aws add_image" """ params_quantity = sum([bool(param) for param in [user, group, policy]]) if params_quantity > 1: raise ModularApiBadRequestException( 'Only one of the following parameters: user, group or policy is ' 'allowed for permissions checking' ) return user_handler_instance().policy_simulator_handler( user_name=user, user_group=group, policy_name=policy, requested_command=command) modular.add_command(install) modular.add_command(user) modular.add_command(policy) modular.add_command(group)