Example #1
0
 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
Example #2
0
 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()
Example #3
0
def check_bind_mounts(
    tracker: ProblemTracker,
    checkout: EdenCheckout,
    mount_table: mtab.MountTable,
    fs_util: filesystem.FsUtil,
) -> None:
    """Check that bind mounts exist and have different device IDs than the top-level
    checkout mount path, to confirm that they are mounted."""
    mount_path = str(checkout.path)
    try:
        checkout_path_stat = mount_table.lstat(mount_path)
    except OSError as ex:
        tracker.add_problem(Problem(f"Failed to stat eden mount: {mount_path}: {ex}"))
        return

    client_bind_mount_dir = str(checkout.state_dir / "bind-mounts")
    bind_mounts = checkout.get_config().bind_mounts

    # Create a dictionary of client paths : mount paths
    # Client directory eg. /data/users/bob/.eden/clients/fbsource-eden/bind-mounts
    # Mount directory eg. /data/users/bob/fbsource/
    client_mount_path_dict = {}
    for client_suffix, mount_suffix in bind_mounts.items():
        path_in_client_dir = os.path.join(client_bind_mount_dir, client_suffix)
        path_in_mount_dir = os.path.join(mount_path, mount_suffix)
        client_mount_path_dict[path_in_client_dir] = path_in_mount_dir

    for path_in_client_dir, path_in_mount_dir in client_mount_path_dict.items():
        _check_bind_mount_client_path(tracker, path_in_client_dir, mount_table, fs_util)
        _check_bind_mount_path(
            tracker,
            path_in_client_dir,
            path_in_mount_dir,
            checkout_path_stat,
            mount_table,
            fs_util,
        )
Example #4
0
 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)
Example #5
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))
    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,
        )
        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 == fb_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)
        )
Example #7
0
    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(),
        )
Example #8
0
    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(),
        )