Skip to content

Commit

Permalink
Fix missing register system on update.run, re-add shortcut if channel…
Browse files Browse the repository at this point in the history
… unchanged
  • Loading branch information
dpausp committed Jul 11, 2023
1 parent 0a8ef26 commit 282869c
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 52 deletions.
34 changes: 27 additions & 7 deletions pkgs/fc/agent/fc/maintenance/activity/tests/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,26 @@ def test_update_activity_run(log, nixos_mock, activity, logger):
nixos_mock.build_system.assert_called_with(
activity.next_channel_url, log=activity.log
)
nixos_mock.register_system_profile.assert_called_with(
NEXT_SYSTEM_PATH, log=activity.log
)
nixos_mock.switch_to_system.assert_called_with(
NEXT_SYSTEM_PATH, lazy=False, log=activity.log
)
log.has("update-run-succeeded")
assert log.has("update-run-succeeded")


def test_update_activity_run_unchanged(log, nixos_mock, activity):
nixos_mock.running_system_version.return_value = activity.next_version
activity.current_system = activity.next_system

activity.run()

nixos_mock.update_system_channel.assert_called_with(
activity.next_channel_url, log=activity.log
)
nixos_mock.build_system.assert_not_called()

assert activity.returncode == 0
log.has("update-run-skip")


