mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
teach EdenFS to auto migrate folks on Ventura
Reviewed By: xavierd Differential Revision: D40243654 fbshipit-source-id: d5c19a8e1be2db1075f1e21c96816a5902dc9ee7
This commit is contained in:
parent
479969e588
commit
6823a627b6
@ -22,7 +22,19 @@ import time
|
||||
import typing
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, IO, KeysView, List, Mapping, Optional, Set, Tuple, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
IO,
|
||||
KeysView,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
import facebook.eden.ttypes as eden_ttypes
|
||||
import toml
|
||||
@ -32,7 +44,15 @@ from facebook.eden.ttypes import MountInfo as ThriftMountInfo, MountState
|
||||
from filelock import BaseFileLock, FileLock
|
||||
|
||||
from . import configinterpolator, configutil, telemetry, util, version
|
||||
from .util import HealthStatus, print_stderr, Spinner, write_file_atomically
|
||||
from .util import (
|
||||
FUSE_MOUNT_PROTOCOL_STRING,
|
||||
HealthStatus,
|
||||
NFS_MOUNT_PROTOCOL_STRING,
|
||||
print_stderr,
|
||||
PRJFS_MOUNT_PROTOCOL_STRING,
|
||||
Spinner,
|
||||
write_file_atomically,
|
||||
)
|
||||
|
||||
try:
|
||||
from eden.thrift import client # @manual
|
||||
@ -85,7 +105,11 @@ DEFAULT_REVISION = { # supported repo name -> default bookmark
|
||||
|
||||
SUPPORTED_REPOS: KeysView[str] = DEFAULT_REVISION.keys()
|
||||
|
||||
SUPPORTED_MOUNT_PROTOCOLS = {"fuse", "nfs", "prjfs"}
|
||||
SUPPORTED_MOUNT_PROTOCOLS: Set[str] = {
|
||||
FUSE_MOUNT_PROTOCOL_STRING,
|
||||
NFS_MOUNT_PROTOCOL_STRING,
|
||||
PRJFS_MOUNT_PROTOCOL_STRING,
|
||||
}
|
||||
|
||||
# Create a readme file with this name in the mount point directory.
|
||||
# The intention is for this to contain instructions telling users what to do if their
|
||||
@ -1558,6 +1582,57 @@ class EdenCheckout:
|
||||
self.save_config(new_config)
|
||||
|
||||
|
||||
# Fuse is still not functional on Ventura, so users will need to use NFS on
|
||||
# Ventura.
|
||||
def should_migrate_mount_protocol_to_nfs(instance: EdenInstance) -> bool:
|
||||
if sys.platform != "darwin":
|
||||
return False
|
||||
|
||||
if util.is_sandcastle():
|
||||
return False
|
||||
|
||||
ventura_os_version = "22.0.0"
|
||||
|
||||
if tuple(os.uname().release.split(".")) >= tuple(ventura_os_version.split(".")):
|
||||
return instance.get_config_bool("core.migrate_existing_to_nfs", default=False)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# Checks for any non NFS mounts and migrates them to NFS.
|
||||
def _do_nfs_migration(
|
||||
instance: EdenInstance, get_migration_success_message: Callable[[str], str]
|
||||
) -> None:
|
||||
migrate_mounts = False
|
||||
for checkout in instance.get_checkouts():
|
||||
if checkout.get_config().mount_protocol != util.NFS_MOUNT_PROTOCOL_STRING:
|
||||
migrate_mounts = True
|
||||
if not migrate_mounts:
|
||||
# most the time this should be the case. we only need to migrate mounts
|
||||
# once, and then we should just be able to skip this all other times.
|
||||
return
|
||||
|
||||
print("migrating mounts to NFS ...")
|
||||
|
||||
for checkout in instance.get_checkouts():
|
||||
if checkout.get_config().mount_protocol != util.NFS_MOUNT_PROTOCOL_STRING:
|
||||
checkout.migrate_mount_protocol(util.NFS_MOUNT_PROTOCOL_STRING)
|
||||
|
||||
instance.log_sample("migrate_existing_clones_to_nfs")
|
||||
print(get_migration_success_message(util.NFS_MOUNT_PROTOCOL_STRING))
|
||||
|
||||
|
||||
def _do_manual_migration(
|
||||
instance: EdenInstance,
|
||||
migrate_to: str,
|
||||
get_migration_success_message: Callable[[str], str],
|
||||
) -> None:
|
||||
for checkout in instance.get_checkouts():
|
||||
checkout.migrate_mount_protocol(migrate_to)
|
||||
|
||||
print(get_migration_success_message(migrate_to))
|
||||
|
||||
|
||||
def detect_nested_checkout(
|
||||
path: Union[str, Path],
|
||||
instance: EdenInstance,
|
||||
|
@ -1699,6 +1699,8 @@ class StartCmd(Subcmd):
|
||||
instance, daemon_binary, args.edenfs_args
|
||||
)
|
||||
|
||||
if config_mod.should_migrate_mount_protocol_to_nfs(instance):
|
||||
config_mod._do_nfs_migration(instance, get_migration_success_message)
|
||||
return daemon.start_edenfs_service(instance, daemon_binary, args.edenfs_args)
|
||||
|
||||
def start_in_foreground(
|
||||
@ -2029,7 +2031,11 @@ re-open these files after EdenFS is restarted.
|
||||
|
||||
self._do_stop(instance, old_pid, timeout=15)
|
||||
if migrate_to is not None:
|
||||
self._do_migration(instance, migrate_to)
|
||||
config_mod._do_manual_migration(
|
||||
instance, migrate_to, get_migration_success_message
|
||||
)
|
||||
elif config_mod.should_migrate_mount_protocol_to_nfs(instance):
|
||||
config_mod._do_nfs_migration(instance, get_migration_success_message)
|
||||
return self._finish_restart(instance)
|
||||
|
||||
def _force_restart(
|
||||
@ -2068,12 +2074,6 @@ re-open these files after EdenFS is restarted.
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
self._wait_for_stop(instance, pid, timeout)
|
||||
|
||||
def _do_migration(self, instance: EdenInstance, migrate_to: str) -> None:
|
||||
for checkout in instance.get_checkouts():
|
||||
checkout.migrate_mount_protocol(migrate_to)
|
||||
|
||||
print(get_migration_success_message(migrate_to))
|
||||
|
||||
def _finish_restart(self, instance: EdenInstance) -> int:
|
||||
exit_code = daemon.start_edenfs_service(
|
||||
instance, daemon_binary=self.args.daemon_binary
|
||||
|
@ -12,14 +12,15 @@ import os
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
import toml
|
||||
import toml.decoder
|
||||
from eden.fs.cli.config import EdenInstance
|
||||
from eden.test_support.temporary_directory import TemporaryDirectoryMixin
|
||||
from eden.test_support.testcase import EdenTestCaseBase
|
||||
|
||||
from .. import config as config_mod, configutil, util
|
||||
from ..config import EdenInstance
|
||||
from ..configinterpolator import EdenConfigInterpolator
|
||||
from ..configutil import EdenConfigParser, UnexpectedType
|
||||
|
||||
@ -543,3 +544,98 @@ class EdenInstanceConstructionTest(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(instance.etc_eden_dir, Path("/etc/eden"))
|
||||
self.assertEqual(instance.home_dir, Path("/home/testuser/"))
|
||||
|
||||
|
||||
class NFSMigrationTest(EdenTestCaseBase):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self._user = "bob"
|
||||
self._state_dir = self.tmp_dir / ".eden"
|
||||
self._etc_eden_dir = self.tmp_dir / "etc/eden"
|
||||
self._config_d = self.tmp_dir / "etc/eden/config.d"
|
||||
self._home_dir = self.tmp_dir / "home" / self._user
|
||||
self._interpolate_dict = {
|
||||
"USER": self._user,
|
||||
"USER_ID": "42",
|
||||
"HOME": str(self._home_dir),
|
||||
}
|
||||
|
||||
self._state_dir.mkdir()
|
||||
self._config_d.mkdir(exist_ok=True, parents=True)
|
||||
self._home_dir.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
def setup_config_files(self, mounts: Dict[str, str]) -> None:
|
||||
config_json_list = ",\n".join(
|
||||
[f'"{self.tmp_dir}/{mount}" : "{mount}"' for mount in mounts]
|
||||
)
|
||||
config_json = f"""{{
|
||||
{config_json_list}
|
||||
}}"""
|
||||
(self._state_dir / "config.json").write_text(config_json)
|
||||
|
||||
(self._state_dir / "clients").mkdir()
|
||||
for mount, initial_mount_protocol in mounts.items():
|
||||
(self._state_dir / "clients" / mount).mkdir()
|
||||
(self._state_dir / "clients" / mount / "config.toml").write_text(
|
||||
f"""
|
||||
[repository]
|
||||
path = "{self.tmp_dir}/.eden-backing-repos/test"
|
||||
type = "hg"
|
||||
protocol = "{initial_mount_protocol}"
|
||||
"""
|
||||
)
|
||||
|
||||
def check_migrate_nfs(self, mounts: Dict[str, str]) -> None:
|
||||
self.setup_config_files(mounts)
|
||||
|
||||
cmdline = [
|
||||
b"/usr/local/libexec/eden/edenfs",
|
||||
b"--edenfs",
|
||||
b"--edenDir",
|
||||
str(self._state_dir).encode("utf-8"),
|
||||
b"--etcEdenDir",
|
||||
str(self._config_d).encode("utf-8"),
|
||||
b"--configPath",
|
||||
str(self._home_dir).encode("utf-8"),
|
||||
b"--edenfsctlPath",
|
||||
b"/usr/local/bin/edenfsctl",
|
||||
b"--takeover",
|
||||
b"",
|
||||
]
|
||||
instance = config_mod.eden_instance_from_cmdline(cmdline)
|
||||
|
||||
for mount, initial_mount_protocol in mounts.items():
|
||||
checkoutConfig = config_mod.EdenCheckout(
|
||||
instance, self.tmp_dir / mount, self._state_dir / "clients" / mount
|
||||
).get_config()
|
||||
|
||||
self.assertEqual(checkoutConfig.mount_protocol, initial_mount_protocol)
|
||||
|
||||
config_mod._do_nfs_migration(instance, lambda protocol: protocol)
|
||||
|
||||
for mount in mounts:
|
||||
checkoutConfig = config_mod.EdenCheckout(
|
||||
instance, self.tmp_dir / mount, self._state_dir / "clients" / mount
|
||||
).get_config()
|
||||
|
||||
self.assertEqual(checkoutConfig.mount_protocol, "nfs")
|
||||
|
||||
def test_none(self) -> None:
|
||||
mounts = {}
|
||||
self.check_migrate_nfs(mounts)
|
||||
|
||||
def test_simple(self) -> None:
|
||||
mounts = {"test": "fuse"}
|
||||
self.check_migrate_nfs(mounts)
|
||||
|
||||
def test_multiple(self) -> None:
|
||||
mounts = {"test1": "fuse", "test2": "fuse"}
|
||||
self.check_migrate_nfs(mounts)
|
||||
|
||||
def test_already_nfs(self) -> None:
|
||||
mounts = {"test": "nfs"}
|
||||
self.check_migrate_nfs(mounts)
|
||||
|
||||
def test_multiple_nfs_fuse(self) -> None:
|
||||
mounts = {"test1": "nfs", "test2": "fuse", "test3": "fuse", "test4": "nfs"}
|
||||
self.check_migrate_nfs(mounts)
|
||||
|
@ -39,6 +39,10 @@ if sys.platform != "win32":
|
||||
LOCK_FILE = "lock"
|
||||
PID_FILE = "pid"
|
||||
|
||||
NFS_MOUNT_PROTOCOL_STRING = "nfs"
|
||||
FUSE_MOUNT_PROTOCOL_STRING = "fuse"
|
||||
PRJFS_MOUNT_PROTOCOL_STRING = "prjfs"
|
||||
|
||||
|
||||
class EdenStartError(Exception):
|
||||
pass
|
||||
@ -711,9 +715,9 @@ def is_apple_silicon() -> bool:
|
||||
|
||||
def get_protocol(nfs: bool) -> str:
|
||||
if sys.platform == "win32":
|
||||
return "prjfs"
|
||||
return PRJFS_MOUNT_PROTOCOL_STRING
|
||||
else:
|
||||
return "nfs" if nfs else "fuse"
|
||||
return NFS_MOUNT_PROTOCOL_STRING if nfs else FUSE_MOUNT_PROTOCOL_STRING
|
||||
|
||||
|
||||
def get_tip_commit_hash(repo: Path) -> bytes:
|
||||
|
Loading…
Reference in New Issue
Block a user