def run_configuration(config_filename, config, args): # pragma: no cover ''' Given a config filename and the corresponding parsed config dict, execute its defined pruning, backups, consistency checks, and/or other actions. ''' (location, storage, retention, consistency, hooks) = (config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks')) try: local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) if args.create: hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup') _run_commands( args=args, consistency=consistency, local_path=local_path, location=location, remote_path=remote_path, retention=retention, storage=storage, ) if args.create: hook.execute_hook(hooks.get('after_backup'), config_filename, 'post-backup') except (OSError, CalledProcessError): hook.execute_hook(hooks.get('on_error'), config_filename, 'on-error') raise
def run_configuration(config_filename, args): # pragma: no cover ''' Parse a single configuration file, and execute its defined pruning, backups, and/or consistency checks. ''' logger.info('{}: Parsing configuration file'.format(config_filename)) config = validate.parse_configuration(config_filename, validate.schema_filename()) (location, storage, retention, consistency, hooks) = ( config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks') ) try: local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) if args.create: hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup') _run_commands( args=args, consistency=consistency, local_path=local_path, location=location, remote_path=remote_path, retention=retention, storage=storage, ) if args.create: hook.execute_hook(hooks.get('after_backup'), config_filename, 'post-backup') except (OSError, CalledProcessError): hook.execute_hook(hooks.get('on_error'), config_filename, 'on-error') raise
def test_initialize_with_passphrase_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'encryption_passphrase': 'pass'}) assert os.environ.get('BORG_PASSPHRASE') == 'pass' finally: os.environ = orig_environ
def test_initialize_passes_through_existing_borg_environment_variable(): orig_environ = os.environ try: os.environ = {'BORG_PASSPHRASE': 'pass'} module.initialize({'ssh_command': 'ssh -C'}) assert os.environ.get('BORG_PASSPHRASE') == 'pass' finally: os.environ = orig_environ
def test_initialize_with_passcommand_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'encryption_passcommand': 'command'}) assert os.environ.get('BORG_PASSCOMMAND') == 'command' finally: os.environ = orig_environ
def test_initialize_with_ssh_command_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'ssh_command': 'ssh -C'}) assert os.environ.get('BORG_RSH') == 'ssh -C' finally: os.environ = orig_environ
def test_initialize_with_ssh_command_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'ssh_command': 'ssh -C'}) assert os.environ.get('BORG_RSH') == 'ssh -C' finally: os.environ = orig_environ
def test_initialize_with_passphrase_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'encryption_passphrase': 'pass'}) assert os.environ.get('BORG_PASSPHRASE') == 'pass' finally: os.environ = orig_environ
def test_initialize_with_passcommand_should_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({'encryption_passcommand': 'command'}) assert os.environ.get('BORG_PASSCOMMAND') == 'command' finally: os.environ = orig_environ
def run_configuration(config_filename, config, arguments): # pragma: no cover ''' Given a config filename, the corresponding parsed config dict, and command-line arguments as a dict from subparser name to a namespace of parsed arguments, execute its defined pruning, backups, consistency checks, and/or other actions. Yield JSON output strings from executing any actions that produce JSON. ''' (location, storage, retention, consistency, hooks) = (config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks')) global_arguments = arguments['global'] try: local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) if 'create' in arguments: hook.execute_hook( hooks.get('before_backup'), hooks.get('umask'), config_filename, 'pre-backup', global_arguments.dry_run, ) for repository_path in location['repositories']: yield from run_actions( arguments=arguments, location=location, storage=storage, retention=retention, consistency=consistency, local_path=local_path, remote_path=remote_path, repository_path=repository_path, ) if 'create' in arguments: hook.execute_hook( hooks.get('after_backup'), hooks.get('umask'), config_filename, 'post-backup', global_arguments.dry_run, ) except (OSError, CalledProcessError): hook.execute_hook( hooks.get('on_error'), hooks.get('umask'), config_filename, 'on-error', global_arguments.dry_run, ) raise
def test_initialize_with_relocated_repo_access_should_override_default(): orig_environ = os.environ try: os.environ = {} module.initialize({'relocated_repo_access_is_ok': True}) assert os.environ.get('BORG_RELOCATED_REPO_ACCESS_IS_OK') == 'yes' finally: os.environ = orig_environ
def test_initialize_prefers_configuration_option_over_borg_environment_variable( ): orig_environ = os.environ try: os.environ = {'BORG_SSH': 'mosh'} module.initialize({'ssh_command': 'ssh -C'}) assert os.environ.get('BORG_RSH') == 'ssh -C' finally: os.environ = orig_environ
def test_initialize_is_not_affected_by_existing_environment(): orig_environ = os.environ try: os.environ = {'BORG_PASSPHRASE': 'pass', 'BORG_SSH': 'mosh'} module.initialize({'ssh_command': 'ssh -C'}) assert 'BORG_PASSPHRASE' not in os.environ assert os.environ.get('BORG_RSH') == 'ssh -C' finally: os.environ = orig_environ
def test_initialize_without_configuration_should_not_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({}) assert sum(1 for key in os.environ.keys() if key.startswith('BORG_')) == 0 finally: os.environ = orig_environ
def test_initialize_without_configuration_should_not_set_environment(): orig_environ = os.environ try: os.environ = {} module.initialize({}) assert os.environ.get('BORG_PASSCOMMAND') is None assert os.environ.get('BORG_PASSPHRASE') is None assert os.environ.get('BORG_RSH') is None finally: os.environ = orig_environ
def test_initialize_without_configuration_should_only_set_default_environment( ): orig_environ = os.environ try: os.environ = {} module.initialize({}) assert { key: value for key, value in os.environ.items() if key.startswith('BORG_') } == { 'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'no', 'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'no', } finally: os.environ = orig_environ
def run_configuration(config_filename, config, args): # pragma: no cover ''' Given a config filename and the corresponding parsed config dict, execute its defined pruning, backups, consistency checks, and/or other actions. Yield JSON output strings from executing any actions that produce JSON. ''' (location, storage, retention, consistency, hooks) = ( config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks') ) try: local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) if args.create: hook.execute_hook( hooks.get('before_backup'), config_filename, 'pre-backup', args.dry_run ) for repository_path in location['repositories']: yield from run_actions( args=args, location=location, storage=storage, retention=retention, consistency=consistency, local_path=local_path, remote_path=remote_path, repository_path=repository_path, ) if args.create: hook.execute_hook( hooks.get('after_backup'), config_filename, 'post-backup', args.dry_run ) except (OSError, CalledProcessError): hook.execute_hook(hooks.get('on_error'), config_filename, 'on-error', args.dry_run) raise
def run_configuration(config_filename, config, arguments): ''' Given a config filename, the corresponding parsed config dict, and command-line arguments as a dict from subparser name to a namespace of parsed arguments, execute its defined pruning, backups, consistency checks, and/or other actions. Yield a combination of: * JSON output strings from successfully executing any actions that produce JSON * logging.LogRecord instances containing errors from any actions or backup hooks that fail ''' (location, storage, retention, consistency, hooks) = (config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks')) global_arguments = arguments['global'] local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) encountered_error = None error_repository = '' if 'create' in arguments: try: healthchecks.ping_healthchecks(hooks.get('healthchecks'), config_filename, global_arguments.dry_run, 'start') command.execute_hook( hooks.get('before_backup'), hooks.get('umask'), config_filename, 'pre-backup', global_arguments.dry_run, ) postgresql.dump_databases(hooks.get('postgresql_databases'), config_filename, global_arguments.dry_run) except (OSError, CalledProcessError) as error: encountered_error = error yield from make_error_log_records( '{}: Error running pre-backup hook'.format(config_filename), error) if not encountered_error: for repository_path in location['repositories']: try: yield from run_actions( arguments=arguments, location=location, storage=storage, retention=retention, consistency=consistency, local_path=local_path, remote_path=remote_path, repository_path=repository_path, ) except (OSError, CalledProcessError) as error: encountered_error = error error_repository = repository_path yield from make_error_log_records( '{}: Error running actions for repository'.format( repository_path), error) if 'create' in arguments and not encountered_error: try: postgresql.remove_database_dumps(hooks.get('postgresql_databases'), config_filename, global_arguments.dry_run) command.execute_hook( hooks.get('after_backup'), hooks.get('umask'), config_filename, 'post-backup', global_arguments.dry_run, ) healthchecks.ping_healthchecks(hooks.get('healthchecks'), config_filename, global_arguments.dry_run) except (OSError, CalledProcessError) as error: encountered_error = error yield from make_error_log_records( '{}: Error running post-backup hook'.format(config_filename), error) if encountered_error: try: command.execute_hook( hooks.get('on_error'), hooks.get('umask'), config_filename, 'on-error', global_arguments.dry_run, repository=error_repository, error=encountered_error, output=getattr(encountered_error, 'output', ''), ) healthchecks.ping_healthchecks(hooks.get('healthchecks'), config_filename, global_arguments.dry_run, 'fail') except (OSError, CalledProcessError) as error: yield from make_error_log_records( '{}: Error running on-error hook'.format(config_filename), error)
def run_configuration(config_filename, config, arguments): ''' Given a config filename, the corresponding parsed config dict, and command-line arguments as a dict from subparser name to a namespace of parsed arguments, execute its defined pruning, backups, consistency checks, and/or other actions. Yield a combination of: * JSON output strings from successfully executing any actions that produce JSON * logging.LogRecord instances containing errors from any actions or backup hooks that fail ''' (location, storage, retention, consistency, hooks) = (config.get(section_name, {}) for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks')) global_arguments = arguments['global'] local_path = location.get('local_path', 'borg') remote_path = location.get('remote_path') borg_environment.initialize(storage) encountered_error = None error_repository = '' prune_create_or_check = {'prune', 'create', 'check'}.intersection(arguments) monitoring_log_level = verbosity_to_log_level( global_arguments.monitoring_verbosity) try: if prune_create_or_check: dispatch.call_hooks( 'ping_monitor', hooks, config_filename, monitor.MONITOR_HOOK_NAMES, monitor.State.START, monitoring_log_level, global_arguments.dry_run, ) if 'prune' in arguments: command.execute_hook( hooks.get('before_prune'), hooks.get('umask'), config_filename, 'pre-prune', global_arguments.dry_run, ) if 'create' in arguments: command.execute_hook( hooks.get('before_backup'), hooks.get('umask'), config_filename, 'pre-backup', global_arguments.dry_run, ) dispatch.call_hooks( 'dump_databases', hooks, config_filename, dump.DATABASE_HOOK_NAMES, location, global_arguments.dry_run, ) if 'check' in arguments: command.execute_hook( hooks.get('before_check'), hooks.get('umask'), config_filename, 'pre-check', global_arguments.dry_run, ) except (OSError, CalledProcessError) as error: if command.considered_soft_failure(config_filename, error): return encountered_error = error yield from make_error_log_records( '{}: Error running pre hook'.format(config_filename), error) if not encountered_error: for repository_path in location['repositories']: try: yield from run_actions( arguments=arguments, location=location, storage=storage, retention=retention, consistency=consistency, hooks=hooks, local_path=local_path, remote_path=remote_path, repository_path=repository_path, ) except (OSError, CalledProcessError, ValueError) as error: encountered_error = error error_repository = repository_path yield from make_error_log_records( '{}: Error running actions for repository'.format( repository_path), error) if not encountered_error: try: if 'prune' in arguments: command.execute_hook( hooks.get('after_prune'), hooks.get('umask'), config_filename, 'post-prune', global_arguments.dry_run, ) if 'create' in arguments: dispatch.call_hooks( 'remove_database_dumps', hooks, config_filename, dump.DATABASE_HOOK_NAMES, location, global_arguments.dry_run, ) command.execute_hook( hooks.get('after_backup'), hooks.get('umask'), config_filename, 'post-backup', global_arguments.dry_run, ) if 'check' in arguments: command.execute_hook( hooks.get('after_check'), hooks.get('umask'), config_filename, 'post-check', global_arguments.dry_run, ) if {'prune', 'create', 'check'}.intersection(arguments): dispatch.call_hooks( 'ping_monitor', hooks, config_filename, monitor.MONITOR_HOOK_NAMES, monitor.State.FINISH, monitoring_log_level, global_arguments.dry_run, ) except (OSError, CalledProcessError) as error: if command.considered_soft_failure(config_filename, error): return encountered_error = error yield from make_error_log_records( '{}: Error running post hook'.format(config_filename), error) if encountered_error and prune_create_or_check: try: command.execute_hook( hooks.get('on_error'), hooks.get('umask'), config_filename, 'on-error', global_arguments.dry_run, repository=error_repository, error=encountered_error, output=getattr(encountered_error, 'output', ''), ) dispatch.call_hooks( 'ping_monitor', hooks, config_filename, monitor.MONITOR_HOOK_NAMES, monitor.State.FAIL, monitoring_log_level, global_arguments.dry_run, ) except (OSError, CalledProcessError) as error: if command.considered_soft_failure(config_filename, error): return yield from make_error_log_records( '{}: Error running on-error hook'.format(config_filename), error)