diff --git a/README.md b/README.md index 68af1ea..1530585 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,23 @@ The main purpose of this collections are to: loading local secrets files into VP secrets stores. 2. Help manage imperative and other utility functions of the cluster + +## Clustergroup discovery + +The **`clustergroup_discovery`** role lists **main and managed clustergroup value stems** for a Validated Patterns checkout (from **`values-global.yaml`** and **`clusterGroup.managedClusterGroups`** in the main **`values-
.yaml|yml`**), and which local **`values-.yaml|yml`** files exist. + +- **`playbooks/list_clustergroups.yml`** — prints discovery facts (stems, load order, file paths). +- **`playbooks/parse_clustergroup_values.yml`** — same, plus optional YAML parse into **`clustergroup_documents`**. + +See **`roles/clustergroup_discovery/README.md`** for variables and behavior. + +## Pattern repository directory (`pattern_dir`) + +Playbooks need the path to your pattern Git checkout (where **`values-global.yaml`** +and related files live). Resolution order: extra var **`pattern_dir`**, environment +variable **`PATTERN_DIR`**, then **`PWD`** and **`pwd`**. + +When running from the imperative container or another fixed working directory, +pass the repository root explicitly, for example **`-e pattern_dir=/git/repo`** (or add +equivalent extra vars via **`clusterGroup.imperative.extraPlaybookArgs`** in the +clustergroup chart). diff --git a/playbooks/list_clustergroups.yml b/playbooks/list_clustergroups.yml new file mode 100644 index 0000000..6759583 --- /dev/null +++ b/playbooks/list_clustergroups.yml @@ -0,0 +1,21 @@ +--- +# Discover values-.yaml|yml under pattern_dir. +# Resolves pattern_dir like pattern_settings (extra var pattern_dir, env PATTERN_DIR, cwd). +- name: List pattern clustergroup value stems + hosts: localhost + connection: local + gather_facts: false + become: false + roles: + - pattern_settings + - role: clustergroup_discovery + tasks: + - name: Report clustergroup discovery + ansible.builtin.debug: + msg: + pattern_dir: "{{ pattern_dir }}" + main_clustergroup: "{{ main_clustergroup }}" + managed_clustergroup_names: "{{ managed_clustergroup_names }}" + clustergroup_names: "{{ clustergroup_names }}" + clustergroup_load_order: "{{ clustergroup_load_order }}" + clustergroup_file_entries: "{{ clustergroup_file_entries }}" diff --git a/playbooks/parse_clustergroup_values.yml b/playbooks/parse_clustergroup_values.yml new file mode 100644 index 0000000..642fe03 --- /dev/null +++ b/playbooks/parse_clustergroup_values.yml @@ -0,0 +1,22 @@ +--- +# Parse every top-level values-.yaml|yml into clustergroup_documents (stem -> root). +# Use for migration tooling or inspection; other roles can include the same discovery logic. +- name: Parse pattern clustergroup values files + hosts: localhost + connection: local + gather_facts: false + become: false + roles: + - pattern_settings + - role: clustergroup_discovery + vars: + clustergroup_discovery_parse_documents: true + tasks: + - name: Summarize parsed clustergroup documents + ansible.builtin.debug: + msg: + pattern_dir: "{{ pattern_dir }}" + main_clustergroup: "{{ main_clustergroup }}" + managed_clustergroup_names: "{{ managed_clustergroup_names }}" + stems_parsed: "{{ clustergroup_documents | default({}) | dict2items | map(attribute='key') | sort | list }}" + document_count: "{{ clustergroup_documents | default({}) | length }}" diff --git a/roles/clustergroup_discovery/README.md b/roles/clustergroup_discovery/README.md new file mode 100644 index 0000000..03dfcb7 --- /dev/null +++ b/roles/clustergroup_discovery/README.md @@ -0,0 +1,27 @@ +# clustergroup_discovery + +Ansible role that lists **which clustergroup value stems are in use** for a Validated Patterns checkout, without scanning every `values-*.yaml` on disk. + +## Behavior + +1. Resolve **`pattern_dir`** the same way as `pattern_settings` (extra var, `PATTERN_DIR`, then `PWD` / `pwd`). +2. Read **`main.clusterGroupName`** from `values-global.yaml` under `pattern_dir` (or use `main_clustergroup` / `main_clustergroupname` if the play already set them). +3. Load **`values-
.yaml`** or **`values-
.yml`** and read **`clusterGroup.managedClusterGroups`**. For each entry, the managed name is **`value.name`** if set, otherwise the **YAML key** (same convention as the clustergroup chart for managed cluster groups). +4. Expose facts: + - **`managed_clustergroup_names`** — sorted unique managed names + - **`clustergroup_load_order`** — `[main, …managed]` (main first; used when merging so later stems override duplicate `applications` keys) + - **`clustergroup_names`** — sorted list of all stems (main + managed) + - **`clustergroup_file_entries`** — `{name, path}` only for stems where a local `values-.yaml|yml` exists + +Optional: set **`clustergroup_discovery_parse_documents: true`** to fill **`clustergroup_documents`** (`` → parsed YAML root) for each file in `clustergroup_file_entries`. + +## Playbooks + +- `playbooks/list_clustergroups.yml` — runs `pattern_settings` + this role and prints the facts above. +- `playbooks/parse_clustergroup_values.yml` — same with parsing enabled. + +Requires `ANSIBLE_ROLES_PATH` (or collection layout) so `pattern_settings` and this role resolve. + +## Using this role from other Ansible + +Include **`clustergroup_discovery`** (after **`pattern_settings`** or an equivalent that sets **`pattern_dir`** / **`main_clustergroup`**) whenever you need **`clustergroup_load_order`**, **`clustergroup_file_entries`**, or parsed **`clustergroup_documents`** before loading clusterGroup values per stem. diff --git a/roles/clustergroup_discovery/defaults/main.yml b/roles/clustergroup_discovery/defaults/main.yml new file mode 100644 index 0000000..8e87810 --- /dev/null +++ b/roles/clustergroup_discovery/defaults/main.yml @@ -0,0 +1,3 @@ +--- +# When true, slurp and parse each resolved clustergroup file into clustergroup_documents (stem -> root mapping) +clustergroup_discovery_parse_documents: false diff --git a/roles/clustergroup_discovery/meta/main.yml b/roles/clustergroup_discovery/meta/main.yml new file mode 100644 index 0000000..8b20d5e --- /dev/null +++ b/roles/clustergroup_discovery/meta/main.yml @@ -0,0 +1,12 @@ +--- +galaxy_info: + author: rhvp + description: >- + Resolve main clustergroup from values-global, read managedClusterGroups from the main + values file, then optionally parse existing values- files for those stems. + license: Apache-2.0 + min_ansible_version: "2.14" + galaxy_tags: + - openshift + - gitops +dependencies: [] diff --git a/roles/clustergroup_discovery/tasks/main.yml b/roles/clustergroup_discovery/tasks/main.yml new file mode 100644 index 0000000..596dbf2 --- /dev/null +++ b/roles/clustergroup_discovery/tasks/main.yml @@ -0,0 +1,118 @@ +--- +# Discover clustergroups in use: main from values-global, managed from main file's clusterGroup.managedClusterGroups. +# Sets: clustergroup_names (sorted stems), managed_clustergroup_names (sorted, excludes main), +# clustergroup_load_order (main first, then managed sorted — later stems override duplicate keys when merging), +# clustergroup_file_entries ({name, path} only when values-.yaml|yml exists), +# clustergroup_documents (optional, stem -> parsed YAML root). + +- name: Resolve pattern_dir for clustergroup discovery + ansible.builtin.include_tasks: ../pattern_settings/tasks/resolve_overrides.yml + when: (pattern_dir | default('', true) | string | trim | length) == 0 + +- name: Fail when pattern_dir is empty after resolve + ansible.builtin.fail: + msg: >- + pattern_dir is required (extra var pattern_dir, env PATTERN_DIR, or cwd with values-global.yaml). + when: (pattern_dir | default('', true) | string | trim | length) == 0 + +- name: Resolve main clustergroup stem from facts or values-global.yaml + ansible.builtin.set_fact: + _clustergroup_discovery_main_stem: >- + {{ + ( + (main_clustergroupname | default(main_clustergroup | default('', true), true) | string | trim | length) > 0 + ) + | ternary( + main_clustergroupname | default(main_clustergroup, true) | string | trim, + ( + lookup('file', (pattern_dir | string | trim) ~ '/values-global.yaml') + | from_yaml + ).main.clusterGroupName | string | trim + ) + }} + +- name: Fail when main clusterGroupName cannot be resolved + ansible.builtin.fail: + msg: >- + Could not resolve main clustergroup (values-global.yaml missing .main.clusterGroupName or empty). + when: (_clustergroup_discovery_main_stem | string | trim | length) == 0 + +- name: Stat main clustergroup values file (yaml) + ansible.builtin.stat: + path: "{{ pattern_dir | string | trim }}/values-{{ _clustergroup_discovery_main_stem }}.yaml" + register: _clustergroup_discovery_main_stat_yaml + +- name: Stat main clustergroup values file (yml) + ansible.builtin.stat: + path: "{{ pattern_dir | string | trim }}/values-{{ _clustergroup_discovery_main_stem }}.yml" + register: _clustergroup_discovery_main_stat_yml + when: not (_clustergroup_discovery_main_stat_yaml.stat.exists | default(false)) + +- name: Set path to main clustergroup values file when present + ansible.builtin.set_fact: + _clustergroup_main_values_path: "{{ pattern_dir | string | trim }}/values-{{ _clustergroup_discovery_main_stem }}.yaml" + when: _clustergroup_discovery_main_stat_yaml.stat.exists | default(false) + +- name: Set path to main clustergroup values file when only yml exists + ansible.builtin.set_fact: + _clustergroup_main_values_path: "{{ pattern_dir | string | trim }}/values-{{ _clustergroup_discovery_main_stem }}.yml" + when: + - _clustergroup_main_values_path is not defined + - _clustergroup_discovery_main_stat_yml is defined + - _clustergroup_discovery_main_stat_yml.stat.exists | default(false) + +- name: Load parsed root from main clustergroup values file + ansible.builtin.set_fact: + _clustergroup_main_root: "{{ lookup('file', _clustergroup_main_values_path) | from_yaml }}" + when: _clustergroup_main_values_path is defined + +- name: Default empty main clustergroup root when file is absent + ansible.builtin.set_fact: + _clustergroup_main_root: {} + when: _clustergroup_main_values_path is not defined + +- name: Collect managed clustergroup names from main file managedClusterGroups + ansible.builtin.set_fact: + managed_clustergroup_names: "{{ managed_clustergroup_names | default([]) + [_cgd_mcg_name] }}" + vars: + _cgd_mcg_name: "{{ (item.value.name | default(item.key, true)) | string | trim }}" + loop: "{{ (_clustergroup_main_root.clusterGroup | default({})).managedClusterGroups | default({}) | dict2items }}" + loop_control: + label: "{{ _cgd_mcg_name }}" + when: + - _clustergroup_main_root is mapping + - (_clustergroup_main_root.clusterGroup | default({})).managedClusterGroups is defined + - ((_clustergroup_main_root.clusterGroup | default({})).managedClusterGroups | default({})) is mapping + +- name: Finalize managed clustergroup names list + ansible.builtin.set_fact: + managed_clustergroup_names: "{{ managed_clustergroup_names | default([]) | unique | sort }}" + +- name: Set clustergroup load order (main first so managed values files override on duplicate keys) + ansible.builtin.set_fact: + clustergroup_load_order: >- + {{ + ( + [_clustergroup_discovery_main_stem] + + (managed_clustergroup_names | reject('equalto', _clustergroup_discovery_main_stem) | list) + ) | unique | list + }} + +- name: Set sorted clustergroup names (all stems in use) + ansible.builtin.set_fact: + clustergroup_names: "{{ clustergroup_load_order | sort }}" + +- name: Build clustergroup_file_entries for stems that have a local values file + ansible.builtin.include_tasks: resolve_clustergroup_file_path.yml + loop: "{{ clustergroup_load_order }}" + loop_control: + loop_var: clustergroup_discovery_stem + +- name: Default empty clustergroup file entries + ansible.builtin.set_fact: + clustergroup_file_entries: [] + when: clustergroup_file_entries is not defined + +- name: Parse each resolved clustergroup values file when requested + ansible.builtin.include_tasks: parse_documents.yml + when: clustergroup_discovery_parse_documents | default(false) | bool diff --git a/roles/clustergroup_discovery/tasks/parse_documents.yml b/roles/clustergroup_discovery/tasks/parse_documents.yml new file mode 100644 index 0000000..e0d29ec --- /dev/null +++ b/roles/clustergroup_discovery/tasks/parse_documents.yml @@ -0,0 +1,7 @@ +--- +- name: Parse clustergroup values YAML into clustergroup_documents + ansible.builtin.set_fact: + clustergroup_documents: "{{ clustergroup_documents | default({}) | combine({item.name: (lookup('file', item.path) | from_yaml)}) }}" + loop: "{{ clustergroup_file_entries }}" + loop_control: + label: "{{ item.name }}" diff --git a/roles/clustergroup_discovery/tasks/resolve_clustergroup_file_path.yml b/roles/clustergroup_discovery/tasks/resolve_clustergroup_file_path.yml new file mode 100644 index 0000000..1e0778b --- /dev/null +++ b/roles/clustergroup_discovery/tasks/resolve_clustergroup_file_path.yml @@ -0,0 +1,32 @@ +--- +# loop_var: clustergroup_discovery_stem — append {name, path} to clustergroup_file_entries when file exists. + +- name: Stat values file for stem {{ clustergroup_discovery_stem }} (yaml) + ansible.builtin.stat: + path: "{{ pattern_dir | string | trim }}/values-{{ clustergroup_discovery_stem | string | trim }}.yaml" + register: _clustergroup_discovery_stem_stat_yaml + +- name: Stat values file for stem {{ clustergroup_discovery_stem }} (yml) + ansible.builtin.stat: + path: "{{ pattern_dir | string | trim }}/values-{{ clustergroup_discovery_stem | string | trim }}.yml" + register: _clustergroup_discovery_stem_stat_yml + +- name: Record clustergroup file entry for {{ clustergroup_discovery_stem }} (prefer yaml) + ansible.builtin.set_fact: + clustergroup_file_entries: "{{ clustergroup_file_entries | default([]) + [_entry] }}" + vars: + _entry: + name: "{{ clustergroup_discovery_stem | string | trim }}" + path: "{{ pattern_dir | string | trim }}/values-{{ clustergroup_discovery_stem | string | trim }}.yaml" + when: _clustergroup_discovery_stem_stat_yaml.stat.exists | default(false) + +- name: Record clustergroup file entry for {{ clustergroup_discovery_stem }} (yml fallback) + ansible.builtin.set_fact: + clustergroup_file_entries: "{{ clustergroup_file_entries | default([]) + [_entry] }}" + vars: + _entry: + name: "{{ clustergroup_discovery_stem | string | trim }}" + path: "{{ pattern_dir | string | trim }}/values-{{ clustergroup_discovery_stem | string | trim }}.yml" + when: + - not (_clustergroup_discovery_stem_stat_yaml.stat.exists | default(false)) + - _clustergroup_discovery_stem_stat_yml.stat.exists | default(false)