Esempio n. 1
0
    def test_mount_overlay(self, scripts_mock):
        image_id_1 = self.make_image_id(1)
        image_id_2 = self.make_image_id(2)
        self.create_image_dir(
            image_id_1,
            images.ImageMetadata(name='base', version='0.0.1'),
        )
        self.create_image_dir(image_id_2, self.sample_metadata)

        pods._mount_overlay(self.sample_pod_dir_path, self.sample_config)
        scripts_mock.run.assert_called_once_with([
            'mount',
            *('-t', 'overlay'),
            *(
                '-o',
                'lowerdir=%s,upperdir=%s,workdir=%s' % (
                    ':'.join([
                        str(pods._get_image_rootfs_path(image_id_2)),
                        str(pods._get_image_rootfs_path(image_id_1)),
                    ]),
                    pods._get_upper_path(self.sample_pod_dir_path),
                    pods._get_work_path(self.sample_pod_dir_path),
                ),
            ),
            'overlay',
            pods._get_rootfs_path(self.sample_pod_dir_path),
        ])
Esempio n. 2
0
    def test_cmd_list(self):

        def cmd_list():
            return sorted(result['id'] for result in pods.cmd_list())

        self.assertEqual(cmd_list(), [])

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        for i, image in enumerate(self.sample_config.images):
            self.create_image_dir(
                self.make_image_id(i + 1),
                images.ImageMetadata(name=image.name, version=image.version),
            )
        self.assertEqual(cmd_list(), [self.sample_pod_id])
Esempio n. 3
0
 def test_prepare_pod_dir(self):
     self.sample_pod_dir_path.mkdir()
     for i, image in enumerate(self.sample_config.images):
         self.create_image_dir(
             self.make_image_id(i + 1),
             images.ImageMetadata(name=image.name, version=image.version),
         )
     with unittest.mock.patch.multiple(
         pods.__name__,
         scripts=unittest.mock.DEFAULT,
         # We don't have a valid base image, and so we can't really
         # call ``builders.generate_unit_file``, etc.
         builders=unittest.mock.DEFAULT,
         _generate_hostname=unittest.mock.DEFAULT,
     ):
         pods._prepare_pod_dir(
             self.sample_pod_dir_path,
             self.sample_pod_id,
             self.sample_config,
         )
