Beispiel #1
0
 def setUp(self):
     self.active_mounts: List[bytes] = [b'/mnt/active1', b'/mnt/active2']
     self.mount_table = FakeMountTable()
     self.mount_table.stats['/mnt/active1'] = mtab.MTStat(
         st_uid=os.getuid(), st_dev=10)
     self.mount_table.stats['/mnt/active2'] = mtab.MTStat(
         st_uid=os.getuid(), st_dev=11)
     self.check = doctor.StaleMountsCheck(
         active_mount_points=self.active_mounts,
         mount_table=self.mount_table)
Beispiel #2
0
    def test_unconfigured_mounts_dont_crash(self):
        # If Eden advertises that a mount is active, but it is not in the
        # configuration, then at least don't throw an exception.
        tmp_dir = tempfile.mkdtemp(prefix='eden_test.')
        try:
            edenfs_path1 = os.path.join(tmp_dir, 'path1')
            edenfs_path2 = os.path.join(tmp_dir, 'path2')

            mount_paths = OrderedDict()
            mount_paths[edenfs_path1] = {
                'bind-mounts': {},
                'mount': edenfs_path1,
                'scm_type': 'hg',
                'snapshot': 'abcd' * 10,
                'client-dir': '/I_DO_NOT_EXIST1'
            }
            # path2 is not configured in the config...
            config = FakeConfig(mount_paths, is_healthy=True)
            # ... but is advertised by the daemon...
            config.get_thrift_client()._mounts = [
                eden_ttypes.MountInfo(mountPoint=edenfs_path1),
                eden_ttypes.MountInfo(mountPoint=edenfs_path2),
            ]

            # ... and is in the system mount table.
            mount_table = FakeMountTable()
            mount_table.stats[edenfs_path1] = mtab.MTStat(st_uid=os.getuid(),
                                                          st_dev=11)
            mount_table.stats[edenfs_path2] = mtab.MTStat(st_uid=os.getuid(),
                                                          st_dev=12)

            os.mkdir(edenfs_path1)
            hg_dir = os.path.join(edenfs_path1, '.hg')
            os.mkdir(hg_dir)
            dirstate = os.path.join(hg_dir, 'dirstate')
            dirstate_hash = b'\xab\xcd' * 10
            parents = (dirstate_hash, b'\x00' * 20)
            with open(dirstate, 'wb') as f:
                eden.dirstate.write(f, parents, tuples_dict={}, copymap={})

            dry_run = False
            out = io.StringIO()
            exit_code = doctor.cure_what_ails_you(config, dry_run, out,
                                                  mount_table)
        finally:
            shutil.rmtree(tmp_dir)

        self.assertEqual(
            f'''\
Performing 3 checks for {edenfs_path1}.
All is well.
''', out.getvalue())
        self.assertEqual(0, exit_code)
