Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.0.0"
requires-python = ">=3.10"
dependencies = [
"cosl",
"ops ~= 2.5",
"ops ~= 3.8",
"pydantic < 2",
"pyyaml ~= 6.0",
]
Expand Down Expand Up @@ -34,6 +34,22 @@ lint = [
reformat = ["ruff"]
tics = ["coverage[toml]"]

[tool.pytest.ini_options]
# Turn warnings into errors so deprecations and resource leaks fail the unit
# suite. Each ignore below is message-anchored and narrow; see the comment on
# each for why it is allowed and where the eventual fix belongs.
filterwarnings = [
"error",
# Harness is deprecated in favour of Scenario. This is the intentional
# signal, ignored narrowly until tests/unit/test_charm.py is migrated; it
# pairs with a migration nudge in the PR.
"ignore:Harness is deprecated:PendingDeprecationWarning",
# The vendored grafana_agent/v0/cos_agent.py lib calls cosl's deprecated
# GrafanaDashboard._serialize; the fix belongs upstream in
# canonical/grafana-agent-operator (lib) / cosl.
"ignore:GrafanaDashboard._serialize is deprecated:DeprecationWarning",
]

[tool.ruff]
line-length = 99
preview = true
Expand Down
15 changes: 10 additions & 5 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import ops
import yaml
from charms.grafana_agent.v0.cos_agent import COSAgentProvider
from charms.operator_libs_linux.v2 import snap
from ops.model import ActiveStatus, BlockedStatus, ModelError, WaitingStatus

from service import SNAP_NAME, UPSTREAM_SNAP, get_installed_snap_service, snap_install_or_refresh
Expand Down Expand Up @@ -183,8 +184,7 @@ def _configure(self, _: ops.HookEvent) -> None:

self.install()

upstream_snap = get_installed_snap_service(UPSTREAM_SNAP)
if upstream_snap.present:
if self._upstream_snap_present():
return

snap_service = get_installed_snap_service(SNAP_NAME)
Expand Down Expand Up @@ -219,6 +219,13 @@ def _on_upgrade(self, _: ops.UpgradeCharmEvent) -> None:
"""Handle upgrade charm event."""
self.install()

def _upstream_snap_present(self) -> bool:
"""Return True iff the legacy `golang-openstack-exporter` snap is installed and present."""
try:
return get_installed_snap_service(UPSTREAM_SNAP).present
except snap.SnapNotFoundError:
return False

def _on_collect_unit_status(self, event: ops.CollectStatusEvent) -> None:
"""Handle collect unit status event (called after every event)."""
if config_error := self.validate_configs():
Expand All @@ -233,10 +240,8 @@ def _on_collect_unit_status(self, event: ops.CollectStatusEvent) -> None:
if not self.model.relations.get("cos-agent"):
event.add_status(BlockedStatus("Grafana Agent is not related"))

upstream_snap = get_installed_snap_service(UPSTREAM_SNAP)

# this is necessary when doing a charm upgrade coming from revision 27
if upstream_snap.present:
if self._upstream_snap_present():
event.add_status(
BlockedStatus(
"golang-openstack-exporter detected. Please see: "
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,18 @@ def test_on_collect_unit_status(self, config, relations, test_values, expected_s
self.harness.charm._on_collect_unit_status(mock_event)
mock_event.add_status.assert_any_call(expected_status)

def test_upstream_snap_present_returns_false_when_snap_missing(self, mocker):
"""_upstream_snap_present treats SnapNotFoundError as absent (not crash)."""
from charms.operator_libs_linux.v2 import snap as snap_mod

mock_get_installed_snap_service = mocker.patch("charm.get_installed_snap_service")
mock_get_installed_snap_service.side_effect = snap_mod.SnapNotFoundError(
"Snap 'golang-openstack-exporter' not found!"
)
self.harness.begin()
assert self.harness.charm._upstream_snap_present() is False
mock_get_installed_snap_service.assert_called_once_with(UPSTREAM_SNAP)

@pytest.mark.parametrize(
"protocol, expected_verify",
[
Expand Down
6 changes: 5 additions & 1 deletion tests/unit/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ def test_snap_install_or_refresh_snap_store(


@mock.patch("service.log_ssdlc_system_event")
@mock.patch("service.remove_upstream_snap")
@mock.patch("service.remove_snap_as_resource")
@mock.patch("service.workaround_bug_268")
@mock.patch("service.snap.add")
def test_snap_install_or_refresh_exception_raises(mock_snap, mock_workaround, mock_ssdlc):
def test_snap_install_or_refresh_exception_raises(
mock_snap, mock_workaround, mock_remove_resource, mock_remove_upstream, mock_ssdlc
):
"""Test that when an exception happens, it will raise an exception to the caller."""
mock_snap.side_effect = service.snap.SnapError("My Error")
with pytest.raises(service.snap.SnapError):
Expand Down
32 changes: 5 additions & 27 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading