2018-07-12 05:01:03 +03:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
# Copyright (c) 2016-present, Facebook, Inc.
|
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# This source code is licensed under the BSD-style license found in the
|
|
|
|
# LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
# of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
|
|
|
|
import os
|
2018-10-24 00:02:52 +03:00
|
|
|
import pathlib
|
2018-07-12 05:01:03 +03:00
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import eden.thrift
|
|
|
|
import eden.thrift.client
|
|
|
|
import pexpect
|
|
|
|
|
|
|
|
from .lib.find_executables import FindExe
|
2018-10-24 00:02:52 +03:00
|
|
|
from .lib.service_test_case import ServiceTestCaseBase, service_test
|
2018-10-11 03:28:04 +03:00
|
|
|
from .lib.temporary_directory import TemporaryDirectoryMixin
|
2018-07-12 05:01:03 +03:00
|
|
|
|
|
|
|
|
2018-10-24 00:02:52 +03:00
|
|
|
@service_test
|
|
|
|
class RestartTest(ServiceTestCaseBase, TemporaryDirectoryMixin):
|
2018-07-12 05:01:03 +03:00
|
|
|
def setUp(self) -> None:
|
2018-10-11 03:28:04 +03:00
|
|
|
self.tmp_dir = self.make_temporary_directory()
|
2018-07-12 05:01:03 +03:00
|
|
|
|
|
|
|
def ensure_stopped() -> None:
|
|
|
|
stop_cmd = [FindExe.EDEN_CLI, "--config-dir", self.tmp_dir, "stop"]
|
|
|
|
subprocess.call(stop_cmd)
|
|
|
|
|
|
|
|
self.addCleanup(ensure_stopped)
|
|
|
|
|
2018-10-16 02:21:08 +03:00
|
|
|
def _spawn_restart(self, *args: str) -> "pexpect.spawn[bytes]":
|
2018-07-12 05:01:03 +03:00
|
|
|
restart_cmd = [
|
|
|
|
FindExe.EDEN_CLI,
|
|
|
|
"--config-dir",
|
|
|
|
self.tmp_dir,
|
|
|
|
"restart",
|
|
|
|
"--daemon-binary",
|
|
|
|
FindExe.FAKE_EDENFS,
|
|
|
|
]
|
|
|
|
restart_cmd.extend(args)
|
|
|
|
|
|
|
|
print("Retarting eden: %r" % (restart_cmd,))
|
2018-10-16 01:48:36 +03:00
|
|
|
return pexpect.spawn(
|
|
|
|
restart_cmd[0], restart_cmd[1:], logfile=sys.stdout.buffer, timeout=5
|
|
|
|
)
|
2018-07-12 05:01:03 +03:00
|
|
|
|
|
|
|
def _start_fake_edenfs(self) -> int:
|
2018-10-24 00:02:52 +03:00
|
|
|
daemon = self.spawn_fake_edenfs(eden_dir=pathlib.Path(self.tmp_dir))
|
2018-10-24 00:02:52 +03:00
|
|
|
return daemon.process_id
|
|
|
|
|
|
|
|
def test_restart_starts_edenfs_if_not_running(self) -> None:
|
|
|
|
"""
|
|
|
|
Run "eden restart". It should start it without prompting since edenfs
|
|
|
|
is not already running.
|
|
|
|
"""
|
2018-07-12 05:01:03 +03:00
|
|
|
p = self._spawn_restart()
|
|
|
|
p.expect_exact("Eden is not currently running. Starting it...")
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid ([0-9]+)\)")
|
2018-10-24 00:02:52 +03:00
|
|
|
int(p.match.group(1))
|
2018-07-12 05:01:03 +03:00
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|
|
|
|
|
|
|
|
def _get_thrift_client(self) -> eden.thrift.EdenClient:
|
|
|
|
return eden.thrift.create_thrift_client(self.tmp_dir)
|
|
|
|
|
|
|
|
def test_restart(self) -> None:
|
|
|
|
self._start_fake_edenfs()
|
|
|
|
|
|
|
|
# Run "eden restart"
|
|
|
|
# It should prompt since we are about to do a non-graceful restart.
|
|
|
|
p = self._spawn_restart()
|
|
|
|
p.expect_exact("About to perform a full restart of Eden")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: this will temporarily disrupt access to your Eden-managed "
|
|
|
|
"repositories"
|
|
|
|
)
|
|
|
|
p.expect_exact("Proceed? [y/N] ")
|
|
|
|
p.sendline("y")
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid [0-9]+\)")
|
|
|
|
p.expect_exact("Successfully restarted edenfs.")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: any programs running inside of an Eden-managed "
|
|
|
|
"directory will need to cd"
|
|
|
|
)
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|
|
|
|
|
|
|
|
def test_restart_sigkill(self) -> None:
|
|
|
|
self._start_fake_edenfs()
|
|
|
|
|
|
|
|
# Tell the fake edenfs binary to ignore attempts to stop it
|
|
|
|
with self._get_thrift_client() as client:
|
|
|
|
client.setOption("honor_stop", "false")
|
|
|
|
|
|
|
|
# Run "eden restart". It should have to kill eden with SIGKILL during the
|
|
|
|
# restart operation.
|
|
|
|
# Explicitly pass in a shorter than normal shutdown timeout just to reduce the
|
|
|
|
# amount of time required for the test.
|
|
|
|
p = self._spawn_restart("--shutdown-timeout=1")
|
|
|
|
p.expect_exact("About to perform a full restart of Eden")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: this will temporarily disrupt access to your Eden-managed "
|
|
|
|
"repositories"
|
|
|
|
)
|
|
|
|
p.expect_exact("Proceed? [y/N] ")
|
|
|
|
p.sendline("y")
|
|
|
|
p.expect(
|
|
|
|
r"sent shutdown request, but edenfs did not exit within "
|
|
|
|
r"[.0-9]+ seconds. Attempting SIGKILL."
|
|
|
|
)
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid [0-9]+\)")
|
|
|
|
p.expect_exact("Successfully restarted edenfs.")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: any programs running inside of an Eden-managed "
|
|
|
|
"directory will need to cd"
|
|
|
|
)
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|
|
|
|
|
|
|
|
def test_restart_force(self) -> None:
|
|
|
|
self._start_fake_edenfs()
|
|
|
|
|
|
|
|
# "eden restart --force" should not prompt if the user wants to proceed
|
|
|
|
p = self._spawn_restart("--force")
|
|
|
|
p.expect_exact("About to perform a full restart of Eden")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: this will temporarily disrupt access to your Eden-managed "
|
|
|
|
"repositories"
|
|
|
|
)
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid [0-9]+\)")
|
|
|
|
p.expect_exact("Successfully restarted edenfs.")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: any programs running inside of an Eden-managed "
|
|
|
|
"directory will need to cd"
|
|
|
|
)
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|
|
|
|
|
|
|
|
def test_restart_while_starting(self) -> None:
|
|
|
|
orig_pid = self._start_fake_edenfs()
|
|
|
|
|
|
|
|
# Tell the fake edenfs daemon to report its status as "starting"
|
|
|
|
with self._get_thrift_client() as client:
|
|
|
|
client.setOption("status", "starting")
|
|
|
|
|
|
|
|
# "eden restart" should not restart if edenfs is still starting
|
|
|
|
p = self._spawn_restart()
|
|
|
|
p.expect_exact(f"The current edenfs daemon (pid {orig_pid}) is still starting")
|
|
|
|
p.expect_exact("Use --force if you want to forcibly restart the current daemon")
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 1)
|
|
|
|
|
|
|
|
# "eden restart --force" should force the restart anyway
|
|
|
|
p = self._spawn_restart("--force")
|
|
|
|
p.expect_exact(f"The current edenfs daemon (pid {orig_pid}) is still starting")
|
|
|
|
p.expect_exact("Forcing a full restart...")
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid [0-9]+\)")
|
|
|
|
p.expect_exact("Successfully restarted edenfs.")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: any programs running inside of an Eden-managed "
|
|
|
|
"directory will need to cd"
|
|
|
|
)
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|
|
|
|
|
|
|
|
def test_restart_unresponsive_thrift(self) -> None:
|
|
|
|
orig_pid = self._start_fake_edenfs()
|
|
|
|
|
|
|
|
# Rename the thrift socket so that "eden restart" will not be able to
|
|
|
|
# communicate with the existing daemon.
|
|
|
|
os.rename(
|
|
|
|
os.path.join(self.tmp_dir, eden.thrift.client.SOCKET_PATH),
|
|
|
|
os.path.join(self.tmp_dir, "old.socket"),
|
|
|
|
)
|
|
|
|
|
|
|
|
# "eden restart" should not restart if it cannot confirm the current health of
|
|
|
|
# edenfs.
|
|
|
|
p = self._spawn_restart()
|
|
|
|
p.expect_exact(
|
|
|
|
f"Found an existing edenfs daemon (pid {orig_pid} that does not "
|
|
|
|
"seem to be responding to thrift calls."
|
|
|
|
)
|
|
|
|
p.expect_exact("Use --force if you want to forcibly restart the current daemon")
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 1)
|
|
|
|
|
|
|
|
# "eden restart --force" should force the restart anyway
|
|
|
|
p = self._spawn_restart("--force")
|
|
|
|
p.expect_exact(
|
|
|
|
f"Found an existing edenfs daemon (pid {orig_pid} that does not "
|
|
|
|
"seem to be responding to thrift calls."
|
|
|
|
)
|
|
|
|
p.expect_exact("Forcing a full restart...")
|
|
|
|
p.expect_exact("Starting fake edenfs daemon")
|
|
|
|
p.expect(r"Started edenfs \(pid [0-9]+\)")
|
|
|
|
p.expect_exact("Successfully restarted edenfs.")
|
|
|
|
p.expect_exact(
|
|
|
|
"Note: any programs running inside of an Eden-managed "
|
|
|
|
"directory will need to cd"
|
|
|
|
)
|
|
|
|
p.wait()
|
|
|
|
self.assertEqual(p.exitstatus, 0)
|