Beispiel #3
0
    def test_not_all_mounts_have_watchman_watcher(self, mock_watchman):
        edenfs_path = '/path/to/eden-mount'
        edenfs_path_not_watched = '/path/to/eden-mount-not-watched'
        side_effects: List[Dict[str, Any]] = []
        calls = []

        calls.append(call(['watch-list']))
        side_effects.append({'roots': [edenfs_path]})
        calls.append(call(['watch-project', edenfs_path]))
        side_effects.append({'watcher': 'eden'})
        calls.append(call(['debug-get-subscriptions', edenfs_path]))
        side_effects.append({})
        mock_watchman.side_effect = side_effects

        out = io.StringIO()
        dry_run = False
        mount_paths = OrderedDict()
        mount_paths[edenfs_path] = {
            'bind-mounts': {},
            'mount': edenfs_path,
            'scm_type': 'git',
            'snapshot': 'abcd' * 10,
            'client-dir': '/I_DO_NOT_EXIST'
        }
        mount_paths[edenfs_path_not_watched] = {
            'bind-mounts': {},
            'mount': edenfs_path_not_watched,
            'scm_type': 'git',
            'snapshot': 'abcd' * 10,
            'client-dir': '/I_DO_NOT_EXIST'
        }
        config = FakeConfig(mount_paths, is_healthy=True)
        config.get_thrift_client()._mounts = [
            eden_ttypes.MountInfo(mountPoint=edenfs_path),
            eden_ttypes.MountInfo(mountPoint=edenfs_path_not_watched),
        ]
        mount_table = FakeMountTable()
        mount_table.stats['/path/to/eden-mount'] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=10)
        mount_table.stats['/path/to/eden-mount-not-watched'] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11)
        exit_code = doctor.cure_what_ails_you(config,
                                              dry_run,
                                              out,
                                              mount_table=mount_table)

        self.assertEqual(
            'Performing 2 checks for /path/to/eden-mount.\n'
            'Performing 2 checks for /path/to/eden-mount-not-watched.\n'
            'All is well.\n', out.getvalue())
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(0, exit_code)
Beispiel #4
0
    def test_bind_mounts_okay(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual("", out)
        self.assert_results(fixer, num_problems=0)
Beispiel #5
0
    def test_bind_mounts_and_client_dir_missing(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm2] = self.bm2
        mount_table.bind_mount_success_paths[self.client_bm3] = self.bm3

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbandroid-buck-out
Creating directory {self.fbsource_bind_mounts}/fbandroid-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Creating directory {self.fbsource_bind_mounts}/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=4, num_fixed_problems=4)
Beispiel #6
0
    def test_does_not_unmount_mounts_with_same_device_as_active_mount(self):
        self.mount_table.set_eden_mounts(self.active_mounts + [b'/mnt/stale1'])
        self.mount_table.stats['/mnt/stale1'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=10)

        result = self.check.do_check(dry_run=False)
        self.assertEqual('', result.message)
        self.assertEqual(doctor.CheckResultType.NO_ISSUE, result.result_type)
        self.assertEqual([], self.mount_table.unmount_lazy_calls)
        self.assertEqual([], self.mount_table.unmount_force_calls)
Beispiel #7
0
    def test_bind_mounts_and_client_dir_missing_dry_run(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbandroid-buck-out
Would create directory {self.fbsource_bind_mounts}/fbandroid-buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Would create directory {self.fbsource_bind_mounts}/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=4)
Beispiel #8
0
    def test_dry_run_prints_stale_mounts_and_does_not_unmount(self):
        self.mount_table.set_eden_mounts(self.active_mounts +
                                         [b'/mnt/stale2', b'/mnt/stale1'])
        self.mount_table.stats['/mnt/stale1'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=12)
        self.mount_table.stats['/mnt/stale2'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=13)

        result = self.check.do_check(dry_run=True)
        self.assertEqual(doctor.CheckResultType.NOT_FIXED_BECAUSE_DRY_RUN,
                         result.result_type)
        self.assertEqual(
            dedent('''\
            Found 2 stale edenfs mount points:
              /mnt/stale1
              /mnt/stale2
            Not unmounting because dry run.
        '''), result.message)
        self.assertEqual([], self.mount_table.unmount_lazy_calls)
        self.assertEqual([], self.mount_table.unmount_force_calls)
Beispiel #9
0
    def test_bind_mount_wrong_device(self):
        # bm1, bm2 should not have same device as edenfs
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1
        mount_table.bind_mount_success_paths[self.client_bm2] = self.bm2

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbcode/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<green>fixed<reset>

