Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Unit.reboot for machine charms #1041

Merged
merged 31 commits into from Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8aec383
Expose juju-reboot [--now].
tonyandrewmeyer Oct 10, 2023
9aa26d8
Fix name.
tonyandrewmeyer Oct 10, 2023
a8576c3
Improve exception docs.
tonyandrewmeyer Oct 10, 2023
74b2e1f
Merge branch 'main' into juju-reboot-929
tonyandrewmeyer Oct 10, 2023
3516f37
Remove whitespace.
tonyandrewmeyer Oct 10, 2023
4607904
Update ops/model.py
tonyandrewmeyer Oct 10, 2023
300fd84
Update ops/testing.py
tonyandrewmeyer Oct 10, 2023
cbd71d2
Rename exception & fix doc link.
tonyandrewmeyer Oct 11, 2023
9253398
Fix type confusion.
tonyandrewmeyer Oct 11, 2023
28063d5
Allow RebootNow as an exception class name.
tonyandrewmeyer Oct 11, 2023
d21dc3c
Update test/test_testing.py
tonyandrewmeyer Oct 11, 2023
41ca79f
Merge branch 'main' into juju-reboot-929
tonyandrewmeyer Oct 11, 2023
7fba4f1
Fix accidental change.
tonyandrewmeyer Oct 11, 2023
5799f9e
Adjust docs as per code review.
tonyandrewmeyer Oct 11, 2023
43acb42
Note Unit.reboot addition.
tonyandrewmeyer Oct 11, 2023
f46c128
Simplify documentation as per review comment.
tonyandrewmeyer Oct 11, 2023
09b6033
Add a property that tells you when reboot() was last called.
tonyandrewmeyer Oct 11, 2023
8f95030
Also note the new harness property.
tonyandrewmeyer Oct 11, 2023
0aaee0f
Style fix.
tonyandrewmeyer Oct 11, 2023
42232d9
Typo
tonyandrewmeyer Oct 11, 2023
f341653
Typo
tonyandrewmeyer Oct 11, 2023
20a391c
Drive-by: update with other changes.
tonyandrewmeyer Oct 11, 2023
50eec52
Add 'new in' version specifiers.
tonyandrewmeyer Oct 12, 2023
2e56068
Merge branch 'main' into juju-reboot-929
tonyandrewmeyer Oct 17, 2023
15c7517
Merge branch 'main' into juju-reboot-929
tonyandrewmeyer Oct 17, 2023
7cc5bdc
Provide a counter of the number of reboots, rather than a datetime, t…
tonyandrewmeyer Oct 17, 2023
fdddc08
Have ops guarantee that nothing after reboot(now=True) will run, rath…
tonyandrewmeyer Oct 17, 2023
cbfe4f8
Use the same exception as the non-testing code.
tonyandrewmeyer Oct 17, 2023
17e8be0
Merge branch 'main' into juju-reboot-929
tonyandrewmeyer Oct 17, 2023
2cdb33f
Update ops/testing.py
tonyandrewmeyer Oct 17, 2023
9b544d0
Merge branch 'main' into juju-reboot-929
benhoyt Oct 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.md
@@ -1,6 +1,6 @@
# 2.8.0

* Added `Unit.reboot()` and `Harness.last_rebooted``
* Added `Unit.reboot()` and `Harness.reboot_count``
* Added `RelationMeta.optional`
* The type of a `Handle`'s `key` was expanded from `str` to `str|None`
* Narrowed types of `app` and `unit` in relation events to exclude `None` where applicable
Expand Down
5 changes: 5 additions & 0 deletions ops/model.py
Expand Up @@ -26,6 +26,7 @@
import shutil
import stat
import subprocess
import sys
import tempfile
import time
import typing
Expand Down Expand Up @@ -3388,6 +3389,10 @@ def _parse_opened_port(cls, port_str: str) -> Optional[Port]:
def reboot(self, now: bool = False):
if now:
self._run("juju-reboot", "--now")
# Juju will kill the Charm process, and in testing no code after
# this point would execute. However, we want to guarantee that for
# Charmers, so we force that to be the case.
sys.exit()
else:
self._run("juju-reboot")

