Merge ~barryprice/charm-telegraf:split_prometheus_client_config into charm-telegraf:master

Proposed by Barry Price
Status: Merged
Approved by: Barry Price
Approved revision: 6296374c10538503897d0bebd576742e5fe414e2
Merged at revision: 681fc188e8d79ae8420393be2b00d62d76099f7b
Proposed branch: ~barryprice/charm-telegraf:split_prometheus_client_config
Merge into: charm-telegraf:master
Diff against target: 382 lines (+132/-73)
4 files modified
src/reactive/telegraf.py (+107/-45)
src/templates/prometheus_client.tmpl (+3/-0)
src/templates/telegraf.conf.tmpl (+0/-6)
src/tests/unit/test_telegraf.py (+22/-22)
Reviewer Review Type Date Requested Status
Haw Loeung +1 Approve
Canonical IS Reviewers Pending
BootStack Reviewers Pending
Review via email: mp+398218@code.launchpad.net

This proposal supersedes a proposal from 2021-01-27.

Commit message

Split prometheus_client output config out of the main config file. Allow a prometheus-client relation to prometheus even if prometheus_output_port is set (and use that port in the relation).

To post a comment you must log in.
Revision history for this message
Laurent Sesquès (sajoupa) wrote : Posted in a previous version of this proposal

passes make test

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
Haw Loeung (hloeung) : Posted in a previous version of this proposal
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
Haw Loeung (hloeung) wrote :

LGTM

