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

Add config option for disabling reporting #326

Merged
merged 17 commits into from
Nov 19, 2024
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 @@ -148,6 +148,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 @@ -445,6 +446,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 @@ -634,7 +639,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 @@ -759,6 +764,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
PietroPasotti marked this conversation as resolved.
Show resolved Hide resolved
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
Loading