PK!EEtronald/__init__.py__version__ = "0.2" PK! ZZtronald/__main__.pytry: from tronald import cli except ImportError: from . import cli cli.run_cli() PK!7=%tronald/cli.pyimport click import shelve import inquirer import configparser from time import sleep from .config import MetaData, TronaldConfig, DOTFILE_NAME from .controllers import RemoteController, LocalController from .initial import ( KEY_PATH_SETUP, DEFAULT_DATABASE_SETUP, CONTAINER_PREFIX, CONTAINER_SUFFIX, perform_initial_setup, ) @click.group() def cli(): pass @cli.command(name="settings") @click.option("--prefix", "prefix") @click.option("--suffix", "suffix") @click.option("--key", "key_path") @click.option("--db", "db_name") def configure(prefix, suffix, key_path, db_name): config = TronaldConfig() if not prefix and not suffix and not key_path and not db_name: click.echo("Current settings:\n") for key, value in config.configuration.items(): click.echo("{}: {}".format(key, value)) return if key_path: config.set_value("keypath", key_path) if db_name: config.set_value("defaultdatabase", db_name) if prefix: config.set_value("prefix", prefix) if suffix: config.set_value("suffix", suffix) @cli.command() @click.option("--container", "container") @click.option("--ssh-user", "ssh_user") @click.option("--postgres-user", "postgres_user") @click.argument("host") @click.argument("target") def dump(host, container, ssh_user, postgres_user, target): meta_params = { "host": host, "ssh_user": ssh_user, "postgres_user": postgres_user, "container": container, "target": target, } meta_data = MetaData(**meta_params) controller = RemoteController(meta_data) controller.dump_and_transfer() @cli.command(name="import") @click.argument("dump_path") @click.argument("container_identifier") @click.option("--postgres-user", "postgres_user") def import_dump(dump_path, container_identifier, postgres_user): configuration_parameters = { "dump_path": dump_path, "container_identifier": container_identifier, "postgres_user": postgres_user or "django", } controller = LocalController(**configuration_parameters) controller.import_dump() def run_cli(): perform_initial_setup() cli() if __name__ == "__main__": perform_initial_setup() cli() PK!wtronald/commands.pyimport os def build_postgres_dump(config): return "docker exec -t {} pg_dump {} -c -U {} > {}".format( config.container, config.db_name, config.postgres_user, config.dump_name ) def remove_remote_dump(config): return "rm {}".format(config.dump_name) def import_dump_to_local_container(dump_path, container_identifier, postgres_user): return os.system( "cat {} | docker exec -i {} psql -U {}".format( dump_path, container_identifier, postgres_user ) ) PK!㯜 tronald/config.pyimport os import configparser DOTFILE_NAME = "{}/tronald.ini".format(os.path.expanduser("~")) class TronaldConfig: def __init__(self): config = configparser.ConfigParser() config.read(DOTFILE_NAME) if not "tronald" in config.sections(): config["tronald"] = { "KeyPath": "", "DefaultDatabase": "", "Prefix": "", "Suffix": "", } with open(DOTFILE_NAME, "w") as configfile: config.write(configfile) config.read(DOTFILE_NAME) self.configuration = config["tronald"] def get_value(self, key): config = configparser.ConfigParser() config.read(DOTFILE_NAME) if not key in config["tronald"]: return None return config["tronald"][key] def set_value(self, key, value): config = configparser.ConfigParser() config.read(DOTFILE_NAME) config["tronald"][key] = value with open(DOTFILE_NAME, "w") as configfile: config.write(configfile) return config class MetaData: def __init__( self, host=None, container=None, ssh_user=None, postgres_user=None, dump_name=None, target=None, ): config = TronaldConfig() self.host = host self.container = container self.postgres_user = postgres_user self.ssh_user = ssh_user self.target = target self.db_name = config.get_value("defaultdatabase") self.prefix = config.get_value("prefix") self.suffix = config.get_value("suffix") self.key_path = config.get_value("keypath") @property def postgres_user(self): return self._postgres_user or "postgres" @postgres_user.setter def postgres_user(self, user): self._postgres_user = user @property def ssh_user(self): return self._ssh_user or "root" @ssh_user.setter def ssh_user(self, user): self._ssh_user = user @property def container(self): if self._container: return self._container if not self.host: raise Exception( "No container name derivation possible because of missing host." ) if self.host: return "{}{}{}".format(self.prefix, self.host, self.suffix) @container.setter def container(self, container_name): self._container = container_name @property def database(self): return self.db_name or "app" @property def rsa_key(self): return self.key_path or "~/.ssh/id_rsa" @property def target(self): return self._target or "recent-dump.db" @target.setter def target(self, target_path): self._target = target_path @property def dump_name(self): return "dump-{}.db".format(self._container) PK!劔tronald/controllers.pyimport time import paramiko from .commands import ( build_postgres_dump, remove_remote_dump, import_dump_to_local_container, ) def get_channel(client): return client.get_transport().open_session() class RemoteController: def __init__(self, config): self.client = paramiko.SSHClient() self.config = config self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) def dump_and_transfer(self): self.client.connect( self.config.host, username=self.config.ssh_user, key_filename=self.config.key_path, ) channel = get_channel(self.client) channel.exec_command(build_postgres_dump(self.config)) while not channel.exit_status_ready(): time.sleep(0.5) sftp = self.client.open_sftp() sftp.get(self.config.dump_name, self.config.target) channel = get_channel(self.client) channel.exec_command(remove_remote_dump(self.config)) sftp.close() self.client.close() class LocalController: def __init__(self, dump_path, container_identifier, postgres_user): self.postgres_user = postgres_user self.dump_path = dump_path self.container = container_identifier def import_dump(self): configuration_parameters = { "dump_path": self.dump_path, "container_identifier": self.container, "postgres_user": self.postgres_user, } import_dump_to_local_container(**configuration_parameters) PK!a^^tronald/initial.pyimport os import shelve import inquirer import configparser from .config import DOTFILE_NAME, TronaldConfig KEY_PATH_SETUP = [ inquirer.Text( "key_path", "Please enter the absolute path to your preferred RSA private key", default="~/.ssh/id_rsa", ) ] DEFAULT_DATABASE_SETUP = [ inquirer.Text( "db_name", "Please enter the default database name to pull dumps from.", default="app", ) ] CONTAINER_PREFIX = [ inquirer.Text( "prefix", "If there are common container prefixes, please provide them for container name derivation.", ) ] CONTAINER_SUFFIX = [ inquirer.Text( "suffix", "If there are common container suffixes, please provide them for container name derivation.", ) ] def perform_initial_setup(): config = TronaldConfig() if not config.get_value("keypath"): answer = inquirer.prompt(KEY_PATH_SETUP) key_path = answer["key_path"] if key_path.startswith("~"): key_location = os.path.expanduser(key_path) else: key_location = key_path config.set_value("keypath", key_location) if not config.get_value("defaultdatabase"): answer = inquirer.prompt(DEFAULT_DATABASE_SETUP) config.set_value("defaultdatabase", answer["db_name"]) if not config.get_value("prefix"): answer = inquirer.prompt(CONTAINER_PREFIX) config.set_value("prefix", answer["prefix"]) if not config.get_value("suffix"): answer = inquirer.prompt(CONTAINER_SUFFIX) config.set_value("suffix", answer["suffix"]) PK!H+],0&tronald-0.2.dist-info/entry_points.txtN+I/N.,()*)KIz񹉙yV9\\PK!HڽTUtronald-0.2.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!Htronald-0.2.dist-info/METADATAK0yThCm&"E|Yw4?j"MCErw}<:4Ӕ][A{J8%F&ȷ|^dWc9G] 8(9]Sy^KcZV ">z!l Y&QJp(5P7tC.C6S=1{sZIZz6MYW,9+&|'iF0?QoK;]FEvu"bkv( ߑlOPK!HWĹ.tronald-0.2.dist-info/RECORDuKwkPy? ep[BDR61lME>uzgM)MwJkƉ{K*:L\^ùGeݫGm9YP0}Km(HGÂ0aqœwfcd䋊s%ꋳ%e8G(~sfk9FRWλ. q("<;4v"{,?gi!