review: Approve (+1)
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 681fc188e8d79ae8420393be2b00d62d76099f7b

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/reactive/telegraf.py b/src/reactive/telegraf.py
2index d8fd268..82cf10e 100644
3--- a/src/reactive/telegraf.py
4+++ b/src/reactive/telegraf.py
5@@ -183,7 +183,14 @@ def list_config_files():
6 current_states = get_states()
7
8 for plugin in list_supported_plugins():
9- if "plugins.{}.configured".format(plugin) in current_states.keys():
10+ # The prometheus_client plugin can be configured either from a relation or
11+ # from the juju config
12+ if ("plugins.{}.configured".format(plugin) in current_states.keys()) or (
13+ "{}.configured".format(plugin) in current_states.keys()
14+ ):
15+ # The "prometheus-client" relation sets the "prometheus_client" plugin
16+ if plugin == "prometheus-client":
17+ plugin = "prometheus_client"
18 config_path = "{}/{}.conf".format(get_configs_dir(), plugin)
19 config_files.append(config_path)
20
21@@ -527,9 +534,6 @@ def configure_telegraf(): # noqa: C901
22 )
23 context["logfile"] = ""
24
25- if get_prometheus_port():
26- context["prometheus_output_port"] = get_prometheus_port()
27- check_prometheus_port("prometheus_output", get_prometheus_port())
28 context["extra_options"] = get_extra_options()
29
30 if get_socket_listener_port():
31@@ -645,6 +649,7 @@ def install_telegraf():
32
33 @when("telegraf.installed")
34 @when("apt.installed.telegraf")
35+@when("plugins.prometheus-client.configured")
36 @when_not("telegraf.configured")
37 @when_not("telegraf.apt.configured")
38 def configure_telegraf_deb():
39@@ -653,6 +658,7 @@ def configure_telegraf_deb():
40
41 @when("telegraf.installed")
42 @when("snap.installed.telegraf")
43+@when("plugins.prometheus-client.configured")
44 @when_not("telegraf.configured")
45 @when_not("telegraf.snap.configured")
46 def configure_telegraf_snap():
47@@ -677,6 +683,7 @@ def handle_config_changes():
48 if config.changed("extra_options"):
49 for plugin in list_supported_plugins():
50 clear_flag("plugins.{}.configured".format(plugin))
51+ clear_flag("prometheus-client.relation.configured")
52 # if something else changed, let's reconfigure telegraf itself just in case
53
54 if config.changed("extra_plugins"):
55@@ -689,6 +696,12 @@ def handle_config_changes():
56 ):
57 clear_flag("telegraf.installed")
58 clear_flag("extra_plugins.configured")
59+ clear_flag("plugins.prometheus-client.configured")
60+ clear_flag("prometheus-client.relation.configured")
61+
62+ if config.changed("prometheus_output_port"):
63+ clear_flag("plugins.prometheus-client.configured")
64+ clear_flag("prometheus-client.relation.configured")
65 clear_flag("telegraf.configured")
66 clear_flag("telegraf.apt.configured")
67 clear_flag("telegraf.snap.configured")
68@@ -1207,59 +1220,108 @@ def influxdb_api_output(influxdb):
69 set_flag("telegraf.needs_reload")
70
71
72+def generate_prometheus_output_config(prometheus_output_port):
73+ # If extra_options are set for prometheus_client, let's integrate them
74+ extra_options = get_extra_options()
75+ options = extra_options["outputs"].get("prometheus_client", {})
76+ listen = options.pop("listen", None)
77+ if not listen:
78+ listen = ":{}".format(prometheus_output_port)
79+ elif int(listen.split(":", 1)[1]) != prometheus_output_port:
80+ hookenv.log(
81+ """prometheus_output_port is {}, but extra_options would set it
82+ to {}. Choosing {} from prometheus_output_port.""".format(
83+ prometheus_output_port,
84+ int(listen.split(":", 1)[1]),
85+ prometheus_output_port,
86+ ),
87+ level=hookenv.WARNING,
88+ )
89+ listen = "{}:{}".format(listen.split(":", 1)[0], prometheus_output_port)
90+
91+ return {
92+ "listen": listen,
93+ "extra_options": render_extra_options(
94+ "outputs", "prometheus_client", extra_options=extra_options
95+ ),
96+ }
97+
98+
99+def render_prometheus_client_config(port):
100+ config_path = "{}/{}.conf".format(get_configs_dir(), "prometheus_client")
101+ hookenv.log(
102+ "Updating {} plugin config file. Port is {}".format("prometheus_client", port),
103+ level=hookenv.INFO,
104+ )
105+ context = generate_prometheus_output_config(port)
106+ render(
107+ source="prometheus_client.tmpl",
108+ templates_dir=get_templates_dir(),
109+ target=config_path,
110+ context=context,
111+ )
112+
113+
114 @when("prometheus-client.available")
115-@when("telegraf.installed")
116-def prometheus_client(prometheus):
117- template = """
118-[[outputs.prometheus_client]]
119- listen = "{{ listen }}"
120-"""
121+@when_not("prometheus-client.relation.configured")
122+def configure_prometheus_client_with_relation(prometheus):
123+ hookenv.log(
124+ "Configuring prometheus_client output plugin, with prometheus-client relation",
125+ level=hookenv.DEBUG,
126+ )
127+ port = get_prometheus_port() or "9126"
128+ # We'll iterate through the prometheus-client relation counterparts,
129+ # inform them of our address so that they scrape it, and get their egress subnets
130+ # so that we can allow them
131+ remote_egress_subnets = []
132 for relation_id in hookenv.relation_ids("prometheus-client"):
133 # if juju 2.x+ then we'll attempt to get the network space address
134 try:
135- hookenv.log("Network Info")
136+ hookenv.log("Getting local network info", level=hookenv.DEBUG)
137 network_info = hookenv.network_get(
138 "prometheus-client", relation_id=relation_id
139 )
140- hookenv.log(network_info)
141+ hookenv.log(network_info, level=hookenv.DEBUG)
142 if "ingress-addresses" in network_info:
143 ip_addr = network_info.get("ingress-addresses")[0]
144 else:
145 ip_addr = hookenv.network_get_primary_address("prometheus-client")
146+ for unit in hookenv.related_units(relation_id):
147+ hookenv.log(
148+ "Getting remote egress subnet for relation {} - {}".format(
149+ unit, relation_id
150+ ),
151+ level=hookenv.DEBUG,
152+ )
153+ remote_egress_subnets.append(
154+ hookenv.relation_get("egress-subnets", unit, relation_id)
155+ )
156 except NotImplementedError:
157 # if that fails, just let prometheus.configure(...) do it's default
158 ip_addr = None
159- if get_prometheus_port():
160- hookenv.log("Prometheus configured globally, skipping plugin setup")
161- prometheus.configure(
162- get_prometheus_port(), hostname=ip_addr, private_address=ip_addr
163- )
164- set_flag("prometheus-client.configured")
165- # bail out, nothing more need to be configured here
166- return
167- port = 9126
168- extra_options = get_extra_options()
169- options = extra_options["outputs"].get("prometheus-client", {})
170- listen = options.pop("listen", None)
171- if listen is not None:
172- hookenv.log(
173- "Configuring prometheus_client plugin to listen on: '{}'".format(listen)
174- )
175- port = int(listen.split(":", 1)[1])
176- else:
177- listen = ":{}".format(port)
178- check_prometheus_port("prometheus_output", port)
179 prometheus.configure(port, hostname=ip_addr, private_address=ip_addr)
180- config_path = "{}/{}.conf".format(get_configs_dir(), "prometheus-client")
181- hookenv.log("Updating {} plugin config file".format("prometheus-client"))
182- context = {"listen": listen}
183- content = render_template(template, context) + render_extra_options(
184- "outputs", "prometheus_client", extra_options=extra_options
185- )
186- host.write_file(config_path, content.encode("utf-8"))
187+ check_prometheus_port("prometheus_output", port)
188+ render_prometheus_client_config(port)
189+ set_flag("plugins.prometheus-client.configured")
190+ set_flag("prometheus-client.relation.configured")
191+ set_flag("telegraf.needs_reload")
192+
193+
194+@when_not("prometheus-client.available")
195+@when_not("plugins.prometheus-client.configured")
196+def configure_prometheus_client():
197+ hookenv.log("Configuring prometheus_client output plugin", level=hookenv.DEBUG)
198+ if get_prometheus_port():
199+ port = get_prometheus_port()
200+ else:
201+ # No relation to prometheus, no port configured: do not configure the plugin
202 set_flag("plugins.prometheus-client.configured")
203- set_flag("telegraf.needs_reload")
204- set_flag("prometheus-client.configured")
205+ return
206+ check_prometheus_port("prometheus_output", port)
207+ render_prometheus_client_config(port)
208+ set_flag("plugins.prometheus-client.configured")
209+ set_flag("telegraf.needs_reload")
210+ clear_flag("prometheus-client.relation.configured")
211
212
213 def convert_days(time_string):
214@@ -1317,17 +1379,15 @@ def render_prometheus_rules(prometheus_rules):
215
216
217 @when_not("prometheus-client.available")
218-@when("plugins.prometheus-client.configured")
219+@when("prometheus-client.relation.configured")
220 def prometheus_client_departed():
221 hookenv.log("prometheus-client relation not available")
222- config_path = "{}/{}.conf".format(get_configs_dir(), "prometheus-client")
223+ config_path = "{}/{}.conf".format(get_configs_dir(), "prometheus_client")
224 rels = hookenv.relations_of_type("prometheus-client")
225 if not rels and os.path.exists(config_path):
226 hookenv.log("Deleting {} plugin config file".format("prometheus-client"))
227 os.unlink(config_path)
228 clear_flag("plugins.prometheus-client.configured")
229- set_flag("telegraf.needs_reload")
230- clear_flag("prometheus-client.configured")
231
232
233 @when(
234@@ -1406,6 +1466,8 @@ def grafana_dashboard_import_failed():
235
236
237 @when("telegraf.needs_reload")
238+@when("telegraf.installed")
239+@when("telegraf.configured")
240 def start_or_restart():
241 states = sorted(
242 [
243diff --git a/src/templates/prometheus_client.tmpl b/src/templates/prometheus_client.tmpl
244new file mode 100644
245index 0000000..add2099
246--- /dev/null
247+++ b/src/templates/prometheus_client.tmpl
248@@ -0,0 +1,3 @@
249+[[outputs.prometheus_client]]
250+ listen = "{{ listen }}"
251+{%- if extra_options %}{{ extra_options }}{%- endif %}
252diff --git a/src/templates/telegraf.conf.tmpl b/src/templates/telegraf.conf.tmpl
253index af59ed5..966b4a9 100644
254--- a/src/templates/telegraf.conf.tmpl
255+++ b/src/templates/telegraf.conf.tmpl
256@@ -80,12 +80,6 @@
257
258 {{ outputs }}
259
260-{% if prometheus_output_port %}
261-[[outputs.prometheus_client]]
262- listen = ":{{ prometheus_output_port }}"
263- {%- if extra_options['outputs']['prometheus_client'] %}{{ render_options('prometheus_client', 'outputs', extra_options['outputs']) }}{%- endif %}
264-{%- endif %}
265-
266 ###############################################################################
267 # INPUTS #
268 ###############################################################################
269diff --git a/src/tests/unit/test_telegraf.py b/src/tests/unit/test_telegraf.py
270index 62a5595..d1ecd6b 100644
271--- a/src/tests/unit/test_telegraf.py
272+++ b/src/tests/unit/test_telegraf.py
273@@ -809,12 +809,10 @@ def test_prometheus_global(monkeypatch, config):
274 monkeypatch.setattr(telegraf.hookenv, "open_port", lambda p: open_ports.add(p))
275 monkeypatch.setattr(telegraf.hookenv, "close_port", lambda p: open_ports.remove(p))
276 config["prometheus_output_port"] = "default"
277- telegraf.configure_telegraf()
278- expected = """
279-[[outputs.prometheus_client]]
280- listen = ":9103"
281-"""
282- config_file = base_dir().join("telegraf.conf")
283+ telegraf.configure_prometheus_client()
284+ expected = '''[[outputs.prometheus_client]]
285+ listen = ":9103"'''
286+ config_file = base_dir().join("telegraf.d", "prometheus_client.conf")
287 assert expected in config_file.read()
288
289
290@@ -833,15 +831,13 @@ outputs:
291 cpu: ["cpu0"]
292
293 """
294- telegraf.configure_telegraf()
295- expected = """
296-[[outputs.prometheus_client]]
297+ telegraf.configure_prometheus_client()
298+ expected = """[[outputs.prometheus_client]]
299 listen = ":9103"
300 namedrop = ["aerospike*"]
301 [outputs.prometheus_client.tagpass]
302- cpu = ["cpu0"]
303-"""
304- config_file = base_dir().join("telegraf.conf")
305+ cpu = ["cpu0"]"""
306+ config_file = os.path.join(base_dir(), "telegraf.d", "prometheus_client.conf")
307 assert expected in config_file.read()
308
309
310@@ -1132,11 +1128,13 @@ def test_influxdb_api_output(monkeypatch, config):
311
312
313 def test_prometheus_client_output(mocker, monkeypatch, config):
314- config["prometheus_output_port"] = "" # Not enabled globally
315+ config["prometheus_output_port"] = "" # Not configured globally
316 monkeypatch.setattr(telegraf.hookenv, "open_port", lambda p: None)
317
318 relation_ids = ["prometheus-client:0"]
319+ related_units = ["prometheus/0"]
320 monkeypatch.setattr(telegraf.hookenv, "relation_ids", lambda r: relation_ids)
321+ monkeypatch.setattr(telegraf.hookenv, "related_units", lambda r: related_units)
322
323 network_get_primary_address = mocker.patch.object(
324 telegraf.hookenv, "network_get_primary_address", return_value="foo"
325@@ -1145,32 +1143,33 @@ def test_prometheus_client_output(mocker, monkeypatch, config):
326 mocker.patch.object(
327 telegraf.hookenv, "network_get", return_value={"ingress_addresses": ["1.2.3.4"]}
328 )
329+ mocker.patch.object(telegraf.hookenv, "relation_get", return_value="5.6.7.8/32")
330
331 interface = mocker.Mock(spec=RelationBase)
332 interface.configure = mocker.Mock()
333- telegraf.prometheus_client(interface)
334+ telegraf.configure_prometheus_client_with_relation(interface)
335 expected = """
336 [[outputs.prometheus_client]]
337 listen = ":9126"
338 """
339 assert (
340- configs_dir().join("prometheus-client.conf").read().strip() == expected.strip()
341+ configs_dir().join("prometheus_client.conf").read().strip() == expected.strip()
342 )
343 network_get_primary_address.assert_called_once_with("prometheus-client")
344 interface.configure.assert_called_once_with(
345- 9126, hostname="foo", private_address="foo"
346+ "9126", hostname="foo", private_address="foo"
347 )
348
349
350 def test_prometheus_client_output_departed(mocker, monkeypatch, config):
351- configs_dir().join("prometheus-client.conf").write("empty")
352+ configs_dir().join("prometheus_client.conf").write("empty")
353 relations = [1]
354 monkeypatch.setattr(telegraf.hookenv, "relations_of_type", lambda n: relations)
355 telegraf.prometheus_client_departed()
356- assert configs_dir().join("prometheus-client.conf").exists()
357+ assert configs_dir().join("prometheus_client.conf").exists()
358 relations.pop()
359 telegraf.prometheus_client_departed()
360- assert not configs_dir().join("prometheus-client.conf").exists()
361+ assert not configs_dir().join("prometheus_client.conf").exists()
362
363
364 # Integration tests (kind of)
365@@ -1264,15 +1263,16 @@ def test_restart_on_output_plugin_relation_departed(mocker, monkeypatch, config)
366 )
367 config["prometheus_output_port"] = ""
368 bus.discover()
369+ set_flag("telegraf.installed")
370 set_flag("snap.telegraf.installed")
371 set_flag("telegraf.configured")
372 interface = mocker.Mock(spec=RelationBase)
373 interface.configure = mocker.Mock()
374- telegraf.prometheus_client(interface)
375- assert configs_dir().join("prometheus-client.conf").exists()
376+ telegraf.configure_prometheus_client_with_relation(interface)
377+ assert configs_dir().join("prometheus_client.conf").exists()
378 # dispatch, file should be gone and telegraf restarted.
379 bus.dispatch()
380- assert not configs_dir().join("prometheus-client.conf").exists()
381+ assert not configs_dir().join("prometheus_client.conf").exists()
382 service_restart.assert_called_with(telegraf.DEB_SERVICE)
383
384

Subscribers

People subscribed via source and target branches

to all changes: