def test_edenfs_stopping(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance( self.make_temporary_directory(), status=fb303_status.STOPPING ) instance.create_test_mount("eden-mount") out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( """\ <yellow>- Found problem:<reset> Eden is currently shutting down. Either wait for edenfs to exit, or to forcibly kill Eden, run: eden stop --kill <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def test_edenfs_starting(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance( self.make_temporary_directory(), status=fb303_status.STARTING ) instance.create_test_mount("eden-mount") out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( """\ <yellow>- Found problem:<reset> Eden is currently still starting. Please wait for edenfs to finish starting. If Eden seems to be taking too long to start you can try restarting it with "eden restart" <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def _test_remount_checkouts( self, mock_get_roots_for_nuclide, mock_watchman, dry_run: bool, old_edenfs: bool = False, ) -> Tuple[int, str, List[Path]]: """Test that `eden doctor` remounts configured mount points that are not currently mounted. """ tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mounts = [] mount1 = instance.create_test_mount("path1") mounts.append(mount1.path) mounts.append(instance.create_test_mount("path2", active=False).path) if old_edenfs: # Mimic older versions of edenfs, and do not return mount state data. instance.get_thrift_client().change_mount_state(mount1.path, None) out = TestOutput() exit_code = doctor.cure_what_ails_you( typing.cast(EdenInstance, instance), dry_run, instance.mount_table, fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) return exit_code, out.getvalue(), mounts
def _test_remount_checkouts( self, mock_get_roots_for_nuclide, mock_watchman, dry_run: bool, old_edenfs: bool = False, ) -> Tuple[int, str, List[Path]]: """Test that `eden doctor` remounts configured mount points that are not currently mounted. """ tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mounts = [] mount1 = instance.create_test_mount("path1") mounts.append(mount1.path) mounts.append(instance.create_test_mount("path2", active=False).path) if old_edenfs: # Mimic older versions of edenfs, and do not return mount state data. instance.get_thrift_client().change_mount_state(mount1.path, None) out = TestOutput() exit_code = doctor.cure_what_ails_you( typing.cast(EdenInstance, instance), dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) return exit_code, out.getvalue(), mounts
def _test_hash_check( self, dirstate_hash_hex: str, snapshot_hex: str, dirstate_parent2_hash_hex=None, commit_checker: Optional[Callable[[bytes, str], bool]] = None, ) -> Tuple[EdenCheckout, doctor.ProblemFixer, str]: instance = FakeEdenInstance(self.make_temporary_directory()) if dirstate_parent2_hash_hex is None: checkout = instance.create_test_mount( "path1", snapshot=snapshot_hex, dirstate_parent=dirstate_hash_hex ) else: checkout = instance.create_test_mount( "path1", snapshot=snapshot_hex, dirstate_parent=(dirstate_hash_hex, dirstate_parent2_hash_hex), ) if commit_checker: client = typing.cast(FakeClient, checkout.instance.get_thrift_client()) client.commit_checker = commit_checker fixer, out = self.create_fixer(dry_run=False) check_hg.check_hg(fixer, checkout) return checkout, fixer, out.getvalue()
def test_edenfs_stopping(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance( self.make_temporary_directory(), status=fb_status.STOPPING ) instance.create_test_mount("eden-mount") out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( """\ <yellow>- Found problem:<reset> Eden is currently shutting down. Either wait for edenfs to exit, or to forcibly kill Eden, run: eden stop --kill <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def test_edenfs_starting(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance( self.make_temporary_directory(), status=fb_status.STARTING ) instance.create_test_mount("eden-mount") out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( """\ <yellow>- Found problem:<reset> Eden is currently still starting. Please wait for edenfs to finish starting. If Eden seems to be taking too long to start you can try restarting it with "eden restart" <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def test_edenfs_not_running(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance(self.make_temporary_directory(), status=fb_status.DEAD) instance.create_test_mount("eden-mount") out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, FakeMountTable(), fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( """\ <yellow>- Found problem:<reset> Eden is not running. To start Eden, run: eden start <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def _test_hash_check( self, dirstate_hash_hex: str, snapshot_hex: str, dirstate_parent2_hash_hex=None, commit_checker: Optional[Callable[[str], bool]] = None, ) -> Tuple[EdenCheckout, doctor.ProblemFixer, str]: instance = FakeEdenInstance(self.make_temporary_directory()) if dirstate_parent2_hash_hex is None: checkout = instance.create_test_mount( "path1", snapshot=snapshot_hex, dirstate_parent=dirstate_hash_hex ) else: checkout = instance.create_test_mount( "path1", snapshot=snapshot_hex, dirstate_parent=(dirstate_hash_hex, dirstate_parent2_hash_hex), ) hg_repo = checkout.instance.get_hg_repo(checkout.path) if commit_checker and hg_repo is not None: fake_hg_repo = typing.cast(FakeHgRepo, hg_repo) fake_hg_repo.commit_checker = commit_checker fixer, out = self.create_fixer(dry_run=False) check_hg.check_hg(fixer, checkout) return checkout, fixer, out.getvalue()
def test_nfs_mounted(self, mock_is_nfs_mounted): mock_is_nfs_mounted.return_value = True instance = FakeEdenInstance(self.make_temporary_directory()) checkout = instance.create_test_mount("mount_dir") dry_run = True out = TestOutput() exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) expected = f"""\ <yellow>- Found problem:<reset> Eden's state directory is on an NFS file system: {instance.state_dir} This will likely cause performance problems and/or other errors. The most common cause for this is if your ~/local symlink does not point to local disk.\ Make sure that ~/local is a symlink pointing to local disk and then restart Eden. Checking {checkout.path} <yellow>- Found problem:<reset> The Mercurial data directory for {checkout.path}/.hg/sharedpath is at \ {instance.default_backing_repo}/.hg which is on a NFS filesystem. \ Accessing files and directories in this repository will be slow. <yellow>Discovered 2 problems during --dry-run<reset> """ self.assertEqual(expected, out.getvalue()) self.assertEqual(1, exit_code)
def test_unconfigured_mounts_dont_crash(self, mock_get_roots_for_nuclide): # If Eden advertises that a mount is active, but it is not in the # configuration, then at least don't throw an exception. instance = FakeEdenInstance(self.make_temporary_directory()) edenfs_path1 = instance.create_test_mount("path1").path edenfs_path2 = instance.create_test_mount("path2").path # Remove path2 from the list of mounts in the instance instance.remove_checkout_configuration(str(edenfs_path2)) dry_run = False out = TestOutput() exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"""\ Checking {edenfs_path1} Checking {edenfs_path2} <yellow>- Found problem:<reset> Checkout {edenfs_path2} is running but not listed in Eden's configuration file. Running "eden unmount {edenfs_path2}" will unmount this checkout. <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def test_unconfigured_mounts_dont_crash(self, mock_get_roots_for_nuclide): # If Eden advertises that a mount is active, but it is not in the # configuration, then at least don't throw an exception. instance = FakeEdenInstance(self.make_temporary_directory()) edenfs_path1 = instance.create_test_mount("path1").path edenfs_path2 = instance.create_test_mount("path2").path # Remove path2 from the list of mounts in the instance instance.remove_checkout_configuration(str(edenfs_path2)) dry_run = False out = TestOutput() exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"""\ Checking {edenfs_path1} Checking {edenfs_path2} <yellow>- Found problem:<reset> Checkout {edenfs_path2} is running but not listed in Eden's configuration file. Running "eden unmount {edenfs_path2}" will unmount this checkout. <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) self.assertEqual(1, exit_code)
def test_pwd_not_set(self) -> None: tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mount = instance.create_test_mount("path1").path exit_code, out = self._test_with_pwd(instance, pwd=None) self.assertEqual( out, f"""\ Checking {mount} <green>No issues detected.<reset> """, ) self.assertEqual(0, exit_code)
def test_not_all_mounts_have_watchman_watcher(self, mock_get_roots_for_nuclide, mock_watchman): instance = FakeEdenInstance(self.make_temporary_directory()) edenfs_path = str( instance.create_test_mount("eden-mount", scm_type="git").path) edenfs_path_not_watched = str( instance.create_test_mount("eden-mount-not-watched", scm_type="git").path) 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"}) mock_watchman.side_effect = side_effects out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, mount_table=instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"Checking {edenfs_path}\n" f"Checking {edenfs_path_not_watched}\n" "<green>No issues detected.<reset>\n", out.getvalue(), ) mock_watchman.assert_has_calls(calls) self.assertEqual(0, exit_code)
def test_not_all_mounts_have_watchman_watcher( self, mock_get_roots_for_nuclide, mock_watchman ): instance = FakeEdenInstance(self.make_temporary_directory()) edenfs_path = str(instance.create_test_mount("eden-mount", scm_type="git").path) edenfs_path_not_watched = str( instance.create_test_mount("eden-mount-not-watched", scm_type="git").path ) 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"}) mock_watchman.side_effect = side_effects out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, mount_table=instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"Checking {edenfs_path}\n" f"Checking {edenfs_path_not_watched}\n" "<green>No issues detected.<reset>\n", out.getvalue(), ) mock_watchman.assert_has_calls(calls) self.assertEqual(0, exit_code)
def run_varying_nfs(self, mock_path_read_text): instance = FakeEdenInstance(self.make_temporary_directory()) v = SimpleNamespace(mount_dir="mount_dir", shared_path="shared_path", instance=instance) mock_path_read_text.return_value = v.shared_path v.client_path = str(instance.create_test_mount(v.mount_dir).path) dry_run = True out = TestOutput() v.exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) v.stdout = out.getvalue() return v
def run_varying_nfs(self, mock_path_read_text): instance = FakeEdenInstance(self.make_temporary_directory()) v = SimpleNamespace( mount_dir="mount_dir", shared_path="shared_path", instance=instance ) mock_path_read_text.return_value = v.shared_path v.client_path = str(instance.create_test_mount(v.mount_dir).path) dry_run = True out = TestOutput() v.exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) v.stdout = out.getvalue() return v
def test_pwd_out_of_date(self) -> None: tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mount = instance.create_test_mount("path1").path exit_code, out = self._test_with_pwd(instance, pwd=tmp_dir) self.assertEqual( out, f"""\ <yellow>- Found problem:<reset> Your current working directory is out-of-date. This can happen if you have (re)started Eden but your shell is still pointing to the old directory from before the Eden checkouts were mounted. Run "cd / && cd -" to update your shell's working directory. Checking {mount} <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, ) self.assertEqual(1, exit_code)
def test_watchman_fails(self, mock_watchman): tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mount = instance.create_test_mount("path1", active=False).path # Make calls to watchman fail rather than returning expected output side_effects = [{"error": "watchman failed"}] mock_watchman.side_effect = side_effects out = TestOutput() exit_code = doctor.cure_what_ails_you( typing.cast(EdenInstance, instance), dry_run=False, mount_table=instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) # "watchman watch-list" should have been called by the doctor code calls = [call(["watch-list"])] mock_watchman.assert_has_calls(calls) self.assertEqual( out.getvalue(), f"""\ Checking {mount} <yellow>- Found problem:<reset> {mount} is not currently mounted Remounting {mount}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, ) self.assertEqual(exit_code, 0)
def test_watchman_fails(self, mock_watchman): tmp_dir = self.make_temporary_directory() instance = FakeEdenInstance(tmp_dir) mount = instance.create_test_mount("path1", active=False).path # Make calls to watchman fail rather than returning expected output side_effects = [{"error": "watchman failed"}] mock_watchman.side_effect = side_effects out = TestOutput() exit_code = doctor.cure_what_ails_you( typing.cast(EdenInstance, instance), dry_run=False, mount_table=instance.mount_table, fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) # "watchman watch-list" should have been called by the doctor code calls = [call(["watch-list"])] mock_watchman.assert_has_calls(calls) self.assertEqual( out.getvalue(), f"""\ Checking {mount} <yellow>- Found problem:<reset> {mount} is not currently mounted Remounting {mount}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, ) self.assertEqual(exit_code, 0)
def test_end_to_end_test_with_various_scenarios( self, mock_get_roots_for_nuclide, mock_watchman ): side_effects: List[Dict[str, Any]] = [] calls = [] instance = FakeEdenInstance(self.make_temporary_directory()) # In edenfs_path1, we will break the snapshot check. edenfs_path1_snapshot = "abcd" * 10 edenfs_path1_dirstate_parent = "12345678" * 5 edenfs_path1 = str( instance.create_test_mount( "path1", snapshot=edenfs_path1_snapshot, dirstate_parent=edenfs_path1_dirstate_parent, ).path ) # In edenfs_path2, we will break the inotify check and the Nuclide # subscriptions check. edenfs_path2 = str( instance.create_test_mount("path2", scm_type="git", setup_path=False).path ) # In edenfs_path3, we do not create the .hg directory edenfs_path3 = str(instance.create_test_mount("path3", setup_path=False).path) os.makedirs(edenfs_path3) # Assume all paths are used as root folders in a connected Nuclide. mock_get_roots_for_nuclide.return_value = { edenfs_path1, edenfs_path2, edenfs_path3, } calls.append(call(["watch-list"])) side_effects.append({"roots": [edenfs_path1, edenfs_path2, edenfs_path3]}) 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_subscriptions=[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_subscriptions=[])) calls.append(call(["watch-project", edenfs_path3])) side_effects.append({"watcher": "eden"}) calls.append(call(["debug-get-subscriptions", edenfs_path3])) side_effects.append( _create_watchman_subscription( filewatcher_subscriptions=[f"filewatcher-{edenfs_path3}"] ) ) mock_watchman.side_effect = side_effects out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"""\ Checking {edenfs_path1} <yellow>- Found problem:<reset> Found inconsistent/missing data in {edenfs_path1}/.hg: mercurial's parent commit is {edenfs_path1_dirstate_parent}, \ but Eden's internal parent commit is {edenfs_path1_snapshot} Repairing hg directory contents for {edenfs_path1}...<green>fixed<reset> Checking {edenfs_path2} <yellow>- Found problem:<reset> Watchman is watching {edenfs_path2} with the wrong watcher type: \ "inotify" instead of "eden" Fixing watchman watch for {edenfs_path2}...<green>fixed<reset> <yellow>- Found problem:<reset> Nuclide appears to be used to edit the following directories under {edenfs_path2}: {edenfs_path2} but the following Watchman subscriptions appear to be missing: filewatcher-{edenfs_path2} This can cause file changes to fail to show up in Nuclide. Currently, the only workaround for this is to run "Nuclide Remote Projects: Kill And Restart" from the command palette in Atom. Checking {edenfs_path3} <yellow>- Found problem:<reset> Missing hg directory: {edenfs_path3}/.hg Repairing hg directory contents for {edenfs_path3}...<green>fixed<reset> <yellow>Successfully fixed 3 problems.<reset> <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) mock_watchman.assert_has_calls(calls) self.assertEqual(1, exit_code)
class BindMountsCheckTest(DoctorTestBase): maxDiff = None def setUp(self) -> None: tmp_dir = self.make_temporary_directory() self.instance = FakeEdenInstance(tmp_dir) self.fbsource_client = os.path.join(self.instance.clients_path, "fbsource") self.fbsource_bind_mounts = os.path.join(self.fbsource_client, "bind-mounts") self.edenfs_path1 = str( self.instance.create_test_mount( "path1", bind_mounts={ "fbcode-buck-out": "fbcode/buck-out", "fbandroid-buck-out": "fbandroid/buck-out", "buck-out": "buck-out", }, client_name="fbsource", setup_path=False, ).path ) # Entries for later inclusion in client bind mount table self.client_bm1 = os.path.join(self.fbsource_bind_mounts, "fbcode-buck-out") self.client_bm2 = os.path.join(self.fbsource_bind_mounts, "fbandroid-buck-out") self.client_bm3 = os.path.join(self.fbsource_bind_mounts, "buck-out") # Entries for later inclusion in bind mount table self.bm1 = os.path.join(self.edenfs_path1, "fbcode/buck-out") self.bm2 = os.path.join(self.edenfs_path1, "fbandroid/buck-out") self.bm3 = os.path.join(self.edenfs_path1, "buck-out") def run_check( self, mount_table: mtab.MountTable, dry_run: bool, fs_util: Optional[FakeFsUtil] = None, ) -> Tuple[doctor.ProblemFixer, str]: fixer, out = self.create_fixer(dry_run) if fs_util is None: fs_util = FakeFsUtil() checkout = EdenCheckout( typing.cast(EdenInstance, self.instance), Path(self.edenfs_path1), Path(self.fbsource_client), ) check_bind_mounts.check_bind_mounts( fixer, checkout, mount_table=mount_table, fs_util=fs_util ) return fixer, out.getvalue() 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) def test_bind_mounts_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=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 ) 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 <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=3) def test_bind_mounts_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=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 ) mount_table.bind_mount_success_paths[self.client_bm1] = self.bm1 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> 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> <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=3, num_fixed_problems=3) 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 ) def test_bind_mounts_and_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 ) 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}/fbcode-buck-out Would create directory {self.fbsource_bind_mounts}/fbcode-buck-out <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> 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=6) 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) 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) def test_client_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) # Note: client_bm3 is not a directory 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=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> 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=1, num_manual_fixes=1) 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) def test_client_bind_mounts_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_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 ) 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}/fbcode-buck-out Would create directory {self.fbsource_bind_mounts}/fbcode-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 """, out, ) self.assert_results(fixer, num_problems=2) def test_client_bind_mounts_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_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 ) 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> Missing client directory for bind mount {self.fbsource_bind_mounts}/buck-out Creating directory {self.fbsource_bind_mounts}/buck-out...<green>fixed<reset> """, out, ) self.assert_results(fixer, num_problems=2, num_fixed_problems=2) 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 ) 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) 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) def test_client_bind_mount_multiple_issues_dry_run(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=True) self.assertEqual( f"""\ <yellow>- Found problem:<reset> Missing client directory for bind mount {self.fbsource_bind_mounts}/fbcode-buck-out Would create directory {self.fbsource_bind_mounts}/fbcode-buck-out <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_manual_fixes=1) 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 )
def test_end_to_end_test_with_various_scenarios( self, mock_get_roots_for_nuclide, mock_watchman ): side_effects: List[Dict[str, Any]] = [] calls = [] instance = FakeEdenInstance(self.make_temporary_directory()) # In edenfs_path1, we will break the snapshot check. edenfs_path1_snapshot = "abcd" * 10 edenfs_path1_dirstate_parent = "12345678" * 5 edenfs_path1 = str( instance.create_test_mount( "path1", snapshot=edenfs_path1_snapshot, dirstate_parent=edenfs_path1_dirstate_parent, ).path ) # In edenfs_path2, we will break the inotify check and the Nuclide # subscriptions check. edenfs_path2 = str( instance.create_test_mount("path2", scm_type="git", setup_path=False).path ) # In edenfs_path3, we do not create the .hg directory edenfs_path3 = str(instance.create_test_mount("path3", setup_path=False).path) os.makedirs(edenfs_path3) # Assume all paths are used as root folders in a connected Nuclide. mock_get_roots_for_nuclide.return_value = { edenfs_path1, edenfs_path2, edenfs_path3, } calls.append(call(["watch-list"])) side_effects.append({"roots": [edenfs_path1, edenfs_path2, edenfs_path3]}) 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_subscriptions=[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_subscriptions=[])) calls.append(call(["watch-project", edenfs_path3])) side_effects.append({"watcher": "eden"}) calls.append(call(["debug-get-subscriptions", edenfs_path3])) side_effects.append( _create_watchman_subscription( filewatcher_subscriptions=[f"filewatcher-{edenfs_path3}"] ) ) mock_watchman.side_effect = side_effects out = TestOutput() dry_run = False exit_code = doctor.cure_what_ails_you( instance, dry_run, instance.mount_table, fs_util=FakeFsUtil(), process_finder=self.make_process_finder(), out=out, ) self.assertEqual( f"""\ Checking {edenfs_path1} <yellow>- Found problem:<reset> Found inconsistent/missing data in {edenfs_path1}/.hg: mercurial's parent commit is {edenfs_path1_dirstate_parent}, \ but Eden's internal parent commit is {edenfs_path1_snapshot} Repairing hg directory contents for {edenfs_path1}...<green>fixed<reset> Checking {edenfs_path2} <yellow>- Found problem:<reset> Watchman is watching {edenfs_path2} with the wrong watcher type: \ "inotify" instead of "eden" Fixing watchman watch for {edenfs_path2}...<green>fixed<reset> <yellow>- Found problem:<reset> Nuclide appears to be used to edit the following directories under {edenfs_path2}: {edenfs_path2} but the following Watchman subscriptions appear to be missing: filewatcher-{edenfs_path2} This can cause file changes to fail to show up in Nuclide. Currently, the only workaround for this is to run "Nuclide Remote Projects: Kill And Restart" from the command palette in Atom. Checking {edenfs_path3} <yellow>- Found problem:<reset> Missing hg directory: {edenfs_path3}/.hg Repairing hg directory contents for {edenfs_path3}...<green>fixed<reset> <yellow>Successfully fixed 3 problems.<reset> <yellow>1 issue requires manual attention.<reset> Ask in the Eden Users group if you need help fixing issues with Eden: https://fb.facebook.com/groups/eden.users/ """, out.getvalue(), ) mock_watchman.assert_has_calls(calls) self.assertEqual(1, exit_code)
class CorruptHgTest(DoctorTestBase): maxDiff = None def setUp(self) -> None: self.instance = FakeEdenInstance(self.make_temporary_directory()) self.checkout = self.instance.create_test_mount("test_mount", scm_type="hg") self.backing_repo = typing.cast( FakeEdenInstance, self.checkout.instance ).default_backing_repo def test_unreadable_hg_shared_path_is_a_problem(self) -> None: sharedpath_path = self.checkout.path / ".hg" / "sharedpath" sharedpath_path.unlink() sharedpath_path.symlink_to(sharedpath_path.name) out = self.cure_what_ails_you(dry_run=True) self.assertIn( "Failed to read .hg/sharedpath: " "[Errno 40] Too many levels of symbolic links", out.getvalue(), ) def test_truncated_hg_dirstate_is_a_problem(self) -> None: dirstate_path = self.checkout.path / ".hg" / "dirstate" os.truncate(dirstate_path, dirstate_path.stat().st_size - 1) out = self.cure_what_ails_you(dry_run=True) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Found inconsistent/missing data in {self.checkout.path}/.hg: error parsing .hg/dirstate: Reached EOF while reading checksum \ hash in {self.checkout.path}/.hg/dirstate. Would repair hg directory contents for {self.checkout.path} <yellow>Discovered 1 problem during --dry-run<reset> """, out.getvalue(), ) def test_missing_sharedpath_and_requires(self) -> None: sharedpath_path = self.checkout.path / ".hg" / "sharedpath" sharedpath_path.unlink() requires_path = self.checkout.path / ".hg" / "requires" requires_path.unlink() out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Found inconsistent/missing data in {self.checkout.path}/.hg: error reading .hg/requires: [Errno 2] No such file or directory: \ {str(requires_path)!r} error reading .hg/sharedpath: [Errno 2] No such file or directory: \ {str(sharedpath_path)!r} Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self.assertIn("eden\n", requires_path.read_text()) self.assertEqual(sharedpath_path.read_text(), str(self.backing_repo / ".hg")) def test_missing_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" shutil.rmtree(hg_dir) out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Missing hg directory: {self.checkout.path}/.hg Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self._verify_hg_dir() def test_empty_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" shutil.rmtree(hg_dir) hg_dir.mkdir() out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> No contents present in hg directory: {self.checkout.path}/.hg Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self._verify_hg_dir() def _verify_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" self.assertTrue((hg_dir / "dirstate").is_file()) self.assertTrue((hg_dir / "hgrc").is_file()) self.assertTrue((hg_dir / "requires").is_file()) self.assertTrue((hg_dir / "sharedpath").is_file()) self.assertTrue((hg_dir / "shared").is_file()) self.assertTrue((hg_dir / "bookmarks").is_file()) self.assertTrue((hg_dir / "branch").is_file()) self.assert_dirstate_p0(self.checkout, FakeEdenInstance.default_commit_hash) self.assertIn("[extensions]\neden =\n", (hg_dir / "hgrc").read_text()) self.assertIn("eden\n", (hg_dir / "requires").read_text()) self.assertEqual( (hg_dir / "sharedpath").read_text(), str(self.backing_repo / ".hg") ) self.assertEqual((hg_dir / "shared").read_text(), "bookmarks\n") self.assertEqual((hg_dir / "bookmarks").read_text(), "") self.assertEqual((hg_dir / "branch").read_text(), "default\n") def cure_what_ails_you(self, dry_run: bool) -> TestOutput: out = TestOutput() doctor.cure_what_ails_you( typing.cast(EdenInstance, self.instance), dry_run, self.instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) return out
class CorruptHgTest(DoctorTestBase): maxDiff = None def setUp(self) -> None: self.instance = FakeEdenInstance(self.make_temporary_directory()) self.checkout = self.instance.create_test_mount("test_mount", scm_type="hg") self.backing_repo = typing.cast( FakeEdenInstance, self.checkout.instance).default_backing_repo def test_unreadable_hg_shared_path_is_a_problem(self) -> None: sharedpath_path = self.checkout.path / ".hg" / "sharedpath" sharedpath_path.unlink() sharedpath_path.symlink_to(sharedpath_path.name) out = self.cure_what_ails_you(dry_run=True) self.assertIn( "Failed to read .hg/sharedpath: " "[Errno 40] Too many levels of symbolic links", out.getvalue(), ) def test_truncated_hg_dirstate_is_a_problem(self) -> None: dirstate_path = self.checkout.path / ".hg" / "dirstate" os.truncate(dirstate_path, dirstate_path.stat().st_size - 1) out = self.cure_what_ails_you(dry_run=True) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Found inconsistent/missing data in {self.checkout.path}/.hg: error parsing .hg/dirstate: Reached EOF while reading checksum \ hash in {self.checkout.path}/.hg/dirstate. Would repair hg directory contents for {self.checkout.path} <yellow>Discovered 1 problem during --dry-run<reset> """, out.getvalue(), ) def test_missing_sharedpath_and_requires(self) -> None: sharedpath_path = self.checkout.path / ".hg" / "sharedpath" sharedpath_path.unlink() requires_path = self.checkout.path / ".hg" / "requires" requires_path.unlink() out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Found inconsistent/missing data in {self.checkout.path}/.hg: error reading .hg/requires: [Errno 2] No such file or directory: \ {str(requires_path)!r} error reading .hg/sharedpath: [Errno 2] No such file or directory: \ {str(sharedpath_path)!r} Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self.assertIn("eden\n", requires_path.read_text()) self.assertEqual(sharedpath_path.read_text(), str(self.backing_repo / ".hg")) def test_missing_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" shutil.rmtree(hg_dir) out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> Missing hg directory: {self.checkout.path}/.hg Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self._verify_hg_dir() def test_empty_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" shutil.rmtree(hg_dir) hg_dir.mkdir() out = self.cure_what_ails_you(dry_run=False) self.assertEqual( f"""\ Checking {self.checkout.path} <yellow>- Found problem:<reset> No contents present in hg directory: {self.checkout.path}/.hg Repairing hg directory contents for {self.checkout.path}...<green>fixed<reset> <yellow>Successfully fixed 1 problem.<reset> """, out.getvalue(), ) self._verify_hg_dir() def _verify_hg_dir(self) -> None: hg_dir = self.checkout.path / ".hg" self.assertTrue((hg_dir / "dirstate").is_file()) self.assertTrue((hg_dir / "hgrc").is_file()) self.assertTrue((hg_dir / "requires").is_file()) self.assertTrue((hg_dir / "sharedpath").is_file()) self.assertTrue((hg_dir / "shared").is_file()) self.assertTrue((hg_dir / "bookmarks").is_file()) self.assertTrue((hg_dir / "branch").is_file()) self.assert_dirstate_p0(self.checkout, FakeEdenInstance.default_commit_hash) self.assertIn("[extensions]\neden =\n", (hg_dir / "hgrc").read_text()) self.assertIn("eden\n", (hg_dir / "requires").read_text()) self.assertEqual((hg_dir / "sharedpath").read_text(), str(self.backing_repo / ".hg")) self.assertEqual((hg_dir / "shared").read_text(), "bookmarks\n") self.assertEqual((hg_dir / "bookmarks").read_text(), "") self.assertEqual((hg_dir / "branch").read_text(), "default\n") def cure_what_ails_you(self, dry_run: bool) -> TestOutput: out = TestOutput() doctor.cure_what_ails_you( typing.cast(EdenInstance, self.instance), dry_run, self.instance.mount_table, fs_util=filesystem.LinuxFsUtil(), process_finder=self.make_process_finder(), out=out, ) return out