diff options
Diffstat (limited to 'molecule')
| -rw-r--r-- | molecule/default/converge.yml | 506 | ||||
| -rw-r--r-- | molecule/default/molecule.yml | 16 | ||||
| -rw-r--r-- | molecule/default/tests/test_check_prometheus_targets.py | 90 | ||||
| -rw-r--r-- | molecule/default/tests/test_lineinfile_strategy.py | 94 | ||||
| -rw-r--r-- | molecule/default/tests/test_yaml_strategy.py | 139 |
5 files changed, 667 insertions, 178 deletions
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 98d7e8a..4e379da 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -1,156 +1,486 @@ --- ################################################################################ ################################################################################ -- name: Simple deploy +- name: Lineinfile bootstrap and subsequent hosts hosts: application pre_tasks: - - name: Create targets - ansible.builtin.file: - path: "{{ item }}" - state: touch - modification_time: preserve - access_time: preserve - mode: "0644" + - name: Create test user + ansible.builtin.user: + name: prometheus + become: true + delegate_to: "{{ prometheus_target_host }}" + + - name: Create lineinfile bootstrap target + ansible.builtin.copy: + dest: /opt/lineinfile_bootstrap.yml + owner: prometheus + group: prometheus + mode: "0600" + content: | + - labels: + my: label + targets: + - existing:9100 + force: false become: true delegate_to: "{{ prometheus_target_host }}" - loop: - - /opt/simple_target1.yml - - /opt/simple_target2.yml - - /opt/simple_target3.yml - - /opt/simple_target4.yml vars: prometheus_target_host: prometheus - prometheus_target_exporter_defaults: - node_exporter: - path: /opt/simple_target1.yml - host: "{{ inventory_hostname }}" - blackbox_exporter: - path: /opt/simple_target2.yml - host: "{{ inventory_hostname }}_AA" + prometheus_target_strategy_lineinfile_prefix: " - " + prometheus_target_strategy_lineinfile_suffix: :9100 roles: - role: kliwniloc.prometheus_target prometheus_target_exporter: - - id: node_exporter - - { id: blackbox_exporter, host: test1 } - - { id: blackbox_exporter, host: test2 } - - { host: exporter_without_id, path: /opt/simple_target4.yml } - - role: kliwniloc.prometheus_target - prometheus_target_exporter: - - id: blackbox_exporter - path: /opt/simple_target3.yml + - path: /opt/lineinfile_bootstrap.yml + host: "{{ inventory_hostname }}" ################################################################################ ################################################################################ -- name: Prefix deploy +- name: Lineinfile first host on empty file hosts: application pre_tasks: - - name: Create targets - ansible.builtin.file: - path: "{{ item.path }}" - state: "{{ item.state }}" - modification_time: preserve - access_time: preserve - mode: "0644" + - name: Create empty lineinfile target + ansible.builtin.copy: + dest: /opt/lineinfile_first_host.yml + owner: prometheus + group: prometheus + mode: "0600" + content: "" + force: false become: true delegate_to: "{{ prometheus_target_host }}" - loop: - - { state: touch, path: /opt/prefix_target1.yml } - - { state: touch, path: /opt/prefix_target2.yml } - - { state: directory, path: /opt/prefix} - - { state: touch, path: /opt/prefix/prefix_target3.yml } vars: prometheus_target_host: prometheus - prometheus_target_exporter_target_prefix: /opt/ - prometheus_target_exporter_defaults: - node_exporter: - path: prefix_target1.yml - host: "{{ inventory_hostname }}" - blackbox_exporter: - path: prefix_target_not_exist.yml - host: "{{ inventory_hostname }}" - path_prefix: /opt/prefix/ + prometheus_target_strategy_lineinfile_prefix: "- " + prometheus_target_strategy_lineinfile_suffix: :9100 roles: - role: kliwniloc.prometheus_target prometheus_target_exporter: - - id: node_exporter - - { id: node_exporter, path: /opt/prefix_target2.yml, path_prefix: "" } - - { id: blackbox_exporter, path: prefix_target3.yml } + - path: /opt/lineinfile_first_host.yml + host: "{{ inventory_hostname }}" ################################################################################ ################################################################################ -- name: Deploy with hooks +- name: Lineinfile subsequent hosts and parallel writes hosts: application_group pre_tasks: - - name: Create targets - ansible.builtin.file: - path: "{{ item }}" - state: touch - modification_time: preserve - access_time: preserve - mode: "0644" + - name: Create lineinfile parallel target + ansible.builtin.copy: + dest: /opt/lineinfile_parallel.yml + owner: prometheus + group: prometheus + mode: "0600" + content: "" + force: false become: true delegate_to: "{{ prometheus_target_host }}" - loop: - - /opt/hook_target.yml - - /opt/hook2 + run_once: true vars: prometheus_target_host: prometheus - prometheus_target_handler_command_enabled: true - prometheus_target_handler_command: - cmd: mkdir /opt/hook1 # Fails if run twice - prometheus_target_handler_command_run_once: true - prometheus_target_handler_shell_enabled: true - prometheus_target_handler_shell: - cmd: echo hello >> /opt/hook2 + prometheus_target_strategy_lineinfile_prefix: "- " + prometheus_target_strategy_lineinfile_suffix: :9100 roles: - role: kliwniloc.prometheus_target prometheus_target_exporter: - - id: node_exporter + - path: /opt/lineinfile_parallel.yml host: "{{ inventory_hostname }}" - path: /opt/hook_target.yml ################################################################################ ################################################################################ -- name: Deploy with lineinfile - hosts: application +- name: Lineinfile user edited file + hosts: application2 pre_tasks: - - name: Create test user - ansible.builtin.user: - name: prometheus - become: true + - name: Create user edited lineinfile target + ansible.builtin.copy: + dest: /opt/lineinfile_user_edited.yml + owner: prometheus + group: prometheus + mode: "0600" + content: | + # user comment + - labels: + env: prod + targets: + - existing:9100 + force: false delegate_to: "{{ prometheus_target_host }}" - - name: Create target + vars: + prometheus_target_host: prometheus + prometheus_target_strategy_lineinfile_prefix: " - " + prometheus_target_strategy_lineinfile_suffix: :9100 + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/lineinfile_user_edited.yml + host: "{{ inventory_hostname }}" + +################################################################################ +################################################################################ +- name: Lineinfile remove last host scenario + hosts: localhost + connection: local + gather_facts: false + tasks: + - name: Create single host lineinfile target ansible.builtin.copy: - dest: /opt/lineinfile.yml + dest: /opt/lineinfile_remove_last.yml owner: prometheus group: prometheus mode: "0600" + content: | + - labels: + env: stage + targets: + - application:9100 + force: false + become: true + delegate_to: prometheus + + - name: Remove last host line externally + ansible.builtin.lineinfile: + path: /opt/lineinfile_remove_last.yml + line: " - application:9100" + state: absent + become: true + delegate_to: prometheus + +################################################################################ +################################################################################ +- name: YAML bootstrap and first host + hosts: application + pre_tasks: + - name: Create empty yaml bootstrap target + ansible.builtin.copy: + dest: /opt/yaml_bootstrap.yml + mode: "0644" + content: "" force: false - # yamllint disable rule:indentation + become: true + delegate_to: "{{ prometheus_target_host }}" + + vars: + prometheus_target_host: prometheus + prometheus_target_strategy: yaml + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/yaml_bootstrap.yml + host: "{{ inventory_hostname }}:9100" + labels: + job: node + +################################################################################ +################################################################################ +- name: YAML subsequent hosts and parallel writes + hosts: application_group + pre_tasks: + - name: Create yaml parallel target + ansible.builtin.copy: + dest: /opt/yaml_parallel.yml + mode: "0644" content: | - labels: - my: label + job: node targets: - - existing:9100 - # yamllint enable rule:indentation + - existing:9100 + force: false + delegate_to: "{{ prometheus_target_host }}" + run_once: true + + vars: + prometheus_target_host: prometheus + prometheus_target_strategy: yaml + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/yaml_parallel.yml + host: "{{ inventory_hostname }}:9100" + labels: + job: node + +################################################################################ +################################################################################ +- name: YAML user edited file exact label matching + hosts: application2 + pre_tasks: + - name: Create user edited yaml target + ansible.builtin.copy: + dest: /opt/yaml_user_edited.yml + mode: "0644" + content: | + # user comment that will be lost after parse/write + - labels: + severity: warning + job: external + targets: + - existing_host1:9100 + - labels: + severity: critical + targets: + - application2:9200 + - targets: + - unlabeled:9100 + force: false + delegate_to: "{{ prometheus_target_host }}" + + vars: + prometheus_target_host: prometheus + prometheus_target_strategy: yaml + prometheus_target_exporter_defaults: + exact_match: + path: /opt/yaml_user_edited.yml + host: "{{ inventory_hostname }}:9100" + labels: + severity: warning + job: external + move_target: + path: /opt/yaml_user_edited.yml + host: "{{ inventory_hostname }}:9200" + labels: + severity: critical + team: blue + no_labels: + path: /opt/yaml_user_edited.yml + host: standalone:9300 + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - id: exact_match + - id: move_target + - id: no_labels + +################################################################################ +################################################################################ +- name: YAML remove last host by moving target + hosts: application + pre_tasks: + - name: Create yaml remove last host target + ansible.builtin.copy: + dest: /opt/yaml_move_remove_last.yml + mode: "0644" + content: | + - labels: + job: old + targets: + - application:9400 + force: false become: true delegate_to: "{{ prometheus_target_host }}" vars: prometheus_target_host: prometheus - prometheus_target_strategy_lineinfile_prefix: " - " - prometheus_target_strategy_lineinfile_suffix: :9100 + prometheus_target_strategy: yaml + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/yaml_move_remove_last.yml + host: "{{ inventory_hostname }}:9400" + labels: + job: new + +################################################################################ +################################################################################ +- name: YAML branch coverage matrix + hosts: application3 + pre_tasks: + - name: Create yaml branch coverage target + ansible.builtin.copy: + dest: /opt/yaml_branch_matrix.yml + mode: "0644" + content: | + - labels: + job: append + env: prod + targets: + - existing_append:9500 + - labels: + job: wrong + env: prod + targets: + - keep_same_length:9500 + - labels: + job: short + targets: + - keep_length_mismatch:9500 + - labels: + job: present + env: prod + targets: + - application3:9501 + - labels: + job: move_source + targets: + - application3:9502 + - targets: + - orphan_unlabeled:9503 + - targets: + - shared_unlabeled:9504 + - labels: + job: foreign + targets: + - foreign:9505 + force: false + delegate_to: "{{ prometheus_target_host }}" + + vars: + prometheus_target_host: prometheus + prometheus_target_strategy: yaml + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/yaml_branch_matrix.yml + host: application3:9506 + labels: + job: append + env: prod + - path: /opt/yaml_branch_matrix.yml + host: application3:9501 + labels: + job: present + env: prod + - path: /opt/yaml_branch_matrix.yml + host: application3:9502 + labels: + job: move_target + - path: /opt/yaml_branch_matrix.yml + host: orphan_unlabeled:9503 + labels: + job: promoted + - path: /opt/yaml_branch_matrix.yml + host: standalone_branch:9504 + - path: /opt/yaml_branch_matrix.yml + host: shared_unlabeled:9504 + +################################################################################ +################################################################################ +- name: YAML missing file creation branches + hosts: application4 + vars: + prometheus_target_host: prometheus + prometheus_target_strategy: yaml + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/yaml_missing_labeled.yml + host: "{{ inventory_hostname }}:9600" + labels: + team: infra + - path: /opt/yaml_missing_unlabeled.yml + host: "{{ inventory_hostname }}:9601" + +################################################################################ +################################################################################ +- name: Simple compatibility checks + hosts: application + pre_tasks: + - name: Create compatibility target files + ansible.builtin.copy: + dest: "{{ item }}" + content: "" + mode: "0644" + force: false + become: true + delegate_to: "{{ prometheus_target_host }}" + loop: + - /opt/simple_target1.yml + - /opt/simple_target2.yml + - /opt/simple_target3.yml + - /opt/simple_target4.yml + - /opt/prefix_target1.yml + - /opt/prefix_target2.yml + + - name: Create prefix directory + ansible.builtin.file: + path: /opt/prefix + state: directory + mode: "0644" + become: true + delegate_to: "{{ prometheus_target_host }}" + + - name: Create prefix target + ansible.builtin.copy: + dest: /opt/prefix/prefix_target3.yml + content: "" + mode: "0644" + force: false + become: true + delegate_to: "{{ prometheus_target_host }}" + + vars: + prometheus_target_host: prometheus + prometheus_target_exporter_target_prefix: /opt/ + prometheus_target_exporter_defaults: + node_exporter: + path: simple_target1.yml + host: "{{ inventory_hostname }}" + blackbox_exporter: + path: simple_target2.yml + host: "{{ inventory_hostname }}_AA" + prefixed_exporter: + path: prefix_target1.yml + host: "{{ inventory_hostname }}" + prefixed_other: + path: prefix_target_not_exist.yml + host: "{{ inventory_hostname }}" + path_prefix: /opt/prefix/ roles: - role: kliwniloc.prometheus_target prometheus_target_exporter: - id: node_exporter - path: /opt/lineinfile.yml + - { id: blackbox_exporter, host: test1 } + - { id: blackbox_exporter, host: test2 } + - { host: exporter_without_id, path: simple_target4.yml } + - { id: blackbox_exporter, path: simple_target3.yml } + - { id: prefixed_exporter } + - { + id: prefixed_exporter, + path: /opt/prefix_target2.yml, + path_prefix: "", + } + - { id: prefixed_other, path: prefix_target3.yml } + +################################################################################ +################################################################################ +- name: Hook compatibility checks + hosts: application_group + pre_tasks: + - name: Create hook target + ansible.builtin.copy: + dest: /opt/hook_target.yml + content: "" + mode: "0644" + force: false + become: true + delegate_to: "{{ prometheus_target_host }}" + + vars: + prometheus_target_host: prometheus + prometheus_target_handler_command_enabled: true + prometheus_target_handler_command: + cmd: mkdir -p /opt/hook1 + creates: /opt/hook1 + prometheus_target_handler_command_run_once: true + prometheus_target_handler_shell_enabled: true + prometheus_target_handler_shell: + cmd: echo hello >> /opt/hook2 + + roles: + - role: kliwniloc.prometheus_target + prometheus_target_exporter: + - path: /opt/hook_target.yml host: "{{ inventory_hostname }}" diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 3ffe0b8..d143303 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -27,6 +27,22 @@ platforms: - name: molecule-container-net groups: [application_group] + - name: application3 + image: ${MOLECULE_DISTRO:-debian:12} + dockerfile: Dockerfile.j2 + pre_build_image: false + networks: + - name: molecule-container-net + groups: [application_group] + + - name: application4 + image: ${MOLECULE_DISTRO:-debian:12} + dockerfile: Dockerfile.j2 + pre_build_image: false + networks: + - name: molecule-container-net + groups: [application_group] + - name: prometheus image: ${MOLECULE_DISTRO:-debian:12} dockerfile: Dockerfile.j2 diff --git a/molecule/default/tests/test_check_prometheus_targets.py b/molecule/default/tests/test_check_prometheus_targets.py deleted file mode 100644 index b9024b4..0000000 --- a/molecule/default/tests/test_check_prometheus_targets.py +++ /dev/null @@ -1,90 +0,0 @@ -testinfra_hosts = ["prometheus"] - -""" -Test functionality of defining exporters and default fallbacks -""" - - -def test_check_hosts_added_simple(host): - t1 = host.file("/opt/simple_target1.yml") - t2 = host.file("/opt/simple_target2.yml") - t3 = host.file("/opt/simple_target3.yml") - t4 = host.file("/opt/simple_target4.yml") - - assert t1.exists - assert t2.exists - assert t3.exists - assert t4.exists - - assert t1.content_string == " - application\n" - - assert t2.content_string == " - test1\n - test2\n" - - assert t3.content_string == " - application_AA\n" - - assert t4.content_string == " - exporter_without_id\n" - - -""" -Test prefix functionality -""" - - -def test_check_hosts_added_prefix(host): - t1 = host.file("/opt/prefix_target1.yml") - t2 = host.file("/opt/prefix_target2.yml") - t3 = host.file("/opt/prefix/prefix_target3.yml") - - assert t1.exists - assert t2.exists - assert t3.exists - - assert t1.content_string == " - application\n" - - assert t2.content_string == " - application\n" - - assert t3.content_string == " - application\n" - - -""" -Test hook functionality -""" - - -def test_check_hosts_added_hooks(host): - t1 = host.file("/opt/hook_target.yml") - t2 = host.file("/opt/hook1") - t3 = host.file("/opt/hook2") - - assert t1.exists - assert t2.exists - assert t3.exists - - assert ( - t1.content_string == " - application\n - application2\n" - or t1.content_string == " - application2\n - application\n" - ) - - assert t3.content_string == "hello\nhello\n" - - -""" -Test lineinfile strategy parameters -""" - - -def test_check_host_added_lineinfile(host): - t1 = host.file("/opt/lineinfile.yml") - - assert t1.user == "prometheus" - assert t1.group == "prometheus" - assert t1.mode == 0o600 - - assert ( - t1.content_string == "- labels:\n" - " my: label\n" - " targets:\n" - " - existing:9100\n" - " - application:9100\n" - ) - diff --git a/molecule/default/tests/test_lineinfile_strategy.py b/molecule/default/tests/test_lineinfile_strategy.py new file mode 100644 index 0000000..b75a656 --- /dev/null +++ b/molecule/default/tests/test_lineinfile_strategy.py @@ -0,0 +1,94 @@ +import yaml + + +testinfra_hosts = ["prometheus"] + + +def read_file(host, path): + target = host.file(path) + assert target.exists + return target + + +def read_yaml_file(host, path): + return yaml.safe_load(read_file(host, path).content_string) + + +def test_lineinfile_bootstrap_adds_first_host_without_breaking_yaml(host): + target = read_file(host, "/opt/lineinfile_first_host.yml") + + assert target.user == "prometheus" + assert target.group == "prometheus" + assert target.mode == 0o600 + assert read_yaml_file(host, "/opt/lineinfile_first_host.yml") == ["application:9100"] + + +def test_lineinfile_adds_subsequent_host_to_existing_yaml(host): + target = read_file(host, "/opt/lineinfile_bootstrap.yml") + + assert target.content_string == ( + "- labels:\n" + " my: label\n" + " targets:\n" + " - existing:9100\n" + " - application:9100\n" + ) + + assert read_yaml_file(host, "/opt/lineinfile_bootstrap.yml") == [ + {"labels": {"my": "label"}, "targets": ["existing:9100", "application:9100"]} + ] + + +def test_lineinfile_parallel_writes_keep_all_hosts_and_parse(host): + hosts = sorted(read_yaml_file(host, "/opt/lineinfile_parallel.yml")) + assert hosts == [ + "application2:9100", + "application3:9100", + "application4:9100", + "application:9100", + ] + + +def test_lineinfile_user_edited_file_stays_parseable(host): + target = read_file(host, "/opt/lineinfile_user_edited.yml") + + assert target.content_string == ( + "# user comment\n" + "- labels:\n" + " env: prod\n" + " targets:\n" + " - existing:9100\n" + " - application2:9100\n" + ) + + assert read_yaml_file(host, "/opt/lineinfile_user_edited.yml") == [ + {"labels": {"env": "prod"}, "targets": ["existing:9100", "application2:9100"]} + ] + + +def test_lineinfile_removing_last_host_still_leaves_parseable_yaml(host): + assert read_yaml_file(host, "/opt/lineinfile_remove_last.yml") == [ + {"labels": {"env": "stage"}, "targets": None} + ] + + +def test_lineinfile_compatibility_simple_and_prefix_cases(host): + assert read_yaml_file(host, "/opt/simple_target1.yml") == ["application"] + assert read_yaml_file(host, "/opt/simple_target2.yml") == ["test1", "test2"] + assert read_yaml_file(host, "/opt/simple_target3.yml") == ["application_AA"] + assert read_yaml_file(host, "/opt/simple_target4.yml") == ["exporter_without_id"] + assert read_yaml_file(host, "/opt/prefix_target1.yml") == ["application"] + assert read_yaml_file(host, "/opt/prefix_target2.yml") == ["application"] + assert read_yaml_file(host, "/opt/prefix/prefix_target3.yml") == ["application"] + + +def test_lineinfile_hooks_still_run(host): + assert sorted(read_yaml_file(host, "/opt/hook_target.yml")) == [ + "application", + "application2", + "application3", + "application4", + ] + + assert host.file("/opt/hook1").exists + assert host.file("/opt/hook2").content_string == "hello\nhello\nhello\nhello\n" diff --git a/molecule/default/tests/test_yaml_strategy.py b/molecule/default/tests/test_yaml_strategy.py new file mode 100644 index 0000000..89dbdeb --- /dev/null +++ b/molecule/default/tests/test_yaml_strategy.py @@ -0,0 +1,139 @@ +import yaml + + +testinfra_hosts = ["prometheus"] + + +def read_yaml_file(host, path): + target = host.file(path) + assert target.exists + return yaml.safe_load(target.content_string) + + +def get_group_by_labels(groups, labels): + for group in groups: + if group.get("labels", {}) == labels: + return group + raise AssertionError(f"Missing target group with labels {labels!r}") + + +def get_unlabeled_group(groups): + for group in groups: + if group.get("labels", {}) == {}: + return group + raise AssertionError("Missing unlabeled target group") + + +def has_group_with_labels(groups, labels): + return any(group.get("labels", {}) == labels for group in groups) + + +def test_yaml_bootstrap_adds_first_host_to_empty_file(host): + assert read_yaml_file(host, "/opt/yaml_bootstrap.yml") == [ + {"labels": {"job": "node"}, "targets": ["application:9100"]} + ] + + +def test_yaml_parallel_writes_keep_all_hosts_in_same_group(host): + groups = read_yaml_file(host, "/opt/yaml_parallel.yml") + assert get_group_by_labels(groups, {"job": "node"})["targets"] == [ + "existing:9100", + "application:9100", + "application2:9100", + "application3:9100", + "application4:9100", + ] + + +def test_yaml_user_edited_file_is_reparsed_and_keeps_expected_semantics(host): + groups = read_yaml_file(host, "/opt/yaml_user_edited.yml") + + assert get_group_by_labels(groups, {"severity": "warning", "job": "external"})[ + "targets" + ] == ["existing_host1:9100", "application2:9100"] + + assert get_group_by_labels(groups, {"severity": "critical", "team": "blue"})[ + "targets" + ] == ["application2:9200"] + + assert get_unlabeled_group(groups)["targets"] == ["unlabeled:9100", "standalone:9300"] + + +def test_yaml_moving_host_removes_last_host_from_old_group(host): + groups = read_yaml_file(host, "/opt/yaml_move_remove_last.yml") + + assert not has_group_with_labels(groups, {"job": "old"}) + assert get_group_by_labels(groups, {"job": "new"})["targets"] == ["application:9400"] + + +def test_yaml_output_remains_parseable_after_all_operations(host): + for path in [ + "/opt/yaml_bootstrap.yml", + "/opt/yaml_parallel.yml", + "/opt/yaml_user_edited.yml", + "/opt/yaml_move_remove_last.yml", + "/opt/yaml_branch_matrix.yml", + "/opt/yaml_missing_labeled.yml", + "/opt/yaml_missing_unlabeled.yml", + ]: + assert read_yaml_file(host, path) is not None + + +def test_yaml_branch_matrix_covers_exact_match_and_append(host): + groups = read_yaml_file(host, "/opt/yaml_branch_matrix.yml") + + assert get_group_by_labels(groups, {"job": "append", "env": "prod"})["targets"] == [ + "existing_append:9500", + "application3:9506", + ] + + assert get_group_by_labels(groups, {"job": "present", "env": "prod"})["targets"] == [ + "application3:9501" + ] + + +def test_yaml_branch_matrix_keeps_nonmatching_groups_for_both_label_mismatch_paths(host): + groups = read_yaml_file(host, "/opt/yaml_branch_matrix.yml") + + assert get_group_by_labels(groups, {"job": "wrong", "env": "prod"})["targets"] == [ + "keep_same_length:9500" + ] + + assert get_group_by_labels(groups, {"job": "short"})["targets"] == [ + "keep_length_mismatch:9500" + ] + + assert get_group_by_labels(groups, {"job": "foreign"})["targets"] == ["foreign:9505"] + + +def test_yaml_branch_matrix_moves_hosts_and_drops_emptied_groups(host): + groups = read_yaml_file(host, "/opt/yaml_branch_matrix.yml") + + assert not has_group_with_labels(groups, {"job": "move_source"}) + + assert get_group_by_labels(groups, {"job": "move_target"})["targets"] == [ + "application3:9502" + ] + + assert get_group_by_labels(groups, {"job": "promoted"})["targets"] == [ + "orphan_unlabeled:9503" + ] + + +def test_yaml_branch_matrix_preserves_unlabeled_group_when_targets_remain(host): + groups = read_yaml_file(host, "/opt/yaml_branch_matrix.yml") + + assert get_unlabeled_group(groups)["targets"] == [ + "shared_unlabeled:9504", + "standalone_branch:9504", + ] + + +def test_yaml_branch_matrix_creates_missing_file_groups_for_labeled_and_unlabeled_targets(host): + assert read_yaml_file(host, "/opt/yaml_missing_labeled.yml") == [ + {"labels": {"team": "infra"}, "targets": ["application4:9600"]} + ] + + assert read_yaml_file(host, "/opt/yaml_missing_unlabeled.yml") == [ + {"targets": ["application4:9601"]} + ] |