Expand Down
16 changes: 8 additions & 8 deletions ops/testing.py
Expand Up @@ -1733,12 +1733,9 @@ def handle_timeout(args: testing.ExecArgs) -> int:
)

@property
def last_rebooted(self) -> Optional[datetime.datetime]:
"""The time that the charm last called :meth:`ops.Unit.reboot` (with or without *now*).

Returns ``None`` if :meth:`ops.Unit.reboot` has not been called.
"""
return self._backend._last_reboot
def reboot_count(self) -> int:
"""Count of times that the charm has called :meth:`ops.Unit.reboot`."""
tonyandrewmeyer marked this conversation as resolved.
Show resolved Hide resolved
return self._backend._reboot_count


def _get_app_or_unit_name(app_or_unit: AppUnitOrName) -> str:
Expand Down Expand Up @@ -1974,7 +1971,7 @@ def __init__(self, unit_name: str, meta: charm.CharmMeta, config: '_RawConfig'):
self._secrets: List[_Secret] = []
self._opened_ports: Set[model.Port] = set()
self._networks: Dict[Tuple[Optional[str], Optional[int]], _NetworkDict] = {}
self._last_reboot = None
self._reboot_count = 0

def _validate_relation_access(self, relation_name: str, relations: List[model.Relation]):
"""Ensures that the named relation exists/has been added.
Expand Down Expand Up @@ -2536,11 +2533,14 @@ def _check_protocol_and_port(self, protocol: str, port: Optional[int]):
raise model.ModelError(f'ERROR invalid protocol "{protocol}", expected "tcp", "udp", or "icmp"\n') # NOQA: test_quote_backslashes

def reboot(self, now: bool = False):
self._last_reboot = datetime.datetime.now(datetime.timezone.utc)
self._reboot_count += 1
if not now:
return
# This should exit, reboot, and re-emit the event, but we'll need the caller
# to handle that. We raise an exception so that they can simulate the exit.
# We have a custom exception, rather than using SystemExit like the real call
# does, because we don't want to terminate someone's tests if they miss
# catching it, or if it happens unexpectedly.
raise RebootNow()


Expand Down
3 changes: 2 additions & 1 deletion test/test_model.py
Expand Up @@ -3607,7 +3607,8 @@ def test_reboot(self):
self.assertEqual(fake_script_calls(self, clear=True), [
['juju-reboot', ''],
])
self.unit.reboot(now=True)
with self.assertRaises(SystemExit):
self.unit.reboot(now=True)
self.assertEqual(fake_script_calls(self, clear=True), [
['juju-reboot', '--now'],
])
Expand Down
18 changes: 5 additions & 13 deletions test/test_testing.py
Expand Up @@ -3362,27 +3362,19 @@ def _reboot(self, event: ops.RemoveEvent):
name: test-app
''')
self.addCleanup(harness.cleanup)
self.assertIsNone(harness.last_rebooted)
self.assertEqual(harness.reboot_count, 0)
backend = harness._backend
backend.reboot()
reboot1 = harness.last_rebooted
assert reboot1 is not None
self.assertTrue(reboot1)
self.assertEqual(harness.reboot_count, 1)
with self.assertRaises(ops.testing.RebootNow):
backend.reboot(now=True)
reboot2 = harness.last_rebooted
assert reboot2 is not None
self.assertGreater(reboot2, reboot1)
self.assertEqual(harness.reboot_count, 2)
harness.begin()
with self.assertRaises(ops.testing.RebootNow):
harness.charm.on.install.emit()
reboot3 = harness.last_rebooted
assert reboot3 is not None
self.assertGreater(reboot3, reboot2)
self.assertEqual(harness.reboot_count, 3)
harness.charm.on.remove.emit()
reboot4 = harness.last_rebooted
assert reboot4 is not None
self.assertGreater(reboot4, reboot3)
self.assertEqual(harness.reboot_count, 4)


class _TestingPebbleClientMixin:
Expand Down