示例#1
0
    def test_run_actions_should_wait_for_the_process(self):
        actions = [{'initial': 1}, {'middle': 2}, {'final': 3}]

        run_task_actions('test', actions)

        self._initial_stream_mock.close.assert_called_once()
        self._middle_stream_mock.wait.assert_called_once()
示例#2
0
    def test_run_actions_should_kill_process_if_action_raises(self):
        actions = [{'initial': 1}, {'middle': 2}, {'ffailure': 3}]

        with self.assertRaises(Exception):
            run_task_actions('test', actions)

        self._middle_stream_mock.send_signal.assert_called_once()
示例#3
0
    def test_run_actions_should_fail_if_process_fails(self):
        actions = [{'initial': 1}, {'middle': 2}, {'final': 3}]
        self._middle_stream_mock.wait.return_value = 1

        with self.assertRaises(Exception):
            run_task_actions('test', actions)

        self._middle_stream_mock.stderr.__iter__.assert_called()
示例#4
0
def archive_file(backup_path: Path, file_path: Path, task: dict,
                 cloud_config: CloudConfig) -> Optional[str]:
    logger = logging.getLogger(__name__).getChild('archive_file')

    # Gets the actions used in this file and check if it is compressed or encrypted using any action
    actions = [next(iter(item.keys())) for item in task['actions']]
    has_compress_action = len(
        [item for item in actions if 'compress-' in item]) != 0
    has_encrypt_action = len([item
                              for item in actions if 'encrypt-' in item]) != 0

    filename = str(file_path.relative_to(backup_path))
    archive_actions = [
        {
            'from-file': str(file_path)
        },
    ]

    if cloud_config.compression_strategy is not None and not has_compress_action:
        archive_actions.append({
            f'compress-{cloud_config.compression_strategy}': {
                'level': cloud_config.compression_level,
                'cpus': cloud_config.compression_cpus,
            },
        })
        filename += f'.{cloud_config.compression_strategy}'
    if cloud_config.cypher_strategy is not None and not has_encrypt_action:
        archive_actions.append({
            'encrypt-gpg': {
                'passphrase': cloud_config.cypher_params.get('passphrase'),
                'recipients': cloud_config.cypher_params.get('keys', []),
                'algorithm': cloud_config.cypher_params.get('algorithm'),
            },
        })
        filename += '.asc'

    # If no extra actions are required, then just return and do nothing
    if len(archive_actions) == 1:
        logger.info(
            f'File {file_path} will not compress/encrypt because it is not required'
        )
        return None

    archive_actions.append(
        {'to-file': {
            '_backup_path': backup_path,
            'to': filename
        }})

    # Do the magic
    logger.info(f'Compressing/encrypting file {file_path} into {filename}')
    run_task_actions('archive-file', archive_actions)

    return filename
示例#5
0
def archive_folder(backup_path: Path, folder: Path,
                   cloud_config: CloudConfig) -> str:
    """
    Given a folder of a backup, archives it into a ``tar`` file and, optionally, compresses the file using different
    strategies. By default, no compression is done.

    A strategy function must be a function that returns a tuple of the command to execute (as pipe) for compress the
    ``tar`` file and the extension to add to the file name. There's some predefined strategies that you can
    use to compress the folder, all available in this package.

    The returned value is the file name for the archived folder.
    """
    logger = logging.getLogger(__name__).getChild('archive_folder')
    filename = str(folder.relative_to(backup_path)) + '.tar'
    actions = [
        {
            'from-directory': str(folder)
        },
        {
            'tar': None
        },
    ]

    if cloud_config.compression_strategy is not None:
        actions.append({
            f'compress-{cloud_config.compression_strategy}': {
                'level': cloud_config.compression_level,
                'cpus': cloud_config.compression_cpus,
            },
        })
        filename += f'.{cloud_config.compression_strategy}'
    if cloud_config.cypher_strategy is not None:
        actions.append({
            'encrypt-gpg': {
                'passphrase': cloud_config.cypher_params.get('passphrase'),
                'recipients': cloud_config.cypher_params.get('keys', []),
                'algorithm': cloud_config.cypher_params.get('algorithm'),
            },
        })
        filename += '.asc'

    actions.append({'to-file': {'_backup_path': backup_path, 'to': filename}})

    # Do the magic
    logger.info(f'Compressing/encrypting directory {folder} into {filename}')
    run_task_actions('archive-folder', actions)

    return filename
示例#6
0
    def test_run_actions_should_return_path(self):
        actions = [{'final': 1}]

        result = run_task_actions('test', actions)

        self.assertIsNotNone(result)
        self.assertEqual(result, Path('final-file'))
示例#7
0
    def test_run_actions_with_final_action_not_returning_path_should_fail(
            self):
        actions = [{'final-with-no-path': 1}]

        with self.assertRaises(ValueError):
            run_task_actions('test', actions)
示例#8
0
    def test_run_actions_with_unexisting_action_should_fail(self):
        actions = [{'nope': None}]

        with self.assertRaises(KeyError):
            run_task_actions('test', actions)
示例#9
0
    def test_run_actions_with_action_failing_should_raise_the_exception(self):
        actions = [{'ffailure': 1}]

        with self.assertRaisesRegex(KeyError, r'f'):
            run_task_actions('test', actions)
示例#10
0
    def test_run_actions_with_incompatible_action_should_fail(self):
        actions = [{'initial': 1}]

        with self.assertRaises(Exception):
            run_task_actions('test', actions)
示例#11
0
    def test_run_actions_should_work(self):
        actions = [{'final': 1}]

        run_task_actions('test', actions)
示例#12
0
 def test_run_actions_should_work(self):
     run_task_actions('test', [])
示例#13
0
    def test_run_actions_should_work_2(self):
        actions = [{'initial': 1}, {'middle': 2}, {'final': 3}]

        run_task_actions('test', actions)
示例#14
0
    def test_run_actions_with_bad_action_should_fail(self):
        actions = [{'bad': ':(', 'final': ':(('}]

        with self.assertRaises(Exception):
            run_task_actions('test', actions)
示例#15
0
    def test_run_actions_with_action_failing_should_raise_the_exception(self):
        actions = [{'initial': 1}, {'ffailure': 2}]

        with self.assertRaises(KeyError):
            run_task_actions('test', actions)
示例#16
0
    def test_run_actions_should_dispose_streams(self):
        actions = [{'initial': 1}, {'final': 2}]

        run_task_actions('test', actions)

        self._initial_stream_mock.close.assert_called_once()