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
17 changes: 17 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,22 @@ 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.
"""
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")
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.6"
APT_PACKAGE_VERSION = "1.0.7"
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
53 changes: 51 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,52 @@ 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 relation to jenkins and the service is not active.
act: when update-status hook is fired.
assert: The charm correctly raise a runtime error.
"""
harness.add_relation(charm_state.AGENT_RELATION, "jenkins-k8s")
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=False))
harness.begin()

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


def test_update_status_service_waiting_for_relation(
harness: ops.testing.Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: given a charm without a relation to jenkins.
act: when update-status hook is fired.
assert: The charm correctly sets the status to blocked.
"""
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=False))
harness.begin()

harness.charm.on.update_status.emit()

assert harness.charm.unit.status.name == ops.BlockedStatus.name


def test_update_status_service_active(
harness: ops.testing.Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: given a charm with relation to jenkins and the service is active.
act: when update-status hook is fired.
assert: The charm correctly sets the status to active.
"""
harness.add_relation(charm_state.AGENT_RELATION, "jenkins-k8s")
monkeypatch.setattr(service.JenkinsAgentService, "is_active", PropertyMock(return_value=True))
harness.begin()

harness.charm.on.update_status.emit()

assert harness.charm.unit.status.name == ops.ActiveStatus.name
1 change: 0 additions & 1 deletion tests/unit/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def test_on_install(harness: ops.testing.Harness, monkeypatch: pytest.MonkeyPatc
assert apt_add_package_mock.call_count == 2
assert apt_add_package_mock.call_args_list[0][0][0] == "openjdk-17-jre"
assert apt_add_package_mock.call_args_list[1][1]["package_names"] == service.APT_PACKAGE_NAME
assert apt_add_package_mock.call_args_list[1][1]["version"] == service.APT_PACKAGE_VERSION

assert harness.charm.unit.status.name == ops.BlockedStatus.name

Expand Down
Loading