def PluginsCmd(config: Config, source_id: str): builder = CommandBuilder('plugins', config.credentials) @builder.from_cls() class _PluginsCmd(CommandWithList): '''Plugins - manage source plugins''' def __init__(self, config: Config, source_id: str): self.config = config super().__init__() self.source_id = source_id self.client = PluginClient( api_host=self.config.credentials.api_host, auth_token=self.config.credentials.auth_token, target_org=config.org.id) self.defaults = { plugin.os: plugin.id for plugin in self.client.list(source_id=source_id, default_only=True) } def get_things(self): plugins = [ p for p in self.client.list(source_id=self.source_id) if p.enabled ] return sorted(plugins, key=lambda x: x.last_updated_timestamp, reverse=True) def things_header(self): return [("Version", 20), ("OS", 20), ("Default", 7)] def format_thing(self, plugin: Plugin) -> str: version = plugin.display_version if plugin.display_version else plugin.version is_default = "●" if self.defaults.get( plugin.os) == plugin.id else "" return f"{version.rjust(20)} {plugin.os.value.rjust(20)} {colorama.Fore.RED}{is_default.center(7)}{colorama.Style.RESET_ALL}" @builder.empty_cmd() def _do_list(self): '''list available plugins''' return self._list() @builder.cmd(permissions=['admin:source:plugins:create']) def do_add(self, arg): '''add new source plugin''' PluginBuilder(self.config, self.source_id).from_user().build() @builder.cmd(permissions=['admin:source:plugins:update']) def do_default(self, idx): '''make plugin [idx] default''' selected: Plugin = self.get_thing_by_index(self.arg_as_idx(idx)) self.client.set_default(selected.id) print("Default updated") return builder.build()(config, source_id)
def ScheduleCmd(config: Config, source: Source): builder = CommandBuilder('schedule', config.credentials) @builder.from_cls() class _SchedulesCmd(Command): '''Schedules - view and manage source execution schedules''' def __init__(self, config: Config, source: Source): self.config = config super().__init__() self.source = source self.client = SchedulerServiceClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) def emptyline(self): return self.do_help('') @builder.cmd( permissions=['admin:source:schedule:get', 'source:schedule:get']) def do_info(self, _arg): '''retrieve schedule details''' try: sched = self.client.get(self.source.id) print(sched.schedule_config) except: print("No schedule set") @builder.cmd(permissions=[ 'admin:source:schedule:delete', 'source:schedule:delete' ]) def do_del(self, _arg): '''delete source schedule - disables automatic data collection''' if Builder.get_yes_no("Really delete schedule?"): self.client.delete(self.source.id) print(f"{source.display_name} schedule removed") @builder.cmd(permissions=[ 'admin:source:schedule:create', 'source:schedule:create', 'admin:source:schedule:update', 'source:schedule:update' ]) def do_set(self, _arg): '''Set source schedule''' update = False try: self.client.get(self.source.id) update = True except: pass SourceScheduleBuilder( self.config, self.source.id).from_user().build(update=update) return builder.build()(config, source)
def SchemasCmd(config: Config, source: Source): builder = CommandBuilder('schemas', config.credentials) @builder.from_cls() class _SchemasCmd(CommandWithList): '''Schemas - view and manage source configuration schemas''' def __init__(self, config: Config, source: Source): self.config = config super().__init__() self.source = source self.schema_client = SourceSchemaClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) def get_things(self): return self.schema_client.get(self.source.id) def things_header(self): return [("Schema", 20)] def format_thing(self, schemas: SourceSchemas) -> str: return schemas.info.description.rjust(20) @builder.empty_cmd() def _do_list(self): '''list source schemas''' return self._list() @builder.cmd(permissions=['admin:schema:read', 'schema:read']) def do_info(self, idx): '''retrieve schema details''' selected: SourceSchemas = self.get_thing_by_index(self.arg_as_idx(idx)) pprint(selected.as_dict()) @builder.cmd(permissions=['admin:source:update']) def do_del(self, idx): '''delete source schema''' selected: SourceSchemas = self.get_thing_by_index(self.arg_as_idx(idx)) all_schemas = self.schema_client.get(self.source.id) updated = [SourceSchemaByName(info=schema.info, auth=schema.auth['title'], connect=schema.connect['title'], settings=schema.settings['title']) for schema in all_schemas if schema.info.description != selected.info.description] self.schema_client.update( self.source.id, SourceSchemaByNameArray(types=updated)) @builder.cmd(permissions=['admin:source:update']) def do_add(self, arg): '''add source schemas''' SourceSchemaBuilder(config=self.config, source_id=self.source.id).from_user().build() return builder.build()(config, source)
def RolesCmd(config: Config, user: User): builder = CommandBuilder('roles', credentials=config.credentials) @builder.from_cls() class _RolesCmd(CommandWithList): '''Roles - manage and interact with roles''' def __init__(self, config: Config, user: User): super().__init__() self.config = config self.user = user self.client = TenantClient( config.credentials.api_host, config.credentials.auth_token, target_org=config.org.id) def get_things(self): return sorted(self.client.roles(), key=lambda x: x.name) def things_header(self): return [("Name", 20), ("Description", 40), ("Active", 6)] def format_thing(self, role: Role) -> str: enabled = "●" if role.name in self.user.roles else '' return f"{role.name.rjust(20)} {role.description.rjust(40)} {colorama.Fore.RED}{enabled.center(6)}{colorama.Style.RESET_ALL}" @builder.empty_cmd() def _do_list(self): '''list org roles''' return self._list() @builder.cmd(permissions=['admin:roles:read', 'tenant:roles:read', 'roles:read']) def do_info(self, idx): '''show role details''' selected: Role = self.get_thing_by_index(self.arg_as_idx(idx)) pprint(selected.as_dict()) @builder.cmd(permissions=['org:user:write']) def do_add(self, idx): '''add role to user''' selected: Role = self.get_thing_by_index(self.arg_as_idx(idx)) self.client.user_add_role(self.user.email, selected.name) print(f"Added {selected.name}") self.user = self.client.get_user(self.user.email) @builder.cmd(permissions=['org:user:write']) def do_del(self, idx): '''remove role from user''' selected: Role = self.get_thing_by_index(self.arg_as_idx(idx)) self.client.user_delete_role(self.user.email, selected.name) print(f"Removed {selected.name}") self.user = self.client.get_user(self.user.email) return builder.build()(config, user)
def UsersCmd(config: Config): builder = CommandBuilder('users', credentials=config.credentials) @builder.from_cls() class _UsersCmd(CommandWithList): '''Users - manage and interact with users''' def __init__(self, config: Config): super().__init__() self.config = config self.client = TenantClient(config.credentials.api_host, config.credentials.auth_token, target_org=config.org.id) def default(self, line: str) -> Union[bool, CmdResponse]: try: idx = int(line) return self._do_select(idx) except IndexError as e: print(str(e)) return False except ValueError: pass return super().default(line) @builder.empty_cmd() def _do_select(self, idx: int) -> CmdResponse: '''change scope into user [idx]''' selected = self.get_thing_by_index(idx) return NewScopeResponse(UserScope(self.config, selected)) def get_things(self): return sorted(self.client.list_users(), key=lambda x: x.name) def things_header(self): return [("Name", 20), ("Email", 30)] def format_thing(self, user: User) -> str: return f"{user.nickname.rjust(20)} {user.email.rjust(30)}" @builder.empty_cmd() def _do_list(self): '''list org users''' return self._list() @builder.cmd(permissions=['org:user:read']) def do_info(self, idx): '''show user details''' selected: User = self.get_thing_by_index(self.arg_as_idx(idx)) pprint(selected.as_dict()) @builder.cmd(permissions=['org:user:write']) def do_add(self, _arg): '''add user to org''' AddUserBuilder(self.config).from_user().build() @builder.cmd(permissions=['org:user:delete']) def do_del(self, idx): '''remove user from org''' selected: User = self.get_thing_by_index(self.arg_as_idx(idx)) if Builder.get_yes_no( f"Really remove user from {self.config.org.org_name}?", default_yes=False): self.client.delete_user(selected.email) return builder.build()(config)
def RunnersCmd(config: Config): builder = CommandBuilder('runners', config.credentials) @builder.from_cls() class _RunnersCmd(CommandWithList): '''Runners - manage runners''' def __init__(self, config: Config): self.config = config super().__init__() self.client = RunnerServiceClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) self.runners = None def get_things(self): epoch = datetime.datetime.utcfromtimestamp(0).replace( tzinfo=datetime.timezone.utc) return sorted(self.client.list(), key=lambda x: x.last_checkin_time or epoch, reverse=True) def things_header(self): return [("Name", 26), ("Last Checkin", 40)] def format_thing(self, runner: Runner) -> str: if runner.display_name: return f"{runner.display_name.rjust(26)} {str(runner.last_checkin_time).rjust(40)}" else: return f"{runner.hostname.rjust(26)} {str(runner.last_checkin_time).rjust(40)}" @builder.empty_cmd() def _do_list(self): '''list available runners''' return self._list() @builder.cmd(permissions=['admin:runner:get', 'runner:get']) def do_info(self, idx: int): '''retrieve runner details''' selected = self.get_thing_by_index(self.arg_as_idx(idx)) pprint(selected.as_dict()) @builder.cmd(permissions=['admin:runner:delete', 'runner:delete']) def do_del(self, idx: int): '''unregister runner''' selected = self.get_thing_by_index(self.arg_as_idx(idx)) if Builder.get_yes_no( f"Really delete runner {selected.display_name}?", default_yes=False): self.client.delete(selected.runner_id) @builder.cmd(permissions=['admin:runner:download', 'runner:download']) def do_download(self, _arg): '''download runner binary''' current_os = platform.system().lower() runner_os = Builder.get_one_of("Which platform", ["linux", "windows", "darwin"], default=current_os) default_target = "~/Downloads/runner.exe" if runner_os == "windows" else "~/Downloads/runner" target = None while target is None: target = Builder.get_input( f"Save binary to [{default_target}]", required=False) or os.path.expanduser(default_target) if os.path.exists(target): if not Builder.get_yes_no(f"{target} exists. Overwrite?", default_yes=False): target = None runner_binary_bytes = self.client.download(runner_os) os.makedirs(os.path.basename(target), exist_ok=True) with open(target, "wb") as f: f.write(runner_binary_bytes) p = Path(target) p.chmod(p.stat().st_mode | stat.S_IXUSR) print(f"Runner {runner_os} binary saved to: {target}") return builder.build()(config)
def OrgsCmd(credentials: ApiCredentials): builder = CommandBuilder('orgs', credentials=credentials) @builder.from_cls() class _OrgsCmd(CommandWithList): '''Orgs - manage and interact with organizations and their configurations''' def __init__(self, credentials: ApiCredentials): super().__init__() self.credentials = credentials self.client = TenantClient(credentials.api_host, credentials.auth_token) def default(self, line: str) -> Union[bool, CmdResponse]: try: idx = int(line) return self._do_select(idx) except IndexError as e: print(str(e)) return False except ValueError: pass return super().default(line) def things_header(self): return [("Org", 32), ("Id", 40)] def get_things(self): return sorted(self.client.org_list(), key=lambda x: x.org_name) def format_thing(self, org: Organization) -> str: return f"{org.org_name.rjust(32)} {org.id.rjust(40)}" @builder.empty_cmd() def _do_list(self): '''list available orgs''' return self._list() @builder.empty_cmd() def _do_select(self, idx: int) -> Union[bool, CmdResponse]: '''change scope into org [idx]''' selected = self.get_thing_by_index(idx) return NewScopeResponse( OrgScope(Config(credentials=self.credentials, org=selected))) @builder.cmd(permissions=['admin:orgs:write', 'tenant:orgs:write']) def do_add(self, _arg): '''add new org''' org = OrgBuilder(credentials=self.credentials).from_user().build() @builder.cmd(permissions=['admin:orgs:delete', 'tenant:orgs:delete']) def do_del(self, idx): '''delete org [idx]''' selected: Organization = self.get_thing_by_index( self.arg_as_idx(idx)) assert selected.id if Builder.get_yes_no(f"Really delete {selected.org_name}?", default_yes=False): self.client.org_delete(selected.id) print(f"Deleted {selected.org_name}") @builder.cmd(permissions=['admin:orgs:account', 'orgs:account']) def do_token(self, idx): '''get service account token for [idx]''' selected: Organization = self.get_thing_by_index( self.arg_as_idx(idx)) assert selected.id print(self.client.svc_token(selected.id)) @builder.cmd(permissions=['admin:orgs:read', 'orgs:my:read']) def do_info(self, idx: int): '''retrieve organization details''' selected = self.get_thing_by_index(self.arg_as_idx(idx)) pprint(selected.as_dict()) return builder.build()(credentials)
def SourcesCmd(config: Config) -> CommandWithList: builder = CommandBuilder('sources', config.credentials) @builder.from_cls() class _SourcesCmd(CommandWithList): '''Sources - work with available integration sources available in the source catalog.''' def __init__(self, config: Config): self.config = config super().__init__() self.client = SourceCatalogClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) self.scheduler_client = SchedulerServiceClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) self.oauth_client = SourceOAuthClient( api_host=self.config.credentials.api_host, auth_token=self.config.credentials.auth_token, target_org=self.config.org.id) def default(self, line: str) -> Union[bool, CmdResponse]: try: idx = int(line) return self._do_select(idx) except IndexError as e: print(str(e)) return False except ValueError: pass return super().default(line) def get_things(self): return self.client.list() def things_header(self): return [("Source", 40)] def format_thing(self, source: Source) -> str: return source.display_name.rjust(40) @builder.empty_cmd() def _do_list(self): '''list available sources''' return self._list() @builder.empty_cmd() def _do_select(self, idx: int) -> CmdResponse: '''change scope into source [idx]''' selected = self.get_thing_by_index(idx) return NewScopeResponse(SourceScope(self.config, selected)) @builder.cmd(permissions=['admin:source:config:write', 'source:config:write']) def do_config(self, idx): '''configure source [idx]''' selected: Source = self.get_thing_by_index(self.arg_as_idx(idx)) builder = source_config = SourceConfigBuilder(self.config, selected.id) source_config = builder.from_user().build() if source_config: assert source_config.id if builder.schema.auth['title'] == 'oauth2': url = self.oauth_client.initiate(source_config_id=source_config.id) print("Browse to the following URL to initiate the OAuth workflow:") print(url) try: self.scheduler_client.execute(source_config_id=source_config.id) except Exception as e: print(f"Unable to schedule immediate execution: {e}") @builder.cmd(permissions=['admin:source:create']) def do_add(self, _arg): '''add new source''' print("Source Configuration") source = SourceBuilder(config=self.config).from_user().build() if source: print("Source Schema Configuration") schema = SourceSchemaBuilder(config=self.config, source_id=source.id).from_user().build() if schema: print("Source Schedule") SourceScheduleBuilder(config=self.config, source_id=source.id).from_user().build() return builder.build()(config)
def SourceConfigsCmd(config: Config, source: Optional[Source] = None): builder = CommandBuilder('configs', config.credentials) @builder.from_cls() class SourceConfigsCmd(CommandWithList): '''Configs - manage source configs''' def __init__(self, config: Config, source: Optional[Source] = None): self.config = config super().__init__() self.source = source self.client = SourceConfigClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) self.scheduler_client = SchedulerServiceClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) catalog_client = SourceCatalogClient( api_host=config.credentials.api_host, auth_token=config.credentials.auth_token, target_org=config.org.id) self.sources_by_id = { self.source.id: self.source } if self.source else { source.id: source for source in catalog_client.list() } def get_things(self): def get_timestamp(x: SourceConfig) -> datetime: assert x.last_updated_timestamp return x.last_updated_timestamp return sorted(self.client.list( source_id=self.source.id if self.source else None), key=get_timestamp, reverse=True) def things_header(self): return [("ID", 36), ("Source", 40)] def format_thing(self, config: SourceConfig) -> str: source = self.sources_by_id.get(config.source_id) return f"{config.id.rjust(36)} {source.display_name.rjust(40) if source else 'UNKNOWN'.rjust(40)}" @builder.empty_cmd() def _do_list(self): '''list source configs''' return self._list() @builder.cmd( permissions=['admin:source:config:read', 'source:config:read']) def do_info(self, idx): '''retrieve details for config [idx]''' selected: SourceConfig = self.get_thing_by_index( self.arg_as_idx(idx)) pprint(selected.as_dict()) @builder.cmd( permissions=['admin:source:audit:read', 'source:audit:read']) def do_audit(self, idx_n): '''retrieve last N execution audit logs for config [idx]''' idx, _, n = idx_n.partition(' ') n = int(n) if n else 10 selected: SourceConfig = self.get_thing_by_index( self.arg_as_idx(idx)) client = SourceAuditClient( api_host=self.config.credentials.api_host, auth_token=self.config.credentials.auth_token, target_org=self.config.org.id) for audit in client.list("execution", source_config_id=selected.id, per_page=n): pprint(audit.as_dict(), width=240) @builder.cmd( permissions=['admin:source:config:write', 'source:config:write']) def do_oauth(self, idx): '''initiate OAuth2 workflow for config [idx]''' selected: SourceConfig = self.get_thing_by_index( self.arg_as_idx(idx)) assert selected.id if selected.auth and selected.auth.schema != 'oauth2': raise Exception("Not an OAuth2 config") client = SourceOAuthClient( api_host=self.config.credentials.api_host, auth_token=self.config.credentials.auth_token, target_org=self.config.org.id) url = client.initiate(source_config_id=selected.id) print( "Browse to the following URL to initiate the OAuth workflow:") print(url) @builder.cmd( permissions=['admin:source:config:delete', 'source:config:delete']) def do_del(self, idx): '''delete config [idx]''' selected: SourceConfig = self.get_thing_by_index( self.arg_as_idx(idx)) assert selected.id if Builder.get_yes_no(f"Really delete {selected.id}?", default_yes=False): self.client.delete(selected.id) print(f"Deleted {selected.id}") @builder.cmd(permissions=['admin:source:schedule:execute']) def do_exec(self, idx): '''execute config [idx]''' selected: SourceConfig = self.get_thing_by_index( self.arg_as_idx(idx)) assert selected.id print(self.scheduler_client.execute(source_config_id=selected.id)) return builder.build()(config, source)