Skip to content

Commit

Permalink
Add config option for disabling reporting (#326)
Browse files Browse the repository at this point in the history
* Add config option for disabling reporting
* Fix scenario tests
* Restart if command differs

---------

Co-authored-by: Pietro Pasotti <[email protected]>
Co-authored-by: Luca Bello <[email protected]>
  • Loading branch information
3 people authored Nov 19, 2024
1 parent a6e47de commit 725f0ca
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 99 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ __pycache__/
tests/integration/*-tester/lib/
.env
cos-tool*

*.egg-info
# Terraform
*.tfstate
*.tfstate.*
Expand All @@ -18,4 +18,4 @@ cos-tool*
crash.log
.terraform/
terraform.tfvars
terraform.tfvars.json
terraform.tfvars.json
7 changes: 7 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,10 @@ options:
to this range by Grafana Agent.
type: float
default: 100.0
reporting_enabled:
description: |
Toggle reporting of usage info to grafana, such as enabled feature flags.
Ref: https://grafana.com/docs/agent/latest/static/configuration/flags/#report-information-usage
type: boolean
default: true
13 changes: 11 additions & 2 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,25 @@ def _layer(self) -> Layer:
"summary": "agent layer",
"description": "pebble config layer for Grafana Agent",
"services": {
"agent": {
self._name: {
"override": "replace",
"summary": "agent",
"command": f"/bin/agent {self._cli_args()}",
"command": self._command(),
"startup": "enabled",
},
},
},
)

def _command(self) -> str:
return f"/bin/agent {self._cli_args()}"

def is_command_changed(self) -> bool:
"""Compare the current command we'd issue with the one in the pebble layer."""
if svc := self._container.get_plan().services.get(self._name):
return svc.command != self._command()
return True

def _on_dashboards_changed(self, _event) -> None:
logger.info("updating dashboards")

Expand Down
9 changes: 8 additions & 1 deletion src/grafana_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def __init__(self, *args):

for rules in [self.loki_rules_paths, self.dashboard_paths]:
if not os.path.isdir(rules.dest):
rules.src.mkdir(parents=True, exist_ok=True)
shutil.copytree(rules.src, rules.dest, dirs_exist_ok=True)

self._remote_write = PrometheusRemoteWriteConsumer(
Expand Down Expand Up @@ -453,6 +454,10 @@ def positions_dir(self) -> str:
"""Return the positions directory."""
raise NotImplementedError("Please override the positions_dir method")

def is_command_changed(self) -> bool:
"""Return True if the command used to start the agent is different from what it would be now."""
raise NotImplementedError("Please override the command method")

def run(self, cmd: List[str]):
"""Run cmd on the workload.
Expand Down Expand Up @@ -642,7 +647,7 @@ def _update_config(self) -> None:
# File does not yet exist? Processing a deferred event?
old_config = None

if config == old_config:
if config == old_config and not self.is_command_changed():
# Nothing changed, possibly new installation. Move on.
self.status.update_config = None
return
Expand Down Expand Up @@ -767,6 +772,8 @@ def _cli_args(self) -> str:
if self.cert.enabled:
args.append("-server.http.enable-tls")
args.append("-server.grpc.enable-tls")
if not self.config["reporting_enabled"]:
args.append("-disable-reporting")
return " ".join(args)

def _generate_config(self) -> Dict[str, Any]:
Expand Down
13 changes: 4 additions & 9 deletions tests/scenario/conftest.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import shutil
from pathlib import Path

import pytest
from ops.testing import Context

CHARM_ROOT = Path(__file__).parent.parent.parent
from charm import GrafanaAgentK8sCharm


@pytest.fixture
def vroot(tmp_path) -> Path:
root = Path(str(tmp_path.absolute()))
shutil.rmtree(root)
shutil.copytree(CHARM_ROOT / "src", root / "src")
return root
def ctx():
yield Context(GrafanaAgentK8sCharm)
30 changes: 30 additions & 0 deletions tests/scenario/test_config_reporting_enabled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from ops.testing import Container, State

containers = [Container(name="agent", can_connect=True)]


def test_reporting_enabled(ctx):
# GIVEN the "reporting_enabled" config option is set to True
state = State(leader=True, config={"reporting_enabled": True}, containers=containers)

# WHEN config-changed fires
out = ctx.run(ctx.on.config_changed(), state)

# THEN the service layer does NOT include the "-disable-reporting" arg
assert (
"-disable-reporting"
not in out.get_container("agent").layers["agent"].services["agent"].command
)


def test_reporting_disabled(ctx):
# GIVEN the "reporting_enabled" config option is set to False
state = State(leader=True, config={"reporting_enabled": False}, containers=containers)
# WHEN config-changed fires
out = ctx.run(ctx.on.config_changed(), state)

# THEN the service layer INCLUDES the "-disable-reporting" arg
assert (
"-disable-reporting"
in out.get_container("agent").layers["agent"].services["agent"].command
)
15 changes: 4 additions & 11 deletions tests/scenario/test_dashboard_transfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@
import json

from cosl import GrafanaDashboard
from scenario import Container, Context, Relation, State

from charm import GrafanaAgentK8sCharm
from ops.testing import Container, Relation, State


def encode_as_dashboard(dct: dict):
return GrafanaDashboard._serialize(json.dumps(dct).encode("utf-8"))


def test_dashboard_propagation(vroot):
def test_dashboard_propagation(ctx):
# This test verifies that if the charm receives a dashboard via the requirer databag,
# it is correctly transferred to the provider databag.

Expand All @@ -29,23 +27,18 @@ def test_dashboard_propagation(vroot):
}
consumer = Relation(
"grafana-dashboards-consumer",
relation_id=1,
remote_app_data={"dashboards": json.dumps(data)},
)

provider = Relation("grafana-dashboards-provider", relation_id=2)
provider = Relation("grafana-dashboards-provider")

ctx = Context(charm_type=GrafanaAgentK8sCharm, charm_root=vroot)
state = State(
relations=[consumer, provider],
leader=True,
containers=[Container("agent", can_connect=True)],
)

with ctx.manager(
state=state,
event=consumer.changed_event,
) as mgr:
with ctx(ctx.on.relation_changed(consumer), state=state) as mgr:
dash = mgr.charm.dashboards[0]
assert dash["charm"] == expected["charm"]
assert dash["title"] == expected["title"]
Expand Down
30 changes: 8 additions & 22 deletions tests/scenario/test_setup_statuses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,28 @@
# See LICENSE file for licensing details.

from ops import BlockedStatus, UnknownStatus, pebble
from scenario import Container, Context, ExecOutput, State
from ops.testing import Container, Exec, State

import charm


def test_install(vroot):
context = Context(
charm.GrafanaAgentK8sCharm,
charm_root=vroot,
)
out = context.run("install", State())
def test_install(ctx):
out = ctx.run(ctx.on.install(), State())
assert out.unit_status == UnknownStatus()


def test_start(vroot):
context = Context(
charm.GrafanaAgentK8sCharm,
charm_root=vroot,
)
out = context.run("start", State())
def test_start(ctx):
out = ctx.run(ctx.on.start(), State())
assert out.unit_status == UnknownStatus()


def test_charm_start_with_container(vroot):
def test_charm_start_with_container(ctx):
agent = Container(
name="agent",
can_connect=True,
exec_mock={("/bin/agent", "-version"): ExecOutput(stdout="42.42")},
execs={Exec(["/bin/agent", "-version"], return_code=0, stdout="42.42")},
)

context = Context(
charm.GrafanaAgentK8sCharm,
charm_root=vroot,
)
state = State(containers=[agent])
out = context.run(agent.pebble_ready_event, state)
out = ctx.run(ctx.on.pebble_ready(agent), state)

assert out.unit_status == BlockedStatus(
"Missing incoming ('requires') relation: metrics-endpoint|logging-provider|tracing-provider|grafana-dashboards-consumer"
Expand Down
35 changes: 9 additions & 26 deletions tests/scenario/test_start_statuses.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.
import dataclasses
from pathlib import Path

from ops import pebble
from scenario import Container, Context, ExecOutput, State

from charm import GrafanaAgentK8sCharm

CHARM_ROOT = Path(__file__).parent.parent.parent
from ops.testing import Container, Exec, State, UnknownStatus


@dataclasses.dataclass
Expand All @@ -21,36 +16,24 @@ def _subp_run_mock(*a, **kw):
return _MockProc(0)


def test_install(vroot):
ctx = Context(
charm_type=GrafanaAgentK8sCharm,
charm_root=vroot,
)
out = ctx.run(state=State(), event="install")
assert out.unit_status == ("unknown", "")
def test_install(ctx):
out = ctx.run(ctx.on.install(), state=State())
assert out.unit_status == UnknownStatus()


def test_start(vroot):
ctx = Context(
charm_type=GrafanaAgentK8sCharm,
charm_root=vroot,
)
out = ctx.run(state=State(), event="start")
def test_start(ctx):
out = ctx.run(ctx.on.start(), state=State())
assert out.unit_status.name == "unknown"


def test_charm_start_with_container(vroot):
def test_charm_start_with_container(ctx):
agent = Container(
name="agent",
can_connect=True,
exec_mock={("/bin/agent", "-version"): ExecOutput(stdout="42.42")},
execs={Exec(["/bin/agent", "-version"], return_code=0, stdout="42.42")},
)

ctx = Context(
charm_type=GrafanaAgentK8sCharm,
charm_root=vroot,
)
out = ctx.run(state=State(containers=[agent]), event=agent.pebble_ready_event)
out = ctx.run(ctx.on.pebble_ready(agent), state=State(containers=[agent]))

assert out.unit_status.name == "blocked"
agent_out = out.get_container("agent")
Expand Down
Loading

0 comments on commit 725f0ca

Please sign in to comment.