""",
            out,
        )
        self.assert_results(fixer, num_problems=2, num_fixed_problems=2)
Beispiel #10
0
    def test_force_unmounts_if_lazy_fails(self):
        self.mount_table.set_eden_mounts(self.active_mounts +
                                         [b'/mnt/stale1', b'/mnt/stale2'])
        self.mount_table.fail_unmount_lazy(b'/mnt/stale1')
        self.mount_table.stats['/mnt/stale1'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=12)
        self.mount_table.stats['/mnt/stale2'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=13)

        result = self.check.do_check(dry_run=False)
        self.assertEqual(
            dedent('''\
            Unmounted 2 stale edenfs mount points:
              /mnt/stale1
              /mnt/stale2
        '''), result.message)
        self.assertEqual(doctor.CheckResultType.FIXED, result.result_type)
        self.assertEqual([b'/mnt/stale1', b'/mnt/stale2'],
                         self.mount_table.unmount_lazy_calls)
        self.assertEqual([b'/mnt/stale1'],
                         self.mount_table.unmount_force_calls)
Beispiel #11
0
    def add_mount(
        self,
        path: str,
        uid: Optional[int] = None,
        dev: Optional[int] = None,
        mode: Optional[int] = None,
        device: str = "edenfs",
        vfstype: str = "fuse",
    ) -> None:
        if uid is None:
            uid = os.getuid()
        if dev is None:
            dev = self._next_dev
        self._next_dev += 1
        if mode is None:
            mode = 16877

        self._add_mount_info(path, device=device, vfstype=vfstype)
        self.stats[path] = mtab.MTStat(st_uid=uid, st_dev=dev, st_mode=mode)
        if device == "edenfs":
            self.stats[os.path.join(path, ".eden")] = mtab.MTStat(st_uid=uid,
                                                                  st_dev=dev,
                                                                  st_mode=mode)
Beispiel #12
0
    def test_bind_mount_wrong_device_dry_run(self):
        # bm1, bm2 should not have same device as edenfs
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=True)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbcode/buck-out

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Would remount bind mount at {self.edenfs_path1}/fbandroid/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=2)
Beispiel #13
0
    def test_fails_if_unmount_fails(self):
        self.mount_table.set_eden_mounts(self.active_mounts +
                                         [b'/mnt/stale1', b'/mnt/stale2'])
        self.mount_table.stats['/mnt/stale1'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=12)
        self.mount_table.stats['/mnt/stale2'] = mtab.MTStat(st_uid=os.getuid(),
                                                            st_dev=13)
        self.mount_table.fail_unmount_lazy(b'/mnt/stale1', b'/mnt/stale2')
        self.mount_table.fail_unmount_force(b'/mnt/stale1')

        result = self.check.do_check(dry_run=False)
        self.assertEqual(doctor.CheckResultType.FAILED_TO_FIX,
                         result.result_type)
        self.assertEqual(
            dedent('''\
            Successfully unmounted 1 mount point:
              /mnt/stale2
            Failed to unmount 1 mount point:
              /mnt/stale1
        '''), result.message)
        self.assertEqual([b'/mnt/stale1', b'/mnt/stale2'],
                         self.mount_table.unmount_lazy_calls)
        self.assertEqual([b'/mnt/stale1', b'/mnt/stale2'],
                         self.mount_table.unmount_force_calls)
Beispiel #14
0
    def test_client_bind_mount_multiple_issues(self):
        # Bind mount 1 does not exist
        # Bind mount 2 has wrong device type
        # Bind mount 3 is a file instead of a directory
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Creating directory {self.fbsource_bind_mounts}/fbcode-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Expected {self.fbsource_bind_mounts}/buck-out to be a directory
Please remove the file at {self.fbsource_bind_mounts}/buck-out

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=2, num_fixed_problems=1, num_manual_fixes=1
        )
Beispiel #15
0
    def test_mount_path_not_dir(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        # Note: bm3 is not a directory
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=33188
        )

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Expected {self.edenfs_path1}/buck-out to be a directory
Please remove the file at {self.edenfs_path1}/buck-out

""",
            out,
        )
        self.assert_results(fixer, num_problems=1, num_manual_fixes=1)
Beispiel #16
0
    def test_client_bind_mounts_missing_fail(self):
        mount_table = FakeMountTable()
        fs_util = FakeFsUtil()

        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )

        fs_util.path_error[self.client_bm3] = "Failed to create directory"

        fixer, out = self.run_check(mount_table, dry_run=False, fs_util=fs_util)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out
Creating directory {self.fbsource_bind_mounts}/fbcode-buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out
Creating directory {self.fbsource_bind_mounts}/buck-out...<red>error<reset>
Failed to fix problem: Failed to create directory

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=2, num_fixed_problems=1, num_failed_fixes=1
        )
