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

refactor is_active check and add update_status hook #25

Merged
merged 13 commits into from
Mar 21, 2024
Merged
2 changes: 1 addition & 1 deletion src-docs/charm.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Unit that this execution is responsible for.

---

<a href="../src/charm.py#L73"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/charm.py#L74"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `restart_agent_service`

Expand Down
27 changes: 14 additions & 13 deletions src-docs/service.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The agent pebble service module.
**Global Variables**
---------------
- **AGENT_SERVICE_NAME**
- **AGENT_PACKAGE_NAME**
- **APT_PACKAGE_NAME**
- **APT_PACKAGE_VERSION**
- **SYSTEMD_SERVICE_CONF_DIR**
- **PPA_URI**
- **PPA_DEB_SRC**
Expand All @@ -33,7 +34,7 @@ Jenkins agent service class.

Attrs: is_active: Indicate if the agent service is active and running.

<a href="../src/service.py#L54"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/service.py#L55"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `__init__`

Expand All @@ -60,7 +61,7 @@ Indicate if the jenkins agent service is active.

---

<a href="../src/service.py#L95"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/service.py#L98"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `install`

Expand All @@ -78,39 +79,39 @@ Install and set up the jenkins agent apt package.

---

<a href="../src/service.py#L123"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/service.py#L169"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `restart`
### <kbd>function</kbd> `reset`

```python
restart() → None
reset() → None
```

Start the agent service.
Stop the agent service and clear its configuration file.



**Raises:**

- <b>`ServiceRestartError`</b>: when restarting the service fails
- <b>`ServiceStopError`</b>: if systemctl stop returns a non-zero exit code.

---

<a href="../src/service.py#L166"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/service.py#L126"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `stop`
### <kbd>function</kbd> `restart`

```python
stop() → None
restart() → None
```

Stop the agent service.
Start the agent service.



**Raises:**

- <b>`ServiceStopError`</b>: if systemctl stop returns a non-zero exit code.
- <b>`ServiceRestartError`</b>: when restarting the service fails


---
Expand Down
19 changes: 19 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(self, *args: typing.Any):
self.framework.observe(self.on.start, self._on_start)
self.framework.observe(self.on.config_changed, self._on_config_changed)
self.framework.observe(self.on.upgrade_charm, self._on_upgrade_charm)
self.framework.observe(self.on.update_status, self._on_update_status)
Thanhphan1147 marked this conversation as resolved.
Show resolved Hide resolved

def _on_install(self, _: ops.InstallEvent) -> None:
"""Handle install event, setup the agent service.
Expand Down Expand Up @@ -94,6 +95,24 @@ def restart_agent_service(self) -> None:

self.model.unit.status = ops.ActiveStatus()

def _on_update_status(self, _: ops.UpdateStatusEvent) -> None:
"""Update status event hook.

Raises:
RuntimeError: when the service is not running.
"""
logger.info("Recalculating charm status")
if self.model.get_relation(AGENT_RELATION) and not self.jenkins_agent_service.is_active:
logger.error("agent related to Jenkins but service is not active")
self.restart_agent_service()
raise RuntimeError("jenkins-agent service is not running")
jdkandersson marked this conversation as resolved.
Show resolved Hide resolved

if not self.model.get_relation(AGENT_RELATION):
self.model.unit.status = ops.BlockedStatus("Waiting for relation.")
return

self.model.unit.status = ops.ActiveStatus()


if __name__ == "__main__": # pragma: no cover
main(JenkinsAgentCharm)
15 changes: 8 additions & 7 deletions src/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

logger = logging.getLogger(__name__)
AGENT_SERVICE_NAME = "jenkins-agent"
APT_PACKAGE_NAME = "jenkins-agent"
APT_PACKAGE_VERSION = "1.0.5"
APT_PACKAGE_VERSION = "1.0.6"
APT_PACKAGE_NAME = f"jenkins-agent-{APT_PACKAGE_VERSION}"
SYSTEMD_SERVICE_CONF_DIR = "/etc/systemd/system/jenkins-agent.service.d/"
PPA_URI = "https://ppa.launchpadcontent.net/canonical-is-devops/jenkins-agent-charm/ubuntu/"
PPA_DEB_SRC = "deb-https://ppa.launchpadcontent.net/canonical-is-devops/jenkins-agent-charm/ubuntu/-" # noqa: E501 pylint: disable=line-too-long
Expand Down Expand Up @@ -88,7 +88,9 @@ def _render_file(self, path: Path, content: str, mode: int) -> None:
def is_active(self) -> bool:
"""Indicate if the jenkins agent service is active."""
try:
return systemd.service_running(AGENT_SERVICE_NAME)
return os.path.exists(str(AGENT_READY_PATH)) and systemd.service_running(
Thanhphan1147 marked this conversation as resolved.
Show resolved Hide resolved
AGENT_SERVICE_NAME
)
except SystemError as exc:
logger.error("Failed to call systemctl:\n%s", exc)
return False
Expand Down Expand Up @@ -117,7 +119,7 @@ def install(self) -> None:
# Install the necessary packages
apt.update()
apt.add_package("openjdk-17-jre")
apt.add_package(package_names=APT_PACKAGE_NAME, version=APT_PACKAGE_VERSION)
apt.add_package(package_names=APT_PACKAGE_NAME)
except (apt.PackageError, apt.PackageNotFoundError, apt.GPGKeyError) as exc:
raise PackageInstallError("Error installing the agent package") from exc

Expand Down Expand Up @@ -187,7 +189,6 @@ def _startup_check(self) -> bool:
timeout = time.time() + STARTUP_CHECK_TIMEOUT
while time.time() < timeout:
time.sleep(STARTUP_CHECK_INTERVAL)
service_up = os.path.exists(str(AGENT_READY_PATH)) and self.is_active
if service_up:
if self.is_active:
break
return os.path.exists(str(AGENT_READY_PATH)) and self.is_active
return self.is_active
33 changes: 31 additions & 2 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

"""Test for charm hooks."""

from unittest.mock import MagicMock
from unittest.mock import MagicMock, PropertyMock

import ops
import ops.testing
Expand Down Expand Up @@ -41,7 +41,7 @@ def test__on_upgrade_charm(harness: ops.testing.Harness, monkeypatch: pytest.Mon
act: when _on_upgrade_charm is called.
assert: The agent falls into waiting status with the correct message.
"""
monkeypatch.setattr(service.JenkinsAgentService, "is_active", MagicMock(return_value=True))
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=True))
monkeypatch.setattr(service.JenkinsAgentService, "restart", MagicMock())
harness.begin()

Expand Down Expand Up @@ -129,3 +129,32 @@ def test_restart_agent_service_service_restart_error(
monkeypatch.setattr(charm.state, "agent_relation_credentials", get_credentials_mock)
with pytest.raises(RuntimeError, match="Error restarting the agent service"):
charm.restart_agent_service()


def test_update_status_service_not_active(
harness: ops.testing.Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: given a charm with patched relation.
act: when _on_config_changed is called.
assert: The charm correctly updates the relation databag.
"""
harness.begin()
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=False))

with pytest.raises(RuntimeError, match="jenkins-agent service is not running"):
harness.charm.on.update_status.emit()


def test_update_status_service_active(
harness: ops.testing.Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: given a charm with patched relation.
act: when _on_config_changed is called.
assert: The charm correctly updates the relation databag.
"""
harness.begin()
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=True))

harness.charm.on.update_status.emit()