aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/molecule
diff options
context:
space:
mode:
authorColin Wilk <colin@wilk.cx>2026-07-21 19:10:19 +0200
committerColin Wilk <colin@wilk.cx>2026-07-21 19:11:08 +0200
commit0055971afd5524f0df00fc8d2203a80f828885f7 (patch)
tree23019217d6a692aa83a5b09ede12bb3ebbc9da69 /molecule
parentd9b5c59a50ac9967dc365f2c8586869d6ef5d9bd (diff)
downloadansible-role-prometheus-target-0055971afd5524f0df00fc8d2203a80f828885f7.tar.gz
ansible-role-prometheus-target-0055971afd5524f0df00fc8d2203a80f828885f7.zip
fix: prevent lost YAML targets during parallel writes
The YAML strategy performed a separate read-modify-write operation for every managed host. Because these tasks were delegated to the same Prometheus host, parallel Ansible forks could read the same original file before any fork wrote its update. Each fork then generated YAML containing only its own target. Although the copy module atomically replaced the file, it did not make the full read-modify-write sequence atomic. The last fork to write therefore overwrote targets added by earlier forks. Collect exporter updates from all hosts through hostvars, process them in one run_once operation, and write each distinct target file once. This preserves every target without requiring users to set serial. Add parallel coverage for multiple target files and label groups, and reset the fixtures on every convergence so stale files cannot hide the race.
Diffstat (limited to 'molecule')
-rw-r--r--molecule/default/converge.yml15
-rw-r--r--molecule/default/tests/test_yaml_strategy.py17
2 files changed, 30 insertions, 2 deletions
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
index 4e379da..f882c44 100644
--- a/molecule/default/converge.yml
+++ b/molecule/default/converge.yml
@@ -185,16 +185,19 @@
pre_tasks:
- name: Create yaml parallel target
ansible.builtin.copy:
- dest: /opt/yaml_parallel.yml
+ dest: "{{ item }}"
mode: "0644"
content: |
- labels:
job: node
targets:
- existing:9100
- force: false
+ force: true
delegate_to: "{{ prometheus_target_host }}"
run_once: true
+ loop:
+ - /opt/yaml_parallel.yml
+ - /opt/yaml_parallel_second.yml
vars:
prometheus_target_host: prometheus
@@ -207,6 +210,14 @@
host: "{{ inventory_hostname }}:9100"
labels:
job: node
+ - path: /opt/yaml_parallel_second.yml
+ host: "{{ inventory_hostname }}:9200"
+ labels:
+ job: node
+ - path: /opt/yaml_parallel.yml
+ host: "{{ inventory_hostname }}:9300"
+ labels:
+ job: alternate
################################################################################
################################################################################
diff --git a/molecule/default/tests/test_yaml_strategy.py b/molecule/default/tests/test_yaml_strategy.py
index 89dbdeb..7ba834e 100644
--- a/molecule/default/tests/test_yaml_strategy.py
+++ b/molecule/default/tests/test_yaml_strategy.py
@@ -44,6 +44,22 @@ def test_yaml_parallel_writes_keep_all_hosts_in_same_group(host):
"application4:9100",
]
+ assert get_group_by_labels(groups, {"job": "alternate"})["targets"] == [
+ "application:9300",
+ "application2:9300",
+ "application3:9300",
+ "application4:9300",
+ ]
+
+ second_groups = read_yaml_file(host, "/opt/yaml_parallel_second.yml")
+ assert get_group_by_labels(second_groups, {"job": "node"})["targets"] == [
+ "existing:9100",
+ "application:9200",
+ "application2:9200",
+ "application3:9200",
+ "application4:9200",
+ ]
+
def test_yaml_user_edited_file_is_reparsed_and_keeps_expected_semantics(host):
groups = read_yaml_file(host, "/opt/yaml_user_edited.yml")
@@ -70,6 +86,7 @@ def test_yaml_output_remains_parseable_after_all_operations(host):
for path in [
"/opt/yaml_bootstrap.yml",
"/opt/yaml_parallel.yml",
+ "/opt/yaml_parallel_second.yml",
"/opt/yaml_user_edited.yml",
"/opt/yaml_move_remove_last.yml",
"/opt/yaml_branch_matrix.yml",