async def switch_impl(ctx, name): project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) db_name = dbenv.db_service["$name"] # 1. If running, stop database was_running = engine.status(project)[db_name] if was_running: await stop_project(ctx, [db_name], show_status=False) # 2. Switch environment try: dbenv.switch(name) echo() echo("Environment switched to: " + style(name, bold=True)) echo() except FileNotFoundError: raise RiptideCliError("Environment does not exist. Create it with db-new or db-copy.", ctx) except Exception as ex: raise RiptideCliError("Error switching environments", ctx) from ex # 3. If was running: start database again if was_running: await start_project(ctx, [db_name])
async def importt_impl(ctx, file): project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) db_name = dbenv.db_service["$name"] env_name = dbenv.currently_selected_name() db_driver = db_driver_for_service.get(dbenv.db_service) if not file or file == "": raise RiptideCliError("Please specify a path.", ctx) if not os.path.exists(os.path.abspath(file)): raise RiptideCliError("The path does not exist.", ctx) # 1. If not running, start database was_running = engine.status(project)[db_name] if not was_running: await start_project(ctx, [db_name], show_status=False) # TODO: Some databases need a while. How to do this better? mysqladmin for example doesn't help :( await sleep(15) # 2. Import echo(f"Importing into database environment {env_name}... this may take a while...") try: db_driver.importt(engine, os.path.abspath(file)) echo() echo(f"Database environment {env_name} imported.") echo() return True except FileNotFoundError: raise RiptideCliError("Environment does not exist. Create it first with db:create", ctx) except Exception as ex: raise RiptideCliError("Error importing database environment", ctx) from ex
async def export(ctx, file): """ Export database dump from the current environment. The format of the dump depends on the database driver. """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) db_name = dbenv.db_service["$name"] env_name = dbenv.currently_selected_name() db_driver = db_driver_for_service.get(dbenv.db_service) # 1. If not running, start database was_running = engine.status(project)[db_name] if not was_running: await start_project(ctx, [db_name], show_status=False) # 2. Export echo(f"Exporting from {env_name}... this may take a while...") try: db_driver.export(engine, os.path.abspath(file)) echo() echo(f"Environment {env_name} exported.") echo() except FileNotFoundError: raise RiptideCliError("Environment does not exist. Create it first with db-create", ctx) except Exception as ex: raise RiptideCliError("Error exporting environment", ctx) from ex
async def copy(ctx, stay, name_to_copy, name_new): """ Copy an existing database environment """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) echo("Copying... this may take a while...") try: dbenv.new(name_new, copy_from=name_to_copy) echo() echo("New environment created: " + style(name_new, bold=True)) echo() except FileExistsError: raise RiptideCliError("Envrionment with this name already exists.", ctx) except FileNotFoundError: raise RiptideCliError("Envrionment to copy from not found.", ctx) except NameError: raise RiptideCliError("Invalid name for new environment, do not use special characters", ctx) except Exception as ex: raise RiptideCliError("Error creating environment", ctx) from ex if not stay: await switch_impl(ctx, name_new)
def lst(ctx, machine_readable, current): """ Lists database environments """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) cur = dbenv.currently_selected_name() if not machine_readable and not current: echo("Database environments: ") for env in dbenv.list(): if env == cur: echo(TAB + "- " + style(env, bold=True) + " [Current]") else: echo(TAB + "- " + env) echo() echo("Use db-switch to switch environments.") elif not current: echo(json.dumps({ "envs": dbenv.list(), "current": cur })) else: echo(cur)
async def db_copy_impl(subject: ReplaySubject, project_name: str, source: str, target:str, switch: bool): project = try_loading_project(project_name, subject, 1, 4) if not project: return engine = registry().engine dbenv = DbEnvironments(project, engine) try: subject.on_next(ResultStep( steps=4, current_step=1, text=f"Copying database environment {source} to {target}..." )) dbenv.new(target, copy_from=source) except FileNotFoundError: subject.on_next(ResultStep( steps=4, current_step=1, text=f"Environment {source} not found.", is_end=True, is_error=True )) except FileExistsError: subject.on_next(ResultStep( steps=4, current_step=1, text=f"Environment with this name already exists.", is_end=True, is_error=True )) except NameError: subject.on_next(ResultStep( steps=4, current_step=1, text=f"Invalid name for new environment, do not use special characters.", is_end=True, is_error=True )) except Exception as ex: subject.on_next(ResultStep( steps=4, current_step=1, text=f"Error creating environment: {ex}", is_end=True, is_error=True )) else: if switch: await db_switch_impl(subject, project, target, 1, 1) else: subject.on_next(ResultStep( steps=4, current_step=4, text=f"Finished creating environment: {target}.", is_end=True ))
async def db_drop_impl(subject: ReplaySubject, project_name: str, name: str): project = try_loading_project(project_name, subject, 1, 4) if not project: return engine = registry().engine dbenv = DbEnvironments(project, engine) try: subject.on_next(ResultStep( steps=1, current_step=1, text=f"Deleting database environment {name}..." )) dbenv.drop(name) except FileNotFoundError: subject.on_next(ResultStep( steps=1, current_step=1, text=f"Environment with this name does not exist.", is_end=True, is_error=True )) except OSError: subject.on_next(ResultStep( steps=1, current_step=1, text=f"Can not delete the environment that is currently active.", is_end=True, is_error=True )) except Exception as ex: subject.on_next(ResultStep( steps=1, current_step=1, text=f"Error deleting environment: {ex}", is_end=True, is_error=True )) else: subject.on_next(ResultStep( steps=1, current_step=1, text=f"Finished deleting environment: {name}.", is_end=True ))
def drop(ctx, name): """ Delete a database environment """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) echo("Deleting... this may take a while...") try: dbenv.drop(name) echo() echo("Environment deleted: " + style(name, bold=True)) echo() except FileNotFoundError: raise RiptideCliError("Envrionment with this name does not exist.", ctx) except OSError: raise RiptideCliError("Can not delete the environment that is currently active.", ctx) except Exception as ex: raise RiptideCliError("Error deleting environment", ctx) from ex
async def new(ctx, stay, name): """ Create a new (blank) database environment """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) try: dbenv.new(name, copy_from=None) echo() echo("New environment created: " + style(name, bold=True)) echo() except FileExistsError: raise RiptideCliError("Envrionment with this name already exists.", ctx) except NameError: raise RiptideCliError("Invalid name for new environment, do not use special characters", ctx) except Exception as ex: raise RiptideCliError("Error creating environment", ctx) from ex if not stay: await switch_impl(ctx, name)
def status(ctx): """ Print information and status of the database. """ load_riptide_core(ctx) cmd_constraint_has_db(ctx) project = ctx.system_config["project"] engine = ctx.engine dbenv = DbEnvironments(project, engine) db_driver = db_driver_for_service.get(dbenv.db_service) running = engine.service_status(project, dbenv.db_service["$name"]) if running: echo(f"{'Status':<20}: " + style("Running", bold=True, fg="green")) else: echo(f"{'Status':<20}: " + style("Not running", bold=True, fg="red")) for key,label in db_driver.collect_info().items(): echo(f"{key:<20}: {label}") current = dbenv.currently_selected_name() echo("Active environment : " + style(current, bold=True))
async def setup_assistant(ctx, force, skip): project = ctx.system_config["project"] engine = ctx.engine if ctx.project_is_set_up and not force: raise RiptideCliError( "The project is already set up. If you still want to run this command, pass --force.", ctx) if skip: echo("Project was marked as set up.") finish(ctx) return echo(style("Thank you for using Riptide!", fg='cyan', bold=True)) echo( f"This command will guide you through the initial setup for {project['name']}." ) echo( style("Please follow it very carefully, it won't take long!", bold=True)) echo(style("> Press any key to continue...", fg='magenta')) getchar() echo() echo(header("> BEGIN SETUP")) if "notices" in project["app"] and "usage" in project["app"]["notices"]: echo() echo( style(f"Usage notes for running {project['app']['name']}", bold=True) + " with Riptide:") echo(TAB + TAB.join(project["app"]["notices"]["usage"].splitlines(True))) echo() # Q1 echo(style("> Do you wish to run this interactive setup? [Y/n] ", fg='magenta'), nl=False) if getchar(True).lower() == 'n': echo() echo() echo(header("> END SETUP")) echo("Okay! To re-run this setup, pass the --force option.") finish(ctx) return echo() echo() echo(header("> INTERACTIVE SETUP")) # Q2: New or existing? echo(style("> Are you working on a ", fg='magenta') + style("n", bold=True, fg="cyan") + style("ew project that needs to be installed or do you want to ", fg='magenta') + style("I", bold=True, fg="cyan") + style("mport existing data? [n/I] ", fg='magenta'), nl=False) if getchar(True).lower() == 'n': # New project if "notices" in project["app"] and "installation" in project["app"][ "notices"]: echo() echo() echo(header("> NEW PROJECT")) echo( "Okay! Riptide can't guide you through the installation automatically." ) echo( f"Please read these notes on how to run a first-time-installation for {project['app']['name']}." ) echo() echo(style("Installation instructions:", bold=True)) echo(TAB + TAB.join(project["app"]["notices"] ["installation"].splitlines(True))) finish(ctx) return # Existing project echo() echo() echo(header("> EXISTING PROJECT")) db_can_be_imported = DbEnvironments.has_db(project) files_can_be_imported = 'import' in project['app'] if not db_can_be_imported and not files_can_be_imported: # Nothing to import echo( f"The app {project['app']['name']} does not specify a database or files to import. You are already done!" ) finish(ctx) return # Import db if db_can_be_imported: dbenv = DbEnvironments(project, engine) db_driver = db_driver_for_service.get(dbenv.db_service) echo(TAB + header("> DATABASE IMPORT")) echo(style( f"> Do you want to import a database (format {dbenv.db_service['driver']['name']})? [Y/n] ", fg='magenta'), nl=False) if getchar(True).lower() != 'n': # Import db echo() exit_cmd = False while not exit_cmd: echo(db_driver.ask_for_import_file() + " ", nl=False) path = stdin.readline().rstrip('\r\n') try: echo(CMD_SEP) await importt_impl(ctx, path) exit_cmd = True echo(CMD_SEP) except RiptideCliError as err: echo("Error: " + style(str(err), fg='red')) echo(CMD_SEP) echo(style("> Do you want to try again? [y/N] ", fg='magenta'), nl=False) if getchar(True).lower() != 'y': exit_cmd = True echo() else: echo() echo( "Skipping database import. If you change your mind, run db:import." ) if files_can_be_imported: echo(TAB + header("> FILE IMPORT")) for key, entry in project['app']['import'].items(): echo(TAB + TAB + header(f"> {key} IMPORT")) echo(style( f"> Do you wish to import {entry['name']} to <project>/{entry['target']}? [Y/n] ", fg='magenta'), nl=False) if getchar(True).lower() != 'n': # Import files echo() exit_cmd = False while not exit_cmd: echo("Enter path of files or directory to copy: ", nl=False) path = stdin.readline().rstrip('\r\n') try: echo(CMD_SEP) files_impl(ctx, key, path) exit_cmd = True echo(CMD_SEP) except RiptideCliError as err: echo("Error: " + style(str(err), fg='red')) echo(CMD_SEP) echo(style("> Do you want to try again? [y/N] ", fg='magenta'), nl=False) if getchar(True).lower() != 'y': exit_cmd = True echo() else: echo() echo() echo(header("> IMPORT DONE!", bold=True)) echo("All files were imported.") finish(ctx)
def collect_volumes(self): return DbEnvironments.get_volume_configuration_for_driver( DATA_PATH, self.service)
def resolve_db_current(parent, info): if not DbEnvironments.has_db(_get_project_doc(parent)): return None dbenv = DbEnvironments(_get_project_doc(parent), registry().engine) return dbenv.currently_selected_name()
def resolve_db_list(parent, info): if not DbEnvironments.has_db(_get_project_doc(parent)): return None dbenv = DbEnvironments(_get_project_doc(parent), registry().engine) return dbenv.list()
def resolve_db_available(parent, info): return DbEnvironments.has_db(_get_project_doc(parent))
async def db_switch_impl( subject: ReplaySubject, project: Project, name: str, total_steps_from_ctx=0, current_step_in_ctx=0 ): """Switches db env. Has 3 total steps and can be used in other db operations.""" engine = registry().engine dbenv = DbEnvironments(project, engine) db_name = dbenv.db_service["$name"] subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 1, text="Switching database..." )) # 1. If running, stop database was_running = engine.service_status(project, db_name, registry().system_config) if was_running: subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 1, text="Stopping database service..." )) async for _ in registry().engine.stop_project(project, [db_name]): pass # 2. Switch environment try: subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 2, text=f"Switching environment to {name}..." )) dbenv.switch(name) except FileNotFoundError: subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 2, text=f"Environment {name} does not exist.", is_end=True, is_error=True )) except Exception as ex: subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 2, text=f"Error switching environment: {str(ex)}", is_end=True, is_error=True )) else: # 3. If was running: start database again if was_running: subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 3, text=f"Starting database...", )) async for _ in registry().engine.start_project(project, [db_name]): pass subject.on_next(ResultStep( steps=total_steps_from_ctx + 3, current_step=current_step_in_ctx + 3, text=f"Finished switching database environment to {name}.", is_end=True ))
def cmd_constraint_has_db(ctx): cmd_constraint_project_loaded(ctx) if not DbEnvironments.has_db(ctx.system_config["project"]): raise RiptideCliError("The project doesn't have a service with the role 'db'. " "This is required to use this command.", ctx)