def test_update_activity_run_update_system_channel_fails(
Expand All @@ -291,7 +298,7 @@ def test_update_activity_run_update_system_channel_fails(
activity.run()

assert activity.returncode == 1
log.has("update-run-failed", returncode=1)
assert log.has("update-run-failed", returncode=1)


def test_update_activity_build_system_fails(log, nixos_mock, activity):
Expand All @@ -302,16 +309,29 @@ def test_update_activity_build_system_fails(log, nixos_mock, activity):
activity.run()

assert activity.returncode == 2
log.has("update-run-failed", returncode=2)
assert log.has("update-run-failed", returncode=2)


def test_update_activity_register_system_profile_fails(
log, nixos_mock, activity
):
nixos_mock.register_system_profile.side_effect = RegisterFailed(
msg="msg", stdout="stdout", stderr="stderr"
)

activity.run()

assert activity.returncode == 3
assert log.has("update-run-failed", returncode=3)


def test_update_activity_switch_to_system_fails(log, nixos_mock, activity):
nixos_mock.switch_to_system.side_effect = SwitchFailed(stdout="stdout")

activity.run()

assert activity.returncode == 3
log.has("update-run-failed", returncode=3)
assert activity.returncode == 4
assert log.has("update-run-failed", returncode=4)


def test_update_activity_from_enc(
Expand Down
25 changes: 20 additions & 5 deletions pkgs/fc/agent/fc/maintenance/activity/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,25 @@ def update_system_channel(self):
nixos.update_system_channel(self.next_channel_url, self.log)

@property
def is_identical_to_running_system(self):
def identical_to_current_channel_url(self) -> bool:
if self.current_channel_url == self.next_channel_url:
self.log.info(
"update-identical-channel",
channel=self.next_channel_url,
_replace_msg=(
"Current system already uses the wanted channel URL."
),
)
return True

return False

@property
def identical_to_current_system(self) -> bool:
if self.current_system == self.next_system:
self.log.info(
"update-identical",
current_version=self.next_version,
next_version=self.next_version,
"update-identical-system",
version=self.next_version,
system=self.next_system.removeprefix("/nix/store/"),
_replace_msg=(
"Running system {system} is already the wanted system."
Expand All @@ -193,7 +206,7 @@ def run(self):
try:
self.update_system_channel()

if self.is_identical_to_running_system:
if self.identical_to_current_system:
self.returncode = 0
return

Expand All @@ -202,6 +215,8 @@ def run(self):
self.next_channel_url, log=self.log
)
step = 3
nixos.register_system_profile(system_path, log=self.log)
step = 4
nixos.switch_to_system(system_path, lazy=False, log=self.log)
except nixos.ChannelException as e:
self.stdout = e.stdout
Expand Down
17 changes: 10 additions & 7 deletions pkgs/fc/agent/fc/maintenance/maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def request_update(log, enc, current_requests) -> Optional[Request]:
activity = UpdateActivity.from_enc(log, enc)

if activity is None:
log.debug("update-no-activity")
log.debug("request-update-no-activity")
return

other_planned_requests = [
Expand All @@ -145,7 +145,7 @@ def request_update(log, enc, current_requests) -> Optional[Request]:

if equivalent_planned_requests:
log.info(
"update-found-equivalent",
"request-update-found-equivalent",
_replace_msg=(
"Existing request {request} with same channel URL: {channel_url}"
),
Expand All @@ -154,27 +154,30 @@ def request_update(log, enc, current_requests) -> Optional[Request]:
)
return

if not other_planned_requests and activity.identical_to_current_channel_url:
# Shortcut to save time preparing an activity which will have no effect.
return

activity.prepare()

# Always request an update if other updates are planned at the moment. Adding
# this activity can cancel out an existing activity by going back to the
# current system state. This is useful when someone requests an update by error
# and wants to undo it. Let the merge algorithm figure this out.
if not other_planned_requests and activity.is_identical_to_running_system:
if not other_planned_requests and activity.identical_to_current_system:
log.info(
"update-system-unchanged",
"request-update-shortcut",
_replace_msg=(
"System produced by this update is identical to the running system."
"As there are no other update requests, skip the update and set the "
"system channel directly."
),
)
activity.update_system_channel()
return

if not activity.is_identical_to_running_system:
if not activity.identical_to_current_system:
log.info(
"update-system-changed",
"request-update-prepared",
_replace_msg=(
"Update preparation was successful. This update will apply "
"changes to the system."
Expand Down
16 changes: 9 additions & 7 deletions pkgs/fc/agent/fc/maintenance/tests/test_maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,16 @@ def fake_changed_kernel_version(path):
def test_request_update(log, logger, monkeypatch):
enc = {}
from_enc_mock = MagicMock()
from_enc_mock.return_value.is_identical_to_running_system = False
from_enc_mock.return_value.identical_to_current_channel_url = False
from_enc_mock.return_value.identical_to_current_system = False
monkeypatch.setattr(
"fc.maintenance.maintenance.UpdateActivity.from_enc", from_enc_mock
)

request = fc.maintenance.maintenance.request_update(logger, enc, [])

assert request
assert log.has("update-system-changed")
assert log.has("request-update-prepared")


def test_request_update_unchanged(log, logger, monkeypatch):
Expand All @@ -185,14 +186,15 @@ def test_request_update_unchanged(log, logger, monkeypatch):
)

assert request is None
assert log.has("update-no-activity")
assert log.has("request-update-no-activity")


def test_request_update_unchanged_system_and_no_other_requests_skips_request(
log, logger, monkeypatch
):
from_enc_mock = MagicMock()
from_enc_mock.return_value.is_identical_to_running_system = True
from_enc_mock.return_value.identical_to_current_channel_url = False
from_enc_mock.return_value.identical_to_current_system = True
monkeypatch.setattr(
"fc.maintenance.maintenance.UpdateActivity.from_enc", from_enc_mock
)
Expand All @@ -204,7 +206,7 @@ def test_request_update_unchanged_system_and_no_other_requests_skips_request(
)

assert request is None
assert log.has("update-system-unchanged")
assert log.has("request-update-shortcut")


class FakeUpdateActivity(UpdateActivity):
Expand All @@ -219,7 +221,7 @@ def test_request_update_unchanged_system_and_other_requests_produces_request(
log, logger, monkeypatch
):
from_enc_mock = MagicMock()
from_enc_mock.return_value.is_identical_to_running_system = True
from_enc_mock.return_value.identical_to_current_system = True
monkeypatch.setattr(
"fc.maintenance.maintenance.UpdateActivity.from_enc", from_enc_mock
)
Expand Down Expand Up @@ -254,7 +256,7 @@ def test_request_update_equivalent_existing_request_skip_request(

assert request is None
assert log.has(
"update-found-equivalent",
"request-update-found-equivalent",
request=existing_request.id,
channel_url="https://fake",
)
27 changes: 1 addition & 26 deletions pkgs/fc/agent/fc/util/nixos.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,9 @@ def current_fc_environment_name(log=_log):


def channel_version(channel_url, log=_log):
log.debug("channel_version", channel=channel_url)
final_channel_url = resolve_url_redirects(channel_url)
try:
nixpkgs_path = subprocess.run(
["nix-instantiate", "-I", final_channel_url, "--find-file", "."],
["nix-instantiate", "-I", channel_url, "--find-file", "."],
check=True,
capture_output=True,
text=True,
Expand Down Expand Up @@ -297,29 +295,6 @@ def update_system_channel(channel_url, log=_log):
raise ChannelUpdateFailed(stdout=stdout, stderr=stderr)


def switch_to_channel(channel_url, lazy=False, log=_log):
final_channel_url = resolve_url_redirects(channel_url)
"""
Build system with this channel and switch to it.
Replicates the behaviour of nixos-rebuild switch and adds an optional
lazy mode which only switches to the built system if it actually changed.
"""
log.info(
"channel-switch",
channel=channel_url,
resolved_channel=final_channel_url,
)
# Put a temporary result link in /run to avoid a race condition
# with the garbage collector which may remove the system we just built.
# If register fails, we still hold a GC root until the next reboot.
out_link = "/run/fc-agent-built-system"
built_system = build_system(final_channel_url, out_link)
register_system_profile(built_system)
# New system is registered, delete the temporary result link.
os.unlink(out_link)
return switch_to_system(built_system, lazy)


def find_nix_build_error(stderr: str, log=_log):
"""Returns the (hopefully) interesting part of the error message from Nix
build output or a generic message if nothing is found.
Expand Down

0 comments on commit 282869c

Please sign in to comment.