Esempio n. 4
0
    def test_cmd_prepare(self):
        config_path = self.test_repo_path / 'sample-config'
        jsons.dump_dataobject(self.sample_config, config_path)
        for i, image in enumerate(self.sample_config.images):
            self.create_image_dir(
                self.make_image_id(i + 1),
                images.ImageMetadata(name=image.name, version=image.version),
            )
        self.assertEqual(self.list_pod_dir_paths(), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        with unittest.mock.patch.multiple(
            pods.__name__,
            scripts=unittest.mock.DEFAULT,
            # We don't have a valid base image, and so we can't really
            # call ``builders.generate_unit_file``, etc.
            builders=unittest.mock.DEFAULT,
            _generate_hostname=unittest.mock.DEFAULT,
        ):
            pods.cmd_prepare(self.sample_pod_id, config_path)
        self.assertEqual(self.list_pod_dir_paths(), [self.sample_pod_id])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        self.assertFalse(self.check_exclusive(self.sample_pod_dir_path))
Esempio n. 5
0
class XarsTest(
        fixtures.TestCaseBase,
        filelocks.Fixture if filelocks else object,
):

    sample_xar_name = 'foo.sh'

    sample_exec_relpath = Path('a/b/c/foo.sh')

    sample_image_id = '0123456789abcdef' * 4

    sample_metadata = images.ImageMetadata(
        name='sample-app',
        version='1.0',
    )

    def setUp(self):
        super().setUp()
        self.xar_runner_script_dir_path = self.test_repo_path / 'runner-bin'
        self.xar_runner_script_dir_path.mkdir()
        bases.PARAMS.xar_runner_script_directory.unsafe_set(
            self.xar_runner_script_dir_path)
        bases.cmd_init()
        images.cmd_init()
        xars.cmd_init()
        self.sample_xar_dir_path = xars._get_xar_dir_path(self.sample_xar_name)
        self.sample_xar_runner_script_path = (self.xar_runner_script_dir_path /
                                              self.sample_xar_name)
        self.sample_image_dir_path = images.get_image_dir_path(
            self.sample_image_id)

    @staticmethod
    def make_image_id(id_int):
        return '%064d' % id_int

    @staticmethod
    def create_image_dir(image_id, metadata, exec_relpath):
        image_dir_path = images.get_image_dir_path(image_id)
        image_dir_path.mkdir()
        jsons.dump_dataobject(
            metadata,
            images._get_metadata_path(image_dir_path),
        )
        rootfs_path = images.get_rootfs_path(image_dir_path)
        (rootfs_path / exec_relpath.parent).mkdir(parents=True)
        (rootfs_path / exec_relpath).touch()

    @staticmethod
    def list_xar_dir_paths():
        return sorted(p.name for p in xars._iter_xar_dir_paths())

    def list_ref_image_ids(self):
        return sorted(xars._iter_ref_image_ids(self.sample_xar_dir_path))

    def has_ref_image_id(self, image_id):
        return xars._has_ref_image_id(self.sample_xar_dir_path, image_id)

    def add_ref_image_id(self, image_id):
        xars._add_ref_image_id(self.sample_xar_dir_path, image_id)

    def maybe_remove_ref_image_id(self, image_id):
        return xars._maybe_remove_ref_image_id(self.sample_xar_dir_path,
                                               image_id)

    def install_sample(self):
        self.create_image_dir(
            self.sample_image_id,
            self.sample_metadata,
            self.sample_exec_relpath,
        )
        xars.cmd_install(
            image_id=self.sample_image_id,
            xar_name=self.sample_xar_name,
            exec_relpath=self.sample_exec_relpath,
        )

    #
    # Data type.
    #

    def test_validate(self):
        for xar_name in (
                'hello-world',
                '01_23.sh',
        ):
            self.assertEqual(models.validate_xar_name(xar_name), xar_name)
            self.assertEqual(models.validate_xar_name(xar_name), xar_name)
        for invalid_xar_name in (
                '',
                'a/b',
        ):
            with self.assertRaisesRegex(AssertionError,
                                        r'expect .*fullmatch.*'):
                models.validate_xar_name(invalid_xar_name)

    #
    # Top-level commands.
    #

    def test_cmd_install(self):
        self.create_image_dir(
            self.sample_image_id,
            self.sample_metadata,
            self.sample_exec_relpath,
        )
        self.assertEqual(self.list_xar_dir_paths(), [])

        image_id_1 = self.make_image_id(1)
        pattern = r'expect.*is_file.*images/trees/%s/metadata' % image_id_1
        with self.assertRaisesRegex(AssertionError, pattern):
            xars.cmd_install(
                image_id=image_id_1,
                xar_name=self.sample_xar_name,
                exec_relpath=self.sample_exec_relpath,
            )
        self.assertEqual(self.list_xar_dir_paths(), [])

        xars.cmd_install(
            name=self.sample_metadata.name,
            version=self.sample_metadata.version,
            xar_name=self.sample_xar_name,
            exec_relpath=self.sample_exec_relpath,
        )
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])

        with self.assertRaisesRegex(AssertionError, r'expect non-None value'):
            xars.cmd_install(
                name='no-such-name',
                version='no-such-version',
                xar_name=self.sample_xar_name,
                exec_relpath=self.sample_exec_relpath,
            )

    def test_cmd_list(self):
        self.assertEqual(list(xars.cmd_list()), [])

        self.install_sample()
        self.assertEqual(
            list(xars.cmd_list()),
            [{
                'xar': self.sample_xar_name,
                'id': self.sample_image_id,
                'name': self.sample_metadata.name,
                'version': self.sample_metadata.version,
                'exec': self.sample_exec_relpath,
                'active': False,
            }],
        )

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    @unittest.mock.patch(xars.__name__ + '.os')
    def test_cmd_exec(self, os_mock):
        with self.assertRaisesRegex(AssertionError, r'expect.*is_dir.*foo.sh'):
            xars.cmd_exec(self.sample_xar_name, [])
        os_mock.execv.assert_not_called()
        os_mock.execv.reset_mock()

        self.install_sample()
        exec_abspath = xars._get_exec_path(self.sample_xar_dir_path).resolve()

        xars.cmd_exec(self.sample_xar_name, ['1', '2', '3'])
        os_mock.execv.assert_called_once_with(
            str(exec_abspath), [self.sample_xar_name, '1', '2', '3'])
        os_mock.execv.reset_mock()

        with self.using_shared(
                xars._get_ref_path(self.sample_xar_dir_path,
                                   self.sample_image_id)):
            xars.cmd_uninstall(self.sample_xar_name)

        with self.assertRaisesRegex(AssertionError, r'expect.*exists.*foo.sh'):
            xars.cmd_exec(self.sample_xar_name, ['4', '5', '6'])
        os_mock.execv.assert_not_called()
        os_mock.execv.reset_mock()

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_cmd_uninstall(self):
        self.assertEqual(self.list_xar_dir_paths(), [])

        exec_path = xars._get_exec_path(self.sample_xar_dir_path)
        self.install_sample()
        self.assertTrue(exec_path.exists())
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])
        self.assertTrue(self.sample_xar_runner_script_path.exists())

        with self.using_shared(
                xars._get_ref_path(self.sample_xar_dir_path,
                                   self.sample_image_id)):
            xars.cmd_uninstall(self.sample_xar_name)
        self.assertFalse(exec_path.exists())
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])
        self.assertFalse(self.sample_xar_runner_script_path.exists())

        xars.cmd_uninstall(self.sample_xar_name)
        self.assertFalse(exec_path.exists())
        self.assertEqual(self.list_xar_dir_paths(), [])
        self.assertFalse(self.sample_xar_runner_script_path.exists())

        # Okay to remove non-existent xar.
        xars.cmd_uninstall(self.sample_xar_name)
        self.assertFalse(exec_path.exists())
        self.assertEqual(self.list_xar_dir_paths(), [])
        self.assertFalse(self.sample_xar_runner_script_path.exists())

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_cmd_cleanup(self):
        self.assertEqual(self.list_xar_dir_paths(), [])

        exec_path = xars._get_exec_path(self.sample_xar_dir_path)
        self.install_sample()
        self.assertTrue(exec_path.exists())
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])
        self.assertTrue(self.sample_xar_runner_script_path.exists())

        xars.cmd_cleanup()
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])
        self.assertTrue(self.sample_xar_runner_script_path.exists())

        with self.using_shared(
                xars._get_ref_path(self.sample_xar_dir_path,
                                   self.sample_image_id)):
            xars.cmd_uninstall(self.sample_xar_name)
        self.assertEqual(self.list_xar_dir_paths(), [self.sample_xar_name])
        self.assertFalse(self.sample_xar_runner_script_path.exists())

        xars.cmd_cleanup()
        self.assertEqual(self.list_xar_dir_paths(), [])
        self.assertFalse(self.sample_xar_runner_script_path.exists())

    #
    # Repo layout.
    #

    def test_repo_layout(self):
        for actual, expect in (
            (
                xars._get_xars_repo_path(),
                bases.get_repo_path() / 'xars',
            ),
            (
                xars._get_xar_dir_path('foo.sh'),
                xars._get_xars_repo_path() / 'foo.sh',
            ),
            (
                xars._get_name(xars._get_xar_dir_path('foo.sh')),
                'foo.sh',
            ),
            (
                xars._get_deps_path(xars._get_xar_dir_path('foo.sh')),
                xars._get_xar_dir_path('foo.sh') / 'deps',
            ),
            (
                xars._get_ref_path(
                    xars._get_xar_dir_path('foo.sh'),
                    self.sample_image_id,
                ),
                xars._get_deps_path(xars._get_xar_dir_path('foo.sh')) /
                self.sample_image_id,
            ),
            (
                xars._get_exec_path(xars._get_xar_dir_path('foo.sh')),
                xars._get_xar_dir_path('foo.sh') / 'exec',
            ),
            (
                xars._get_image_rootfs_abspath(self.sample_image_id),
                images.get_rootfs_path(self.sample_image_dir_path),
            ),
            (
                xars._get_xar_runner_script_path('foo.sh'),
                self.xar_runner_script_dir_path / 'foo.sh',
            ),
        ):
            self.assertEqual(actual, expect)

        exec_abspath = xars._get_exec_path(self.sample_xar_dir_path).absolute()
        with self.assertRaisesRegex(ValueError, r' does not start with '):
            xars._get_exec_relpath(exec_abspath, self.sample_image_id)
        with self.assertRaisesRegex(ValueError, r' does not start with '):
            xars._get_image_id(exec_abspath)

        self.install_sample()

        exec_abspath = xars._get_exec_path(self.sample_xar_dir_path).resolve()
        self.assertEqual(
            xars._get_exec_relpath(exec_abspath, self.sample_image_id),
            self.sample_exec_relpath,
        )
        self.assertEqual(
            xars._get_image_id(exec_abspath),
            self.sample_image_id,
        )
        exec_target = xars._get_exec_target(self.sample_image_id,
                                            self.sample_exec_relpath)
        self.assertEqual(
            exec_target,
            Path('../../images/trees') / self.sample_image_id / 'rootfs' /
            self.sample_exec_relpath,
        )
        self.assertTrue((self.sample_xar_dir_path / exec_target).exists())

    #
    # Xar directories.
    #

    def test_iter_xar_dir_paths(self):
        self.create_image_dir(
            self.sample_image_id,
            self.sample_metadata,
            self.sample_exec_relpath,
        )

        self.assertEqual(self.list_xar_dir_paths(), [])
        self.assertEqual(images._get_ref_count(self.sample_image_dir_path), 1)

        xars._install_xar_dir(
            xars._get_xar_dir_path('foo.sh'),
            self.sample_image_id,
            self.sample_exec_relpath,
        )
        self.assertEqual(self.list_xar_dir_paths(), ['foo.sh'])
        self.assertEqual(images._get_ref_count(self.sample_image_dir_path), 2)

        xars._install_xar_dir(
            xars._get_xar_dir_path('bar.sh'),
            self.sample_image_id,
            self.sample_exec_relpath,
        )
        self.assertEqual(self.list_xar_dir_paths(), ['bar.sh', 'foo.sh'])
        self.assertEqual(images._get_ref_count(self.sample_image_dir_path), 3)

    #
    # Xar directory.
    #

    def test_xar_dir(self):
        def assert_absent():
            self.assertFalse(self.sample_xar_dir_path.exists())
            self.assertFalse(self.sample_xar_runner_script_path.exists())
            self.assertEqual(images._get_ref_count(image_dir_path_1), 1)
            self.assertEqual(images._get_ref_count(image_dir_path_2), 1)

        def assert_present(current_image_id, image_ids):
            self.assertTrue(self.sample_xar_dir_path.exists())
            self.assertEqual(
                xars._get_exec_path(self.sample_xar_dir_path).resolve(),
                images.get_rootfs_path(
                    images.get_image_dir_path(current_image_id)) /
                self.sample_exec_relpath,
            )
            self.assertEqual(self.list_ref_image_ids(), image_ids)
            self.assertTrue(self.sample_xar_runner_script_path.exists())
            self.assertEqual(
                images._get_ref_count(image_dir_path_1),
                2 if image_id_1 in image_ids else 1,
            )
            self.assertEqual(
                images._get_ref_count(image_dir_path_2),
                2 if image_id_2 in image_ids else 1,
            )

        def install_xar_dir(image_id):
            xars._install_xar_dir(self.sample_xar_dir_path, image_id,
                                  self.sample_exec_relpath)

        image_id_1 = self.make_image_id(1)
        image_dir_path_1 = images.get_image_dir_path(image_id_1)
        self.create_image_dir(
            image_id_1,
            self.sample_metadata,
            self.sample_exec_relpath,
        )
        image_id_2 = self.make_image_id(2)
        image_dir_path_2 = images.get_image_dir_path(image_id_2)
        self.create_image_dir(
            image_id_2,
            self.sample_metadata,
            self.sample_exec_relpath,
        )
        image_id_3 = self.make_image_id(3)
        pattern = r'expect.*is_file.*images/trees/%s/metadata' % image_id_3

        assert_absent()

        with self.assertRaisesRegex(AssertionError, pattern):
            install_xar_dir(image_id_3)
        assert_absent()

        install_xar_dir(image_id_1)
        assert_present(image_id_1, [image_id_1])

        with self.assertRaisesRegex(AssertionError, pattern):
            install_xar_dir(image_id_3)
        assert_present(image_id_1, [image_id_1])

        install_xar_dir(image_id_2)
        assert_present(image_id_2, [image_id_1, image_id_2])

        with self.assertRaisesRegex(AssertionError, pattern):
            install_xar_dir(image_id_3)
        assert_present(image_id_2, [image_id_1, image_id_2])

        install_xar_dir(image_id_1)
        assert_present(image_id_1, [image_id_1, image_id_2])

        with self.assertRaisesRegex(AssertionError, pattern):
            install_xar_dir(image_id_3)
        assert_present(image_id_1, [image_id_1, image_id_2])

        xars._cleanup_xar_dir(self.sample_xar_dir_path)
        assert_present(image_id_1, [image_id_1])

        xars._remove_xar_dir(self.sample_xar_dir_path)
        assert_absent()

    #
    # Dependent images.
    #

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_ref_image_ids(self):
        image_id_1 = self.make_image_id(1)
        self.create_image_dir(
            image_id_1,
            self.sample_metadata,
            self.sample_exec_relpath,
        )
        image_id_2 = self.make_image_id(2)
        self.create_image_dir(
            image_id_2,
            self.sample_metadata,
            self.sample_exec_relpath,
        )

        xars._install_xar_dir(
            self.sample_xar_dir_path,
            image_id_1,
            self.sample_exec_relpath,
        )
        self.assertEqual(self.list_ref_image_ids(), [image_id_1])
        self.assertTrue(self.has_ref_image_id(image_id_1))
        self.assertFalse(self.has_ref_image_id(image_id_2))

        self.add_ref_image_id(image_id_2)
        self.assertEqual(self.list_ref_image_ids(), [image_id_1, image_id_2])
        self.assertTrue(self.has_ref_image_id(image_id_1))
        self.assertTrue(self.has_ref_image_id(image_id_2))

        self.assertTrue(self.maybe_remove_ref_image_id(image_id_1))
        self.assertEqual(self.list_ref_image_ids(), [image_id_2])
        self.assertFalse(self.has_ref_image_id(image_id_1))
        self.assertTrue(self.has_ref_image_id(image_id_2))

        with self.using_shared(
                xars._get_ref_path(self.sample_xar_dir_path, image_id_2)):
            self.assertFalse(self.maybe_remove_ref_image_id(image_id_2))
        self.assertEqual(self.list_ref_image_ids(), [image_id_2])
        self.assertFalse(self.has_ref_image_id(image_id_1))
        self.assertTrue(self.has_ref_image_id(image_id_2))