Beispiel #17
0
    def create_test_mount(
        self,
        path: str,
        snapshot: Optional[str] = None,
        bind_mounts: Optional[Dict[str, str]] = None,
        client_name: Optional[str] = None,
        scm_type: str = "hg",
        active: bool = True,
        setup_path: bool = True,
        dirstate_parent: Union[str, Tuple[str, str], None] = None,
        backing_repo: Optional[Path] = None,
    ) -> EdenCheckout:
        """
        Define a configured mount.

        If active is True and status was set to ALIVE when creating the FakeClient
        then the mount will appear as a normal active mount.  It will be reported in the
        thrift results and the mount table, and the mount directory will be populated
        with a .hg/ or .git/ subdirectory.

        The setup_path argument can be set to False to prevent creating the fake mount
        directory on disk.

        Returns the absolute path to the mount directory.
        """
        full_path = os.path.join(self._tmp_dir, path)
        if full_path in self._checkouts_by_path:
            raise Exception(f"duplicate mount definition: {full_path}")

        if snapshot is None:
            snapshot = self.default_commit_hash
        if bind_mounts is None:
            bind_mounts = {}
        if client_name is None:
            client_name = path.replace("/", "_")
        backing_repo_path = (backing_repo if backing_repo is not None else
                             self.default_backing_repo)

        state_dir = self.clients_path / client_name
        assert full_path not in self._checkouts_by_path
        config = CheckoutConfig(
            backing_repo=backing_repo_path,
            scm_type=scm_type,
            bind_mounts=bind_mounts,
            default_revision=snapshot,
            redirections={},
        )
        checkout = FakeCheckout(state_dir=state_dir,
                                config=config,
                                snapshot=snapshot)
        self._checkouts_by_path[full_path] = checkout

        # Write out the config file and snapshot file
        state_dir.mkdir()
        eden_checkout = EdenCheckout(typing.cast(EdenInstance, self),
                                     Path(full_path), state_dir)
        eden_checkout.save_config(config)
        eden_checkout.save_snapshot(snapshot)

        if active and self._status == fb303_status.ALIVE:
            # Report the mount in /proc/mounts
            dev_id = self._next_dev_id
            self._next_dev_id += 1
            self.mount_table.stats[full_path] = mtab.MTStat(
                st_uid=os.getuid(),
                st_dev=dev_id,
                st_mode=(stat.S_IFDIR | 0o755))

            # Tell the thrift client to report the mount as active
            self._fake_client._mounts.append(
                eden_ttypes.MountInfo(
                    mountPoint=os.fsencode(full_path),
                    edenClientPath=os.fsencode(state_dir),
                    state=eden_ttypes.MountState.RUNNING,
                ))

            # Set up directories on disk that look like the mounted checkout
            if setup_path:
                os.makedirs(full_path)
                if scm_type == "hg":
                    self._setup_hg_path(full_path, checkout, dirstate_parent)
                elif scm_type == "git":
                    os.mkdir(os.path.join(full_path, ".git"))

        return EdenCheckout(typing.cast(EdenInstance, self), Path(full_path),
                            Path(state_dir))
Beispiel #18
0
    def test_bind_mounts_missing_fail(self):
        mount_table = FakeMountTable()
        mount_table.stats[self.fbsource_bind_mounts] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=11, st_mode=16877
        )
        mount_table.stats[self.edenfs_path1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Client bind mount paths (under .eden)
        mount_table.stats[self.client_bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.client_bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # Bind mount paths (under eden path)
        mount_table.stats[self.bm1] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm2] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )
        mount_table.stats[self.bm3] = mtab.MTStat(
            st_uid=os.getuid(), st_dev=12, st_mode=16877
        )

        # These bound mind operations will succeed.
        mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1

        fixer, out = self.run_check(mount_table, dry_run=False)
        self.assertEqual(
            f"""\
<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbcode/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbcode/buck-out...<green>fixed<reset>

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/fbandroid/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/fbandroid/buck-out...<red>error<reset>
Failed to fix problem: Command 'sudo mount -o bind \
{self.fbsource_bind_mounts}/fbandroid-buck-out \
{self.edenfs_path1}/fbandroid/buck-out' returned non-zero exit status 1.

<yellow>- Found problem:<reset>
Bind mount at {self.edenfs_path1}/buck-out is not mounted
Remounting bind mount at {self.edenfs_path1}/buck-out...<red>error<reset>
Failed to fix problem: Command \
'sudo mount -o bind {self.fbsource_bind_mounts}/buck-out \
{self.edenfs_path1}/buck-out' returned non-zero exit status 1.

""",
            out,
        )
        self.assert_results(
            fixer, num_problems=3, num_fixed_problems=1, num_failed_fixes=2
        )
