def check_in_progress_checkout(tracker: ProblemTracker, checkout: EdenCheckout) -> None: try: checkout.get_snapshot() except InProgressCheckoutError as ex: if proc_utils.new().is_edenfs_process(ex.pid): return tracker.add_problem(PreviousEdenFSCrashedDuringCheckout(checkout, ex))
def get_checkout(self) -> EdenCheckout: state_dir = ( self.running_state_dir if self.running_state_dir is not None else self.configured_state_dir ) assert state_dir is not None return EdenCheckout(self.instance, self.path, state_dir)
def get_checkouts(self) -> List[EdenCheckout]: results: List[EdenCheckout] = [] for mount_path, checkout in self._checkouts_by_path.items(): results.append( EdenCheckout( typing.cast(EdenInstance, self), Path(mount_path), Path(checkout.state_dir), )) return results
def test_list_mounts_no_backing_repos(self) -> None: self.maxDiff = None thrift_mounts = [ MountInfo( mountPoint=b"/data/users/johndoe/mercurial", edenClientPath=b"/home/johndoe/.eden/clients/mercurial", state=MountState.RUNNING, backingRepoPath=b"/home/johndoe/eden-repos/mercurial", ), MountInfo( mountPoint=b"/data/users/johndoe/git", edenClientPath=b"/home/johndoe/.eden/clients/git", state=MountState.SHUTTING_DOWN, backingRepoPath=None, ), MountInfo( mountPoint=b"/data/users/johndoe/apache", edenClientPath=b"/home/johndoe/.eden/clients/apache", state=MountState.RUNNING, backingRepoPath=b"/home/johndoe/eden-repos/apache", ), MountInfo( mountPoint=b"/data/users/johndoe/configs", edenClientPath=b"/home/johndoe/.eden/clients/configs", state=MountState.INITIALIZING, ), ] instance = EdenInstance( config_dir="/home/johndoe/.eden", etc_eden_dir="/etc/eden", home_dir="/home/johndoe", ) checkout1 = EdenCheckout( instance, Path("/data/users/johndoe/mercurial"), Path("/home/johndoe/.eden/clients/mercurial"), ) checkout1.set_config( CheckoutConfig( # note the backing repo is never expected to be different in the # daemon and client, but for the sake of testing that the # backing repo will be taken from the daemon we make them # different backing_repo=Path("/home/johndoe/eden-repos/mercurial1"), scm_type="hg", guid="123", mount_protocol="fuse", case_sensitive=False, require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, enable_tree_overlay=False, ) ) checkout2 = EdenCheckout( instance, Path("/data/users/johndoe/git"), Path("/home/johndoe/.eden/clients/git"), ) checkout2.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/git"), scm_type="git", guid="456", mount_protocol="fuse", case_sensitive=False, require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, enable_tree_overlay=False, ) ) config_checkouts = [ checkout1, checkout2, ] mounts = EdenInstance._combine_mount_info(thrift_mounts, config_checkouts) normal_out = TestOutput() main_mod.ListCmd.print_mounts(normal_out, mounts) self.assertEqual( """\ /data/users/johndoe/apache (unconfigured) /data/users/johndoe/configs (INITIALIZING) (unconfigured) /data/users/johndoe/git (SHUTTING_DOWN) /data/users/johndoe/mercurial """, normal_out.getvalue(), ) json_out = TestOutput() main_mod.ListCmd.print_mounts_json(json_out, mounts) self.assertEqual( """\ { "/data/users/johndoe/apache": { "backing_repo": "/home/johndoe/eden-repos/apache", "configured": false, "data_dir": "/home/johndoe/.eden/clients/apache", "state": "RUNNING" }, "/data/users/johndoe/configs": { "backing_repo": null, "configured": false, "data_dir": "/home/johndoe/.eden/clients/configs", "state": "INITIALIZING" }, "/data/users/johndoe/git": { "backing_repo": "/home/johndoe/eden-repos/git", "configured": true, "data_dir": "/home/johndoe/.eden/clients/git", "state": "SHUTTING_DOWN" }, "/data/users/johndoe/mercurial": { "backing_repo": "/home/johndoe/eden-repos/mercurial", "configured": true, "data_dir": "/home/johndoe/.eden/clients/mercurial", "state": "RUNNING" } } """, json_out.getvalue(), )
def test_list_mounts_no_backing_repos(self) -> None: self.maxDiff = None thrift_mounts = [ MountInfo( mountPoint=b"/data/users/johndoe/mercurial", edenClientPath=b"/home/johndoe/.eden/clients/mercurial", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/git", edenClientPath=b"/home/johndoe/.eden/clients/git", state=MountState.SHUTTING_DOWN, ), MountInfo( mountPoint=b"/data/users/johndoe/apache", edenClientPath=b"/home/johndoe/.eden/clients/apache", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/configs", edenClientPath=b"/home/johndoe/.eden/clients/configs", state=MountState.INITIALIZING, ), MountInfo( mountPoint=b"/data/users/johndoe/repos/linux", edenClientPath=b"/home/johndoe/.eden/clients/linux", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/other_repos/linux", edenClientPath=b"/home/johndoe/.eden/clients/linux2", state=MountState.RUNNING, ), ] instance = EdenInstance( config_dir="/home/johndoe/.eden", etc_eden_dir="/etc/eden", home_dir="/home/johndoe", ) checkout1 = EdenCheckout( instance, Path("/data/users/johndoe/mercurial"), Path("/home/johndoe/.eden/clients/mercurial"), ) checkout1.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/mercurial"), scm_type="hg", mount_protocol="fuse", default_revision=DEFAULT_REVISION["hg"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, ) ) checkout2 = EdenCheckout( instance, Path("/data/users/johndoe/git"), Path("/home/johndoe/.eden/clients/git"), ) checkout2.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/git"), scm_type="git", mount_protocol="fuse", default_revision=DEFAULT_REVISION["git"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, ) ) checkout3 = EdenCheckout( instance, Path("/data/users/johndoe/repos/linux"), Path("/home/johndoe/.eden/clients/linux"), ) checkout3.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/linux"), scm_type="git", mount_protocol="fuse", default_revision=DEFAULT_REVISION["git"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, ) ) checkout4 = EdenCheckout( instance, Path("/data/users/johndoe/other_repos/linux"), Path("/home/johndoe/.eden/clients/linux2"), ) checkout4.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/linux"), scm_type="git", mount_protocol="fuse", default_revision=DEFAULT_REVISION["git"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, ) ) checkout5 = EdenCheckout( instance, Path("/data/users/johndoe/www"), Path("/home/johndoe/.eden/clients/www"), ) checkout5.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/www"), scm_type="hg", mount_protocol="fuse", default_revision=DEFAULT_REVISION["hg"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, ) ) config_checkouts = [ checkout1, checkout2, checkout3, checkout4, checkout5, ] mounts = EdenInstance._combine_mount_info(thrift_mounts, config_checkouts) normal_out = TestOutput() main_mod.ListCmd.print_mounts(normal_out, mounts) self.assertEqual( """\ /data/users/johndoe/apache (unconfigured) /data/users/johndoe/configs (INITIALIZING) (unconfigured) /data/users/johndoe/git (SHUTTING_DOWN) /data/users/johndoe/mercurial /data/users/johndoe/other_repos/linux /data/users/johndoe/repos/linux /data/users/johndoe/www (not mounted) """, normal_out.getvalue(), ) json_out = TestOutput() main_mod.ListCmd.print_mounts_json(json_out, mounts) self.assertEqual( """\ { "/data/users/johndoe/apache": { "backing_repo": null, "configured": false, "data_dir": "/home/johndoe/.eden/clients/apache", "state": "RUNNING" }, "/data/users/johndoe/configs": { "backing_repo": null, "configured": false, "data_dir": "/home/johndoe/.eden/clients/configs", "state": "INITIALIZING" }, "/data/users/johndoe/git": { "backing_repo": "/home/johndoe/eden-repos/git", "configured": true, "data_dir": "/home/johndoe/.eden/clients/git", "state": "SHUTTING_DOWN" }, "/data/users/johndoe/mercurial": { "backing_repo": "/home/johndoe/eden-repos/mercurial", "configured": true, "data_dir": "/home/johndoe/.eden/clients/mercurial", "state": "RUNNING" }, "/data/users/johndoe/other_repos/linux": { "backing_repo": "/home/johndoe/eden-repos/linux", "configured": true, "data_dir": "/home/johndoe/.eden/clients/linux2", "state": "RUNNING" }, "/data/users/johndoe/repos/linux": { "backing_repo": "/home/johndoe/eden-repos/linux", "configured": true, "data_dir": "/home/johndoe/.eden/clients/linux", "state": "RUNNING" }, "/data/users/johndoe/www": { "backing_repo": "/home/johndoe/eden-repos/www", "configured": true, "data_dir": "/home/johndoe/.eden/clients/www", "state": "NOT_RUNNING" } } """, json_out.getvalue(), )
def test_list_mounts_no_state(self) -> None: self.maxDiff = None # Simulate an older edenfs daemon that does not send the "state" field thrift_mounts = [ MountInfo( mountPoint=b"/data/users/johndoe/mercurial", edenClientPath=b"/home/johndoe/.eden/clients/mercurial", ), MountInfo( mountPoint=b"/data/users/johndoe/git", edenClientPath=b"/home/johndoe/.eden/clients/git", ), MountInfo( mountPoint=b"/data/users/johndoe/configs", edenClientPath=b"/home/johndoe/.eden/clients/configs", ), ] instance = EdenInstance( config_dir="/home/johndoe/.eden", etc_eden_dir="/etc/eden", home_dir="/home/johndoe", ) checkout1 = EdenCheckout( instance, Path("/data/users/johndoe/mercurial"), Path("/home/johndoe/.eden/clients/mercurial"), ) checkout1.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/mercurial"), scm_type="hg", guid="789", mount_protocol="fuse", case_sensitive=False, require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, enable_tree_overlay=False, ) ) checkout2 = EdenCheckout( instance, Path("/data/users/johndoe/git"), Path("/home/johndoe/.eden/clients/git"), ) checkout2.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/git"), scm_type="git", guid="321", mount_protocol="fuse", case_sensitive=False, require_utf8_path=True, default_revision=DEFAULT_REVISION["git"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, enable_tree_overlay=False, ) ) checkout3 = EdenCheckout( instance, Path("/data/users/johndoe/www"), Path("/home/johndoe/.eden/clients/www"), ) checkout3.set_config( CheckoutConfig( backing_repo=Path("/home/johndoe/eden-repos/www"), scm_type="hg", guid="654", mount_protocol="fuse", case_sensitive=False, require_utf8_path=True, default_revision=DEFAULT_REVISION["hg"], redirections={}, active_prefetch_profiles=[], predictive_prefetch_profiles_active=False, predictive_prefetch_num_dirs=0, enable_tree_overlay=False, ) ) config_checkouts = [ checkout1, checkout2, checkout3, ] mounts = EdenInstance._combine_mount_info(thrift_mounts, config_checkouts) normal_out = TestOutput() main_mod.ListCmd.print_mounts(normal_out, mounts) self.assertEqual( """\ /data/users/johndoe/configs (unconfigured) /data/users/johndoe/git /data/users/johndoe/mercurial /data/users/johndoe/www (not mounted) """, normal_out.getvalue(), ) json_out = TestOutput() main_mod.ListCmd.print_mounts_json(json_out, mounts) self.assertEqual( """\ { "/data/users/johndoe/configs": { "backing_repo": null, "configured": false, "data_dir": "/home/johndoe/.eden/clients/configs", "state": "RUNNING" }, "/data/users/johndoe/git": { "backing_repo": "/home/johndoe/eden-repos/git", "configured": true, "data_dir": "/home/johndoe/.eden/clients/git", "state": "RUNNING" }, "/data/users/johndoe/mercurial": { "backing_repo": "/home/johndoe/eden-repos/mercurial", "configured": true, "data_dir": "/home/johndoe/.eden/clients/mercurial", "state": "RUNNING" }, "/data/users/johndoe/www": { "backing_repo": "/home/johndoe/eden-repos/www", "configured": true, "data_dir": "/home/johndoe/.eden/clients/www", "state": "NOT_RUNNING" } } """, json_out.getvalue(), )
def create_test_mount( self, path: str, snapshot: Optional[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 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, guid=uuid.uuid4(), mount_protocol="prjfs" if sys.platform == "win32" else "fuse", default_revision=snapshot, redirections={}, active_prefetch_profiles=[], ) 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))
def test_list_mounts(self): self.maxDiff = None thrift_mounts = [ MountInfo( mountPoint=b"/data/users/johndoe/mercurial", edenClientPath=b"/home/johndoe/.eden/clients/mercurial", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/git", edenClientPath=b"/home/johndoe/.eden/clients/git", state=MountState.SHUTTING_DOWN, ), MountInfo( mountPoint=b"/data/users/johndoe/apache", edenClientPath=b"/home/johndoe/.eden/clients/apache", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/configs", edenClientPath=b"/home/johndoe/.eden/clients/configs", state=MountState.INITIALIZING, ), MountInfo( mountPoint=b"/data/users/johndoe/repos/linux", edenClientPath=b"/home/johndoe/.eden/clients/linux", state=MountState.RUNNING, ), MountInfo( mountPoint=b"/data/users/johndoe/other_repos/linux", edenClientPath=b"/home/johndoe/.eden/clients/linux2", state=MountState.RUNNING, ), ] instance = EdenInstance( config_dir="/home/johndoe/.eden", etc_eden_dir="/etc/eden", home_dir="/home/johndoe", ) config_checkouts = [ EdenCheckout( instance, Path("/data/users/johndoe/mercurial"), Path("/home/johndoe/.eden/clients/mercurial"), ), EdenCheckout( instance, Path("/data/users/johndoe/git"), Path("/home/johndoe/.eden/clients/git"), ), EdenCheckout( instance, Path("/data/users/johndoe/repos/linux"), Path("/home/johndoe/.eden/clients/linux"), ), EdenCheckout( instance, Path("/data/users/johndoe/other_repos/linux"), Path("/home/johndoe/.eden/clients/linux2"), ), EdenCheckout( instance, Path("/data/users/johndoe/www"), Path("/home/johndoe/.eden/clients/www"), ), ] mounts = main_mod.ListCmd.combine_mount_info(thrift_mounts, config_checkouts) normal_out = TestOutput() main_mod.ListCmd.print_mounts(normal_out, mounts) self.assertEqual( """\ /data/users/johndoe/apache (unconfigured) /data/users/johndoe/configs (INITIALIZING) (unconfigured) /data/users/johndoe/git (SHUTTING_DOWN) /data/users/johndoe/mercurial /data/users/johndoe/other_repos/linux /data/users/johndoe/repos/linux /data/users/johndoe/www (not mounted) """, normal_out.getvalue(), ) json_out = TestOutput() main_mod.ListCmd.print_mounts_json(json_out, mounts) self.assertEqual( """\ { "/data/users/johndoe/apache": { "configured": false, "data_dir": "/home/johndoe/.eden/clients/apache", "state": "RUNNING" }, "/data/users/johndoe/configs": { "configured": false, "data_dir": "/home/johndoe/.eden/clients/configs", "state": "INITIALIZING" }, "/data/users/johndoe/git": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/git", "state": "SHUTTING_DOWN" }, "/data/users/johndoe/mercurial": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/mercurial", "state": "RUNNING" }, "/data/users/johndoe/other_repos/linux": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/linux2", "state": "RUNNING" }, "/data/users/johndoe/repos/linux": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/linux", "state": "RUNNING" }, "/data/users/johndoe/www": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/www", "state": "NOT_RUNNING" } } """, json_out.getvalue(), )
def test_list_mounts_old_thrift(self): self.maxDiff = None # Simulate an older edenfs daemon that does not send the "state" field thrift_mounts = [ MountInfo( mountPoint=b"/data/users/johndoe/mercurial", edenClientPath=b"/home/johndoe/.eden/clients/mercurial", ), MountInfo( mountPoint=b"/data/users/johndoe/git", edenClientPath=b"/home/johndoe/.eden/clients/git", ), MountInfo( mountPoint=b"/data/users/johndoe/configs", edenClientPath=b"/home/johndoe/.eden/clients/configs", ), ] instance = EdenInstance( config_dir="/home/johndoe/.eden", etc_eden_dir="/etc/eden", home_dir="/home/johndoe", ) config_checkouts = [ EdenCheckout( instance, Path("/data/users/johndoe/mercurial"), Path("/home/johndoe/.eden/clients/mercurial"), ), EdenCheckout( instance, Path("/data/users/johndoe/git"), Path("/home/johndoe/.eden/clients/git"), ), EdenCheckout( instance, Path("/data/users/johndoe/www"), Path("/home/johndoe/.eden/clients/www"), ), ] mounts = main_mod.ListCmd.combine_mount_info(thrift_mounts, config_checkouts) normal_out = TestOutput() main_mod.ListCmd.print_mounts(normal_out, mounts) self.assertEqual( """\ /data/users/johndoe/configs (unconfigured) /data/users/johndoe/git /data/users/johndoe/mercurial /data/users/johndoe/www (not mounted) """, normal_out.getvalue(), ) json_out = TestOutput() main_mod.ListCmd.print_mounts_json(json_out, mounts) self.assertEqual( """\ { "/data/users/johndoe/configs": { "configured": false, "data_dir": "/home/johndoe/.eden/clients/configs", "state": "RUNNING" }, "/data/users/johndoe/git": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/git", "state": "RUNNING" }, "/data/users/johndoe/mercurial": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/mercurial", "state": "RUNNING" }, "/data/users/johndoe/www": { "configured": true, "data_dir": "/home/johndoe/.eden/clients/www", "state": "NOT_RUNNING" } } """, json_out.getvalue(), )