Esempio n. 6
0
class PodsTest(
    fixtures.TestCaseBase,
    filelocks.Fixture if filelocks else object,
):

    sample_pod_id = '01234567-89ab-cdef-0123-456789abcdef'

    sample_config = models.PodConfig(
        name='test-pod',
        version='0.0.1',
        apps=[
            models.PodConfig.App(
                name='hello',
                exec=['/bin/echo', 'hello', 'world'],
            ),
        ],
        images=[
            models.PodConfig.Image(
                name='base',
                version='0.0.1',
            ),
            models.PodConfig.Image(
                name='sample-app',
                version='1.0',
            ),
        ],
        mounts=[
            models.PodConfig.Mount(
                source='/dev/null',
                target='/this/is/pod/path',
                read_only=True,
            ),
        ],
        overlays=[
            models.PodConfig.Overlay(
                sources=[''],
                target='/this/is/some/other/pod/path',
                read_only=False,
            ),
        ],
    )

    sample_image_id = '0123456789abcdef' * 4

    sample_metadata = images.ImageMetadata(
        name='sample-app',
        version='1.0',
    )

    def setUp(self):
        super().setUp()
        bases.cmd_init()
        images.cmd_init()
        pods.cmd_init()
        self.sample_pod_dir_path = pods._get_pod_dir_path(self.sample_pod_id)
        patcher = unittest.mock.patch.object(pods, 'journals')
        self.mock_journals = patcher.start()
        self.addCleanup(patcher.stop)

    @staticmethod
    def make_pod_id(id_int):
        return str(uuid.UUID(int=id_int))

    @staticmethod
    def create_pod_dir(pod_id, config):
        pod_dir_path = pods._get_pod_dir_path(pod_id)
        pod_dir_path.mkdir()
        pods._setup_pod_dir_barely(pod_dir_path, config)
        pods._pod_dir_create_config(pod_dir_path, config)

    @staticmethod
    def list_pod_dir_paths():
        return sorted(p.name for p in pods._iter_pod_dir_paths())

    @staticmethod
    def list_active():
        return sorted(p.name for p in pods._get_active_path().iterdir())

    @staticmethod
    def list_graveyard():
        return sorted(p.name for p in pods._get_graveyard_path().iterdir())

    @staticmethod
    def list_tmp():
        return sorted(p.name for p in pods._get_tmp_path().iterdir())

    @staticmethod
    def make_image_id(id_int):
        return '%064d' % id_int

    @staticmethod
    def create_image_dir(image_id, metadata):
        image_dir_path = images.get_image_dir_path(image_id)
        image_dir_path.mkdir()
        jsons.dump_dataobject(
            metadata,
            images._get_metadata_path(image_dir_path),
        )
        images.get_rootfs_path(image_dir_path).mkdir()

    #
    # Top-level commands.
    #

    def test_cmd_init(self):
        self.assertEqual(
            sorted(p.name for p in pods._get_pod_repo_path().iterdir()),
            ['active', 'graveyard', 'tmp'],
        )

    def test_cmd_list(self):

        def cmd_list():
            return sorted(result['id'] for result in pods.cmd_list())

        self.assertEqual(cmd_list(), [])

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        for i, image in enumerate(self.sample_config.images):
            self.create_image_dir(
                self.make_image_id(i + 1),
                images.ImageMetadata(name=image.name, version=image.version),
            )
        self.assertEqual(cmd_list(), [self.sample_pod_id])

    def test_cmd_show(self):
        with self.assertRaisesRegex(AssertionError, r'expect.*is_dir'):
            pods.cmd_show(self.sample_pod_id)

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.assertEqual(
            pods.cmd_show(self.sample_pod_id),
            [{
                'name': 'hello',
                'status': None,
                'last-updated': None,
                'ref-count': 1,
            }],
        )

    def test_cmd_cat_config(self):
        with self.assertRaisesRegex(AssertionError, r'expect.*is_file'):
            pods.cmd_cat_config(self.sample_pod_id, io.BytesIO())

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        buffer = io.BytesIO()
        pods.cmd_cat_config(self.sample_pod_id, buffer)
        self.assertEqual(
            buffer.getvalue(),
            pods._get_config_path(self.sample_pod_dir_path).read_bytes(),
        )

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_cmd_prepare(self):
        config_path = self.test_repo_path / 'sample-config'
        jsons.dump_dataobject(self.sample_config, config_path)
        for i, image in enumerate(self.sample_config.images):
            self.create_image_dir(
                self.make_image_id(i + 1),
                images.ImageMetadata(name=image.name, version=image.version),
            )
        self.assertEqual(self.list_pod_dir_paths(), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        with unittest.mock.patch.multiple(
            pods.__name__,
            scripts=unittest.mock.DEFAULT,
            # We don't have a valid base image, and so we can't really
            # call ``builders.generate_unit_file``, etc.
            builders=unittest.mock.DEFAULT,
            _generate_hostname=unittest.mock.DEFAULT,
        ):
            pods.cmd_prepare(self.sample_pod_id, config_path)
        self.assertEqual(self.list_pod_dir_paths(), [self.sample_pod_id])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        self.assertFalse(self.check_exclusive(self.sample_pod_dir_path))

    def test_cmd_remove(self):
        config_path = self.test_repo_path / 'sample-config'
        jsons.dump_dataobject(self.sample_config, config_path)
        self.assertEqual(self.list_pod_dir_paths(), [])
        self.assertEqual(list(pods._get_graveyard_path().iterdir()), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.assertEqual(pods._get_ref_count(self.sample_pod_dir_path), 1)
        self.assertEqual(self.list_pod_dir_paths(), [self.sample_pod_id])
        self.assertEqual(list(pods._get_graveyard_path().iterdir()), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])

        ref_path = self.test_repo_path / 'ref'
        pods.cmd_add_ref(self.sample_pod_id, ref_path)
        with unittest.mock.patch(pods.__name__ + '.scripts'):
            with self.assertRaisesRegex(
                AssertionError, r'expect x <= 1, not 2'
            ):
                pods.cmd_remove(self.sample_pod_id)
        self.assertEqual(pods._get_ref_count(self.sample_pod_dir_path), 2)
        self.assertEqual(self.list_pod_dir_paths(), [self.sample_pod_id])
        self.assertEqual(list(pods._get_graveyard_path().iterdir()), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])
        self.mock_journals.remove_journal_dir.assert_not_called()

        ref_path.unlink()
        with unittest.mock.patch(pods.__name__ + '.scripts'):
            pods.cmd_remove(self.sample_pod_id)
        self.assertEqual(self.list_pod_dir_paths(), [])
        self.assertEqual(list(pods._get_graveyard_path().iterdir()), [])
        self.assertEqual(list(pods._get_tmp_path().iterdir()), [])
        self.mock_journals.remove_journal_dir.assert_called_once_with(
            self.sample_pod_id
        )

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_cmd_cleanup(self):
        future = datetimes.utcnow() + datetime.timedelta(days=1)
        pod_id_1 = self.make_pod_id(1)
        pod_id_2 = self.make_pod_id(2)
        self.create_pod_dir(pod_id_1, self.sample_config)
        self.create_pod_dir(pod_id_2, self.sample_config)
        self.assertEqual(self.list_active(), [pod_id_1, pod_id_2])
        self.assertEqual(self.list_graveyard(), [])
        self.assertEqual(self.list_tmp(), [])

        ref_path = self.test_repo_path / 'ref'
        pods.cmd_add_ref(pod_id_1, ref_path)
        pods.cmd_cleanup(future)
        self.assertEqual(self.list_active(), [pod_id_1])
        self.assertEqual(self.list_graveyard(), [])
        self.assertEqual(self.list_tmp(), [])
        ref_path.unlink()
        self.mock_journals.remove_journal_dir.assert_called_once_with(pod_id_2)

        self.mock_journals.remove_journal_dir.reset_mock()
        with self.using_exclusive(pods._get_pod_dir_path(pod_id_1)):
            pods.cmd_cleanup(future)
        self.assertEqual(self.list_active(), [pod_id_1])
        self.assertEqual(self.list_graveyard(), [])
        self.assertEqual(self.list_tmp(), [])
        self.mock_journals.remove_journal_dir.assert_not_called()

        self.mock_journals.remove_journal_dir.reset_mock()
        pods.cmd_cleanup(future)
        self.assertEqual(self.list_active(), [])
        self.assertEqual(self.list_graveyard(), [])
        self.assertEqual(self.list_tmp(), [])
        self.mock_journals.remove_journal_dir.assert_called_once_with(pod_id_1)

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_cleanup_active(self):
        future = datetimes.utcnow() + datetime.timedelta(days=1)
        pod_id_1 = self.make_pod_id(1)
        pod_id_2 = self.make_pod_id(2)
        self.create_pod_dir(pod_id_1, self.sample_config)
        self.create_pod_dir(pod_id_2, self.sample_config)
        self.assertEqual(self.list_active(), [pod_id_1, pod_id_2])
        self.assertEqual(self.list_graveyard(), [])
        self.assertEqual(self.list_tmp(), [])

        with self.using_exclusive(pods._get_pod_dir_path(pod_id_1)):
            pods._cleanup_active(future)
        self.assertEqual(self.list_active(), [pod_id_1])
        self.assertEqual(self.list_graveyard(), [pod_id_2])
        self.assertEqual(self.list_tmp(), [])
        self.mock_journals.remove_journal_dir.assert_called_once_with(pod_id_2)

        self.mock_journals.remove_journal_dir.reset_mock()
        pods._cleanup_active(future)
        self.assertEqual(self.list_active(), [])
        self.assertEqual(self.list_graveyard(), [pod_id_1, pod_id_2])
        self.assertEqual(self.list_tmp(), [])
        self.mock_journals.remove_journal_dir.assert_called_once_with(pod_id_1)

    #
    # Locking strategy.
    #

    @unittest.skipUnless(filelocks, 'g1.tests.filelocks unavailable')
    def test_create_tmp_pod_dir(self):
        tmp_path = pods._create_tmp_pod_dir()
        self.assertFalse(self.check_exclusive(tmp_path))

    #
    # Data type.
    #

    def test_config(self):
        with self.assertRaisesRegex(AssertionError, r'expect non-empty'):
            models.PodConfig(
                name='test-pod',
                version='0.0.1',
                apps=self.sample_config.apps,
                images=[],
            )
        with self.assertRaisesRegex(
            AssertionError, r'expect unique elements in '
        ):
            models.PodConfig(
                name='test-pod',
                version='0.0.1',
                apps=[
                    models.PodConfig.App(name='some-app', exec=['/bin/true']),
                    models.PodConfig.App(name='some-app', exec=['/bin/false']),
                ],
                images=self.sample_config.images,
            )
        with self.assertRaisesRegex(
            AssertionError, r'expect unique elements in '
        ):
            models.PodConfig(
                name='test-pod',
                version='0.0.1',
                apps=self.sample_config.apps,
                images=self.sample_config.images,
                mounts=[
                    models.PodConfig.Mount(source='/p', target='/a'),
                ],
                overlays=[
                    models.PodConfig.Overlay(sources=['/q'], target='/a'),
                ],
            )
        with self.assertRaisesRegex(AssertionError, r'expect only one'):
            models.PodConfig.Image()
        with self.assertRaisesRegex(AssertionError, r'expect.*xor.*be false'):
            models.PodConfig.Image(name='name')
        with self.assertRaisesRegex(AssertionError, r'expect.*is_absolute'):
            models.PodConfig.Mount(source='foo', target='/bar')
        with self.assertRaisesRegex(AssertionError, r'expect.*is_absolute'):
            models.PodConfig.Mount(source='/foo', target='bar')
        with self.assertRaisesRegex(AssertionError, r'expect non-empty'):
            models.PodConfig.Overlay(sources=[], target='/bar')
        with self.assertRaisesRegex(AssertionError, r'expect.*is_absolute'):
            models.PodConfig.Overlay(sources=['foo'], target='/bar')
        with self.assertRaisesRegex(AssertionError, r'expect.*is_absolute'):
            models.PodConfig.Overlay(sources=['/foo'], target='bar')
        with self.assertRaisesRegex(AssertionError, r'expect x == 1, not 0'):
            models.PodConfig.Overlay(sources=['', '/foo'], target='/bar')

    def test_validate_id(self):
        self.assertEqual(
            models.validate_pod_id(self.sample_pod_id), self.sample_pod_id
        )
        for test_data in (
            '',
            '01234567-89AB-CDEF-0123-456789ABCDEF',
            '01234567-89ab-cdef-0123-456789abcde',
        ):
            with self.subTest(test_data):
                with self.assertRaisesRegex(
                    AssertionError, r'expect .*fullmatch.*'
                ):
                    models.validate_pod_id(test_data)

    def test_id_converter(self):
        self.assertEqual(
            models.
            pod_id_to_machine_id('01234567-89ab-cdef-0123-456789abcdef'),
            '0123456789abcdef0123456789abcdef',
        )
        self.assertEqual(
            models.machine_id_to_pod_id('0123456789abcdef0123456789abcdef'),
            '01234567-89ab-cdef-0123-456789abcdef',
        )

    def test_generate_id(self):
        id1 = models.generate_pod_id()
        id2 = models.generate_pod_id()
        self.assertNotEqual(id1, id2)
        self.assertEqual(models.validate_pod_id(id1), id1)
        self.assertEqual(models.validate_pod_id(id2), id2)

    #
    # Repo layout.
    #

    def test_repo_layout(self):
        for path1, path2 in (
            (
                pods._get_pod_repo_path(),
                bases.get_repo_path() / 'pods',
            ),
            (
                pods._get_active_path(),
                pods._get_pod_repo_path() / 'active',
            ),
            (
                pods._get_graveyard_path(),
                pods._get_pod_repo_path() / 'graveyard',
            ),
            (
                pods._get_tmp_path(),
                pods._get_pod_repo_path() / 'tmp',
            ),
            (
                pods._get_pod_dir_path(self.sample_pod_id),
                pods._get_active_path() / self.sample_pod_id,
            ),
            (
                pods._get_id(self.sample_pod_dir_path),
                self.sample_pod_id,
            ),
            (
                pods._get_config_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'config',
            ),
            (
                pods._get_orig_config_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'config.orig',
            ),
            (
                pods._get_deps_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'deps',
            ),
            (
                pods._get_work_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'work',
            ),
            (
                pods._get_upper_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'upper',
            ),
            (
                pods._get_rootfs_path(self.sample_pod_dir_path),
                pods._get_active_path() / self.sample_pod_id / 'rootfs',
            ),
        ):
            with self.subTest((path1, path2)):
                self.assertEqual(path1, path2)

    #
    # Top-level directories.
    #

    def test_cleanup_top_dir(self):
        pod_id_1 = self.make_pod_id(1)
        pod_id_2 = self.make_pod_id(2)
        self.create_pod_dir(pod_id_1, self.sample_config)
        self.create_pod_dir(pod_id_2, self.sample_config)
        self.assertEqual(self.list_pod_dir_paths(), [pod_id_1, pod_id_2])

        with unittest.mock.patch(pods.__name__ + '.scripts'):

            with locks.acquiring_exclusive(pods._get_pod_dir_path(pod_id_2)):
                pods._cleanup_top_dir(pods._get_active_path())
            self.assertEqual(self.list_pod_dir_paths(), [pod_id_2])

            pods._cleanup_top_dir(pods._get_active_path())
            self.assertEqual(self.list_pod_dir_paths(), [])

    #
    # Pod directories.
    #

    def test_iter_pod_dir_paths(self):
        pod_id_1 = self.make_pod_id(1)
        pod_id_2 = self.make_pod_id(2)

        (pods._get_active_path() / 'irrelevant').touch()
        self.assertEqual(self.list_pod_dir_paths(), [])
        self.assertEqual(self.list_active(), ['irrelevant'])

        self.create_pod_dir(pod_id_2, self.sample_config)
        self.assertEqual(self.list_pod_dir_paths(), [pod_id_2])

        (pods._get_active_path() / pod_id_1).mkdir()
        self.assertEqual(self.list_pod_dir_paths(), [pod_id_1, pod_id_2])

    def test_maybe_move_pod_dir_to_active(self):
        self.assertEqual(self.list_pod_dir_paths(), [])

        path = self.test_repo_path / 'some-dir'
        path.mkdir()
        self.assertTrue(path.exists())
        self.assertTrue(
            pods._maybe_move_pod_dir_to_active(path, self.sample_pod_id)
        )
        self.assertEqual(self.list_pod_dir_paths(), [self.sample_pod_id])
        self.assertFalse(path.exists())

        path.mkdir()
        self.assertFalse(
            pods._maybe_move_pod_dir_to_active(path, self.sample_pod_id)
        )

    def test_move_pod_dir_to_graveyard(self):

        def list_grave_paths():
            return sorted(p.name for p in pods._get_graveyard_path().iterdir())

        self.assertEqual(list_grave_paths(), [])

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.assertTrue(self.sample_pod_dir_path.exists())
        pods._move_pod_dir_to_graveyard(self.sample_pod_dir_path)
        self.assertEqual(list_grave_paths(), [self.sample_pod_id])
        self.assertFalse(self.sample_pod_dir_path.exists())

    #
    # Pod directory.
    #

    def test_prepare_pod_dir(self):
        self.sample_pod_dir_path.mkdir()
        for i, image in enumerate(self.sample_config.images):
            self.create_image_dir(
                self.make_image_id(i + 1),
                images.ImageMetadata(name=image.name, version=image.version),
            )
        with unittest.mock.patch.multiple(
            pods.__name__,
            scripts=unittest.mock.DEFAULT,
            # We don't have a valid base image, and so we can't really
            # call ``builders.generate_unit_file``, etc.
            builders=unittest.mock.DEFAULT,
            _generate_hostname=unittest.mock.DEFAULT,
        ):
            pods._prepare_pod_dir(
                self.sample_pod_dir_path,
                self.sample_pod_id,
                self.sample_config,
            )

    def test_setup_pod_dir_barely(self):
        pod_dir_path = pods._get_pod_dir_path(self.sample_pod_id)
        pod_dir_path.mkdir()
        pods._setup_pod_dir_barely(pod_dir_path, self.sample_config)
        self.assertFalse(
            pods._get_config_path(self.sample_pod_dir_path).is_file()
        )
        self.assertTrue(
            pods._get_orig_config_path(self.sample_pod_dir_path).is_file()
        )
        self.assertTrue(pods._get_deps_path(self.sample_pod_dir_path).is_dir())
        self.assertTrue(pods._get_work_path(self.sample_pod_dir_path).is_dir())
        self.assertTrue(
            pods._get_upper_path(self.sample_pod_dir_path).is_dir()
        )
        self.assertTrue(
            pods._get_rootfs_path(self.sample_pod_dir_path).is_dir()
        )
        self.assertEqual(
            sorted(p.name for p in self.sample_pod_dir_path.iterdir()),
            ['config.orig', 'deps', 'rootfs', 'upper', 'work'],
        )

    def test_remove_pod_dir(self):
        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.assertTrue(self.sample_pod_dir_path.is_dir())
        with unittest.mock.patch(pods.__name__ + '.scripts'):
            pods._remove_pod_dir(self.sample_pod_dir_path)
        self.assertFalse(self.sample_pod_dir_path.exists())

    #
    # Pod.
    #

    @unittest.mock.patch(pods.__name__ + '.scripts')
    def test_mount_overlay(self, scripts_mock):
        image_id_1 = self.make_image_id(1)
        image_id_2 = self.make_image_id(2)
        self.create_image_dir(
            image_id_1,
            images.ImageMetadata(name='base', version='0.0.1'),
        )
        self.create_image_dir(image_id_2, self.sample_metadata)

        pods._mount_overlay(self.sample_pod_dir_path, self.sample_config)
        scripts_mock.run.assert_called_once_with([
            'mount',
            *('-t', 'overlay'),
            *(
                '-o',
                'lowerdir=%s,upperdir=%s,workdir=%s' % (
                    ':'.join([
                        str(pods._get_image_rootfs_path(image_id_2)),
                        str(pods._get_image_rootfs_path(image_id_1)),
                    ]),
                    pods._get_upper_path(self.sample_pod_dir_path),
                    pods._get_work_path(self.sample_pod_dir_path),
                ),
            ),
            'overlay',
            pods._get_rootfs_path(self.sample_pod_dir_path),
        ])

    def test_make_bind_argument(self):
        self.assertEqual(
            pods._make_bind_argument(
                models.PodConfig.Mount(
                    source='/a',
                    target='/b',
                    read_only=True,
                )
            ),
            '--bind-ro=/a:/b',
        )
        self.assertEqual(
            pods._make_bind_argument(
                models.PodConfig.Mount(
                    source='/a',
                    target='/b',
                    read_only=False,
                )
            ),
            '--bind=/a:/b',
        )

    def test_make_overlay_argument(self):
        self.assertEqual(
            pods._make_overlay_argument(
                models.PodConfig.Overlay(
                    sources=['/a', '/b'],
                    target='/c',
                    read_only=True,
                )
            ),
            '--overlay-ro=/a:/b:/c',
        )
        self.assertEqual(
            pods._make_overlay_argument(
                models.PodConfig.Overlay(
                    sources=['/a', ''],
                    target='/b',
                    read_only=False,
                )
            ),
            '--overlay=/a::/b',
        )

    #
    # Configs.
    #

    def test_iter_configs(self):

        def list_configs():
            return sorted((p.name, c) for p, c in pods._iter_configs())

        pod_id_1 = self.make_pod_id(1)
        pod_id_2 = self.make_pod_id(2)

        self.assertEqual(list_configs(), [])

        self.create_pod_dir(pod_id_2, self.sample_config)
        self.assertEqual(list_configs(), [(pod_id_2, self.sample_config)])

        self.create_pod_dir(pod_id_1, self.sample_config)
        self.assertEqual(
            list_configs(),
            [(pod_id_1, self.sample_config), (pod_id_2, self.sample_config)],
        )

    def test_read_config(self):
        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.assertEqual(
            pods._read_config(self.sample_pod_dir_path),
            self.sample_config,
        )

    def test_write_config(self):
        self.assertFalse((self.test_repo_path / 'config').exists())
        pods._write_config(self.sample_config, self.test_repo_path)
        self.assertTrue((self.test_repo_path / 'config').exists())
        self.assertEqual(
            pods._read_config(self.test_repo_path),
            self.sample_config,
        )
        self.assertFalse((self.test_repo_path / 'config.orig').exists())

    def test_write_orig_config(self):
        self.assertFalse((self.test_repo_path / 'config.orig').exists())
        pods._write_orig_config(self.sample_config, self.test_repo_path)
        self.assertTrue((self.test_repo_path / 'config.orig').exists())
        self.assertEqual(
            pods._read_orig_config(self.test_repo_path),
            self.sample_config,
        )
        self.assertFalse((self.test_repo_path / 'config').exists())

    def test_iter_image_ids(self):

        def list_image_ids(config):
            return sorted(pods._iter_image_ids(config))

        self.create_image_dir(self.sample_image_id, self.sample_metadata)
        images.cmd_tag(image_id=self.sample_image_id, new_tag='some-tag')

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[models.PodConfig.Image(id=self.sample_image_id)],
        )
        self.assertEqual(list_image_ids(config), [self.sample_image_id])

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[models.PodConfig.Image(name='sample-app', version='1.0')],
        )
        self.assertEqual(list_image_ids(config), [self.sample_image_id])

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[models.PodConfig.Image(tag='some-tag')],
        )
        self.assertEqual(list_image_ids(config), [self.sample_image_id])

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[models.PodConfig.Image(name='no-such-app', version='1.0')],
        )
        with self.assertRaisesRegex(AssertionError, r'expect non-None value'):
            list_image_ids(config)

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[models.PodConfig.Image(tag='no-such-tag')],
        )
        with self.assertRaisesRegex(AssertionError, r'expect non-None value'):
            list_image_ids(config)

    #
    # Dependent images.
    #

    def test_add_ref_image_ids(self):

        def list_image_ids():
            return sorted(
                p.name for p in \
                pods._get_deps_path(self.sample_pod_dir_path).iterdir()
            )

        image_id_1 = self.make_image_id(1)
        image_id_2 = self.make_image_id(2)
        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.create_image_dir(image_id_1, self.sample_metadata)
        self.create_image_dir(image_id_2, self.sample_metadata)
        self.assertEqual(list_image_ids(), [])
        self.assertEqual(
            images._get_ref_count(images.get_image_dir_path(image_id_1)), 1
        )
        self.assertEqual(
            images._get_ref_count(images.get_image_dir_path(image_id_2)), 1
        )

        config = models.PodConfig(
            name='test-pod',
            version='0.0.1',
            apps=self.sample_config.apps,
            images=[
                models.PodConfig.Image(id=image_id_1),
                models.PodConfig.Image(id=image_id_2),
            ],
        )

        new_config = pods._add_ref_image_ids(self.sample_pod_dir_path, config)
        self.assertEqual(config, new_config)
        self.assertEqual(list_image_ids(), [image_id_1, image_id_2])
        self.assertEqual(
            images._get_ref_count(images.get_image_dir_path(image_id_1)), 2
        )
        self.assertEqual(
            images._get_ref_count(images.get_image_dir_path(image_id_2)), 2
        )

    def test_iter_ref_image_ids(self):

        def list_ref_image_ids(pod_dir_path):
            return sorted(pods._iter_ref_image_ids(pod_dir_path))

        image_id_1 = self.make_image_id(1)
        image_id_2 = self.make_image_id(2)
        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        self.create_image_dir(image_id_1, self.sample_metadata)
        self.create_image_dir(image_id_2, self.sample_metadata)
        self.assertEqual(list_ref_image_ids(self.sample_pod_dir_path), [])

        pods._add_ref_image_ids(
            self.sample_pod_dir_path,
            models.PodConfig(
                name='test-pod',
                version='0.0.1',
                apps=self.sample_config.apps,
                images=[
                    models.PodConfig.Image(id=image_id_1),
                    models.PodConfig.Image(id=image_id_2),
                ],
            ),
        )
        self.assertEqual(
            list_ref_image_ids(self.sample_pod_dir_path),
            [image_id_1, image_id_2],
        )

    #
    # Pod runtime state.
    #

    def test_get_pod_status(self):
        self.assertEqual(
            pods._get_pod_status(self.sample_pod_dir_path, self.sample_config),
            {},
        )

        self.create_pod_dir(self.sample_pod_id, self.sample_config)
        app = self.sample_config.apps[0]
        path = builders._get_pod_app_exit_status_path(
            pods._get_rootfs_path(self.sample_pod_dir_path), app
        )
        path.parent.mkdir(parents=True)
        path.write_text('99')
        pod_status = pods._get_pod_status(
            self.sample_pod_dir_path, self.sample_config
        )
        self.assertEqual(list(pod_status.keys()), [app.name])
        self.assertEqual(pod_status[app.name][0], 99)

    def test_get_last_updated(self):
        self.assertIsNone(pods._get_last_updated({}))
        self.assertEqual(
            pods._get_last_updated({
                'app-1': (0, datetime.datetime(2001, 1, 1)),
            }),
            datetime.datetime(2001, 1, 1),
        )
        self.assertEqual(
            pods._get_last_updated({
                'app-1': (0, datetime.datetime(2001, 1, 1)),
                'app-2': (0, datetime.datetime(2002, 1, 1)),
            }),
            datetime.datetime(2002, 1, 1),
        )

    #
    # Helpers for mount/umount.
    #

    def test_umount(self):
        path1 = pods._get_pod_repo_path() / 'some-file'
        path2 = pods._get_pod_repo_path() / 'some-dir'
        path3 = pods._get_pod_repo_path() / 'link-to-dir'
        path1.touch()
        path2.mkdir()
        path3.symlink_to(path2)
        pods._umount(path1)
        pods._umount(path2)
        with self.assertRaisesRegex(AssertionError, r'expect not.*is_symlink'):
            pods._umount(path3)