Beispiel #19
0
    def test_end_to_end_test_with_various_scenarios(self, mock_watchman):
        side_effects: List[Dict[str, Any]] = []
        calls = []
        tmp_dir = tempfile.mkdtemp(prefix='eden_test.')
        try:
            # In edenfs_path1, we will break the snapshot check.
            edenfs_path1 = os.path.join(tmp_dir, 'path1')
            # In edenfs_path2, we will break the inotify check and the Nuclide
            # subscriptions check.
            edenfs_path2 = os.path.join(tmp_dir, 'path2')

            calls.append(call(['watch-list']))
            side_effects.append({'roots': [edenfs_path1, edenfs_path2]})

            calls.append(call(['watch-project', edenfs_path1]))
            side_effects.append({'watcher': 'eden'})

            calls.append(call(['debug-get-subscriptions', edenfs_path1]))
            side_effects.append(
                _create_watchman_subscription(
                    filewatcher_subscription=f'filewatcher-{edenfs_path1}', ))

            calls.append(call(['watch-project', edenfs_path2]))
            side_effects.append({'watcher': 'inotify'})
            calls.append(call(['watch-del', edenfs_path2]))
            side_effects.append({'watch-del': True, 'root': edenfs_path2})
            calls.append(call(['watch-project', edenfs_path2]))
            side_effects.append({'watcher': 'eden'})

            calls.append(call(['debug-get-subscriptions', edenfs_path2]))
            side_effects.append(
                _create_watchman_subscription(filewatcher_subscription=None))

            mock_watchman.side_effect = side_effects

            out = io.StringIO()
            dry_run = False
            mount_paths = OrderedDict()
            edenfs_path1_snapshot_hex = 'abcd' * 10
            mount_paths[edenfs_path1] = {
                'bind-mounts': {},
                'mount': edenfs_path1,
                'scm_type': 'hg',
                'snapshot': edenfs_path1_snapshot_hex,
                'client-dir': '/I_DO_NOT_EXIST1'
            }
            mount_paths[edenfs_path2] = {
                'bind-mounts': {},
                'mount': edenfs_path2,
                'scm_type': 'git',
                'snapshot': 'dcba' * 10,
                'client-dir': '/I_DO_NOT_EXIST2'
            }
            config = FakeConfig(mount_paths, is_healthy=True)
            config.get_thrift_client()._mounts = [
                eden_ttypes.MountInfo(mountPoint=edenfs_path1),
                eden_ttypes.MountInfo(mountPoint=edenfs_path2),
            ]

            os.mkdir(edenfs_path1)
            hg_dir = os.path.join(edenfs_path1, '.hg')
            os.mkdir(hg_dir)
            dirstate = os.path.join(hg_dir, 'dirstate')
            dirstate_hash = b'\x12\x34\x56\x78' * 5
            parents = (dirstate_hash, b'\x00' * 20)
            with open(dirstate, 'wb') as f:
                eden.dirstate.write(f, parents, tuples_dict={}, copymap={})

            mount_table = FakeMountTable()
            mount_table.stats[edenfs_path1] = mtab.MTStat(st_uid=os.getuid(),
                                                          st_dev=11)
            mount_table.stats[edenfs_path2] = mtab.MTStat(st_uid=os.getuid(),
                                                          st_dev=12)
            exit_code = doctor.cure_what_ails_you(config, dry_run, out,
                                                  mount_table)
        finally:
            shutil.rmtree(tmp_dir)

        self.assertEqual(
            f'''\
Performing 3 checks for {edenfs_path1}.
p1 for {edenfs_path1} is {'12345678' * 5}, but Eden's internal
hash in its SNAPSHOT file is {edenfs_path1_snapshot_hex}.
Performing 2 checks for {edenfs_path2}.
Previous Watchman watcher for {edenfs_path2} was "inotify" but is now "eden".
Nuclide appears to be used to edit {edenfs_path2},
but a key Watchman subscription appears to be missing.
This can cause file changes to fail to show up in Nuclide.
Currently, the only workaround this is to run
"Nuclide Remote Projects: Kill And Restart" from the
command palette in Atom.
Number of fixes made: 1.
Number of issues that could not be fixed: 2.
''', out.getvalue())
        mock_watchman.assert_has_calls(calls)
        self.assertEqual(1, exit_code)