1187 lines
41 KiB
Markdown
1187 lines
41 KiB
Markdown
# Role Structure Standards
|
|
|
|
## Summary: Pattern Confidence
|
|
|
|
Analyzed 7 geerlingguy roles: security, users, docker, postgresql, nginx, pip, git
|
|
|
|
**Universal Patterns (All 7 roles):**
|
|
|
|
- Standard Ansible role directory structure (defaults/, tasks/, meta/, molecule/, .github/) (7/7 roles)
|
|
- tasks/main.yml as router with include_tasks/import_tasks (7/7 roles)
|
|
- Role-prefixed variable names preventing conflicts (7/7 roles use rolename_feature_attribute)
|
|
- Snake_case naming convention throughout (7/7 roles)
|
|
- defaults/ for user configuration, vars/ for OS-specific values (7/7 roles)
|
|
- Descriptive task names starting with action verbs (7/7 roles)
|
|
- Configuration file validation before applying (sshd -T, visudo -cf) (7/7 security-sensitive roles)
|
|
- Explicit file permissions on security-sensitive files (7/7 roles)
|
|
- Quality control files (.ansible-lint, .yamllint, .gitignore) (7/7 roles)
|
|
|
|
**Contextual Patterns (Varies by complexity):**
|
|
|
|
- Task file organization: simple roles use single main.yml, complex roles split into 8+ feature files
|
|
- vars/ directory presence: only when OS-specific data needed (4/7 roles have it)
|
|
- templates/ usage: complex config roles use templates/ heavily, simple roles use lineinfile/copy
|
|
- handlers/ presence: only service-managing roles need handlers (4/7 roles have them)
|
|
- Directory count scales with complexity: minimal roles (pip) have 3 dirs, complex roles (postgresql) have 7+ dirs
|
|
|
|
**Evolving Patterns (Newer roles improved):**
|
|
|
|
- Advanced include_vars with first_found lookup (docker role) provides fallback chain for better distribution support
|
|
- import_tasks vs include_tasks distinction: import for ordered execution, include for conditional
|
|
- Jinja2 block inheritance in templates (nginx role) for user extensibility without full template replacement
|
|
|
|
**Sources:**
|
|
|
|
- geerlingguy.security (analyzed 2025-10-23)
|
|
- geerlingguy.github-users (analyzed 2025-10-23)
|
|
- geerlingguy.docker (analyzed 2025-10-23)
|
|
- geerlingguy.postgresql (analyzed 2025-10-23)
|
|
- geerlingguy.nginx (analyzed 2025-10-23)
|
|
- geerlingguy.pip (analyzed 2025-10-23)
|
|
- geerlingguy.git (analyzed 2025-10-23)
|
|
|
|
**Repositories:**
|
|
|
|
- <https://github.com/geerlingguy/ansible-role-security>
|
|
- <https://github.com/geerlingguy/ansible-role-github-users>
|
|
- <https://github.com/geerlingguy/ansible-role-docker>
|
|
- <https://github.com/geerlingguy/ansible-role-postgresql>
|
|
- <https://github.com/geerlingguy/ansible-role-nginx>
|
|
- <https://github.com/geerlingguy/ansible-role-pip>
|
|
- <https://github.com/geerlingguy/ansible-role-git>
|
|
|
|
## Pattern Confidence Levels (Historical)
|
|
|
|
Analyzed 2 geerlingguy roles: security, github-users
|
|
|
|
**Universal Patterns (Both roles use identical approach):**
|
|
|
|
1. ✅ **Standard directory structure** - Both follow defaults/, tasks/, meta/, molecule/, .github/ structure
|
|
2. ✅ **Role-prefixed variable names** - security_*, github_users_* (prevents conflicts)
|
|
3. ✅ **Descriptive task names** - Action verb + object pattern ("Ensure...", "Add...", "Update...")
|
|
4. ✅ **defaults/ for user configuration** - All user-overridable values in defaults/main.yml
|
|
5. ✅ **Snake_case naming** - Consistent variable naming convention
|
|
6. ✅ **Inline validation** - validate parameter for critical config files
|
|
7. ✅ **File permissions** - Explicit mode settings on all files
|
|
8. ✅ **Quality control files** - .ansible-lint, .yamllint, .gitignore present
|
|
|
|
**Contextual Patterns (Varies by role complexity):**
|
|
|
|
1. ⚠️ **Task file organization** - security splits tasks (ssh.yml, fail2ban.yml), github-users keeps single
|
|
main.yml (role is simpler)
|
|
2. ⚠️ **vars/ directory** - security has OS-specific vars files, github-users doesn't need them
|
|
3. ⚠️ **templates/ usage** - security uses templates for fail2ban config, github-users has no templates
|
|
4. ⚠️ **handlers/** - security has 3 handlers (services to restart), github-users has none (no services managed)
|
|
5. ⚠️ **Conditional task execution** - security uses OS-family conditionals, github-users is OS-agnostic
|
|
|
|
**Key Finding:** Simple roles (like github-users) can keep all tasks in main.yml. Complex roles (like security)
|
|
should split into feature-based files when tasks exceed ~30-40 lines.
|
|
|
|
## Overview
|
|
|
|
This document captures role structure and organization patterns from production-grade Ansible roles,
|
|
demonstrating how to organize tasks, variables, handlers, and templates for maintainability and clarity.
|
|
|
|
## Directory Organization
|
|
|
|
### Pattern: Standard Ansible Role Structure
|
|
|
|
**Description:** Follow the standard Ansible role directory structure for consistency and Galaxy compatibility.
|
|
|
|
**Directory Tree:**
|
|
|
|
```text
|
|
ansible-role-security/
|
|
├── .github/
|
|
│ └── workflows/
|
|
│ ├── ci.yml
|
|
│ ├── release.yml
|
|
│ └── stale.yml
|
|
├── defaults/
|
|
│ └── main.yml
|
|
├── handlers/
|
|
│ └── main.yml
|
|
├── meta/
|
|
│ └── main.yml
|
|
├── molecule/
|
|
│ └── default/
|
|
│ ├── converge.yml
|
|
│ └── molecule.yml
|
|
├── tasks/
|
|
│ ├── main.yml
|
|
│ ├── ssh.yml
|
|
│ ├── fail2ban.yml
|
|
│ ├── autoupdate-RedHat.yml
|
|
│ └── autoupdate-Debian.yml
|
|
├── templates/
|
|
│ └── jail.local.j2
|
|
├── vars/
|
|
│ ├── Debian.yml
|
|
│ └── RedHat.yml
|
|
├── .ansible-lint
|
|
├── .gitignore
|
|
├── .yamllint
|
|
├── LICENSE
|
|
└── README.md
|
|
```
|
|
|
|
**Directory Purposes:**
|
|
|
|
- **defaults/** - User-overridable default values (lowest precedence)
|
|
- **vars/** - OS-specific or internal variables (high precedence)
|
|
- **tasks/** - Ansible tasks organized into logical files
|
|
- **handlers/** - Event-triggered tasks (service restarts, reloads)
|
|
- **templates/** - Jinja2 templates for configuration files
|
|
- **meta/** - Role metadata (Galaxy info, dependencies)
|
|
- **molecule/** - Testing scenarios and configurations
|
|
- **.github/workflows/** - CI/CD automation
|
|
- **files/** - Static files (not used in this role, but common)
|
|
|
|
**When to Use:**
|
|
|
|
- Always create this base structure for new roles
|
|
- Omit directories you don't need (files/, templates/ if unused)
|
|
- Add molecule/ for all production roles
|
|
- Include .github/workflows/ for open source or team roles
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't create directories you won't use (empty dirs confuse users)
|
|
- Avoid non-standard directory names
|
|
- Don't mix role content with playbooks in same directory
|
|
|
|
## Task Organization
|
|
|
|
### Pattern: Main Task File as Router
|
|
|
|
**Description:** Use tasks/main.yml as a routing file that includes other task files based on conditions.
|
|
This keeps the main file simple and delegates work to focused task files.
|
|
|
|
**File Path:** `tasks/main.yml`
|
|
|
|
**Example Code:**
|
|
|
|
```yaml
|
|
---
|
|
- name: Include OS-specific variables.
|
|
include_vars: "{{ ansible_os_family }}.yml"
|
|
|
|
# Fail2Ban
|
|
- include_tasks: fail2ban.yml
|
|
when: security_fail2ban_enabled | bool
|
|
|
|
# SSH
|
|
- include_tasks: ssh.yml
|
|
|
|
# Autoupdate
|
|
- include_tasks: autoupdate-RedHat.yml
|
|
when:
|
|
- ansible_os_family == 'RedHat'
|
|
- security_autoupdate_enabled | bool
|
|
|
|
- include_tasks: autoupdate-Debian.yml
|
|
when:
|
|
- ansible_os_family == 'Debian'
|
|
- security_autoupdate_enabled | bool
|
|
```
|
|
|
|
**Key Elements:**
|
|
|
|
1. **include_vars at top** - Load OS-specific variables first
|
|
2. **Logical grouping** - Each include_tasks represents a feature
|
|
3. **Conditional includes** - Only run tasks when needed
|
|
4. **Comments as section headers** - Improve readability
|
|
5. **Boolean filter** - `| bool` ensures proper boolean evaluation
|
|
6. **Multi-line conditions** - Use list format for multiple when clauses
|
|
|
|
**Task File Organization Strategy:**
|
|
|
|
- **Feature-based:** ssh.yml, fail2ban.yml (grouped by functionality)
|
|
- **OS-specific:** autoupdate-RedHat.yml, autoupdate-Debian.yml (split by platform)
|
|
|
|
**When to Use:**
|
|
|
|
- Split tasks into separate files when >30-40 lines
|
|
- Create OS-specific task files for platform differences
|
|
- Use conditional includes for optional features
|
|
- Keep main.yml under 50 lines as a routing file
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't put all tasks in main.yml (hard to maintain)
|
|
- Avoid deep nesting of include_tasks (max 2 levels)
|
|
- Don't split too granularly (each file should have 10+ lines)
|
|
|
|
### Pattern: Feature-Specific Task Files
|
|
|
|
**Description:** Create focused task files for specific features, with clear names that describe their purpose.
|
|
|
|
**File Path:** `tasks/ssh.yml`
|
|
|
|
**Example Code:**
|
|
|
|
```yaml
|
|
---
|
|
- name: Ensure SSH daemon is running.
|
|
service:
|
|
name: "{{ security_sshd_name }}"
|
|
state: "{{ security_sshd_state }}"
|
|
|
|
- name: Update SSH configuration to be more secure.
|
|
lineinfile:
|
|
dest: "{{ security_ssh_config_path }}"
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
state: present
|
|
validate: 'sshd -T -f %s'
|
|
mode: 0644
|
|
with_items:
|
|
- regexp: "^PasswordAuthentication"
|
|
line: "PasswordAuthentication {{ security_ssh_password_authentication }}"
|
|
- regexp: "^PermitRootLogin"
|
|
line: "PermitRootLogin {{ security_ssh_permit_root_login }}"
|
|
- regexp: "^Port"
|
|
line: "Port {{ security_ssh_port }}"
|
|
- regexp: "^UseDNS"
|
|
line: "UseDNS {{ security_ssh_usedns }}"
|
|
- regexp: "^PermitEmptyPasswords"
|
|
line: "PermitEmptyPasswords {{ security_ssh_permit_empty_password }}"
|
|
- regexp: "^ChallengeResponseAuthentication"
|
|
line: "ChallengeResponseAuthentication {{ security_ssh_challenge_response_auth }}"
|
|
- regexp: "^GSSAPIAuthentication"
|
|
line: "GSSAPIAuthentication {{ security_ssh_gss_api_authentication }}"
|
|
- regexp: "^X11Forwarding"
|
|
line: "X11Forwarding {{ security_ssh_x11_forwarding }}"
|
|
notify:
|
|
- reload systemd
|
|
- restart ssh
|
|
|
|
- name: Add configured users allowed to connect over ssh
|
|
lineinfile:
|
|
dest: "{{ security_ssh_config_path }}"
|
|
regexp: '^AllowUsers'
|
|
line: "AllowUsers {{ security_ssh_allowed_users | join(' ') }}"
|
|
state: present
|
|
create: true
|
|
validate: 'sshd -T -f %s'
|
|
mode: 0644
|
|
when: security_ssh_allowed_users | length > 0
|
|
notify: restart ssh
|
|
|
|
- name: Add configured user accounts to passwordless sudoers.
|
|
lineinfile:
|
|
dest: /etc/sudoers
|
|
regexp: '^{{ item }}'
|
|
line: '{{ item }} ALL=(ALL) NOPASSWD: ALL'
|
|
state: present
|
|
validate: 'visudo -cf %s'
|
|
mode: 0440
|
|
with_items: "{{ security_sudoers_passwordless }}"
|
|
when: security_sudoers_passwordless | length > 0
|
|
```
|
|
|
|
**Key Patterns:**
|
|
|
|
1. **Validation parameters:**
|
|
- `validate: 'sshd -T -f %s'` - Test SSH config before applying
|
|
- `validate: 'visudo -cf %s'` - Validate sudoers syntax
|
|
- Prevents breaking critical system files
|
|
|
|
2. **Idempotent configuration:**
|
|
- lineinfile with regexp - Updates or adds lines
|
|
- state: present - Ensures line exists
|
|
- Anchored regexps (^) - Match start of line
|
|
|
|
3. **Conditional execution:**
|
|
- `when: security_ssh_allowed_users | length > 0` - Skip if empty list
|
|
- Prevents unnecessary file modifications
|
|
|
|
4. **Handler notifications:**
|
|
- `notify: restart ssh` - Trigger service restart on changes
|
|
- Multiple handlers can be notified
|
|
- Handlers run once at end, even if notified multiple times
|
|
|
|
5. **File permissions:**
|
|
- `mode: 0644` for SSH config (readable by all)
|
|
- `mode: 0440` for sudoers (read-only, no world access)
|
|
|
|
**When to Use:**
|
|
|
|
- Always validate critical config files (SSH, sudoers, etc.)
|
|
- Use lineinfile for simple config changes
|
|
- Notify handlers instead of inline service restarts
|
|
- Set explicit file permissions on security-sensitive files
|
|
- Use conditional execution to skip unnecessary tasks
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't modify critical files without validation
|
|
- Avoid command/shell when modules exist (lineinfile vs sed)
|
|
- Don't restart services directly in tasks (use handlers)
|
|
- Avoid hardcoded paths (use variables for OS differences)
|
|
|
|
## Naming Conventions
|
|
|
|
### Pattern: Descriptive Variable Names with Role Prefix
|
|
|
|
**Description:** Prefix all role variables with the role name to avoid conflicts with other roles or playbook variables.
|
|
|
|
**File Path:** `defaults/main.yml`
|
|
|
|
**Example Code:**
|
|
|
|
```yaml
|
|
---
|
|
security_ssh_port: 22
|
|
security_ssh_password_authentication: "no"
|
|
security_ssh_permit_root_login: "no"
|
|
security_ssh_usedns: "no"
|
|
security_ssh_permit_empty_password: "no"
|
|
security_ssh_challenge_response_auth: "no"
|
|
security_ssh_gss_api_authentication: "no"
|
|
security_ssh_x11_forwarding: "no"
|
|
security_sshd_state: started
|
|
security_ssh_restart_handler_state: restarted
|
|
security_ssh_allowed_users: []
|
|
security_ssh_allowed_groups: []
|
|
|
|
security_sudoers_passwordless: []
|
|
security_sudoers_passworded: []
|
|
|
|
security_autoupdate_enabled: true
|
|
security_autoupdate_blacklist: []
|
|
security_autoupdate_additional_origins: []
|
|
|
|
security_autoupdate_reboot: "false"
|
|
security_autoupdate_reboot_time: "03:00"
|
|
security_autoupdate_mail_to: ""
|
|
security_autoupdate_mail_on_error: true
|
|
|
|
security_fail2ban_enabled: true
|
|
security_fail2ban_custom_configuration_template: "jail.local.j2"
|
|
```
|
|
|
|
**Naming Pattern:**
|
|
|
|
```text
|
|
{role_name}_{feature}_{attribute}
|
|
```
|
|
|
|
Examples:
|
|
|
|
- `security_ssh_port` - Role: security, Feature: ssh, Attribute: port
|
|
- `security_fail2ban_enabled` - Role: security, Feature: fail2ban, Attribute: enabled
|
|
- `security_autoupdate_reboot_time` - Role: security, Feature: autoupdate, Attribute: reboot_time
|
|
|
|
**Key Elements:**
|
|
|
|
1. **Role prefix** - All variables start with "security_"
|
|
2. **Feature grouping** - Related variables have common prefix (security_ssh_, security_fail2ban_)
|
|
3. **Descriptive names** - Full words, not abbreviations
|
|
4. **Underscore separation** - snake_case, not camelCase
|
|
5. **Boolean as strings** - "yes"/"no" for SSH config (preserves YAML booleans elsewhere)
|
|
|
|
**When to Use:**
|
|
|
|
- Always prefix variables with role name
|
|
- Group related variables with feature prefix
|
|
- Use descriptive names (avoid abbreviations)
|
|
- Choose meaningful defaults
|
|
- Quote string values that look like booleans ("yes", "no", "true", "false")
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't use generic variable names (port, enabled, config_path)
|
|
- Avoid abbreviations (ssh_cfg instead of ssh_config)
|
|
- Don't mix naming styles (snake_case vs camelCase)
|
|
- Avoid unquoted yes/no/true/false strings (YAML interprets as booleans)
|
|
|
|
### Pattern: Task Naming Convention
|
|
|
|
**Description:** Write task names that are descriptive, actionable, and follow a consistent format.
|
|
|
|
**Task Name Pattern:**
|
|
|
|
```text
|
|
[Action verb] [object] [additional context]
|
|
```
|
|
|
|
Examples from the role:
|
|
|
|
- "Ensure SSH daemon is running" - State verification
|
|
- "Update SSH configuration to be more secure" - Modification action
|
|
- "Add configured users allowed to connect over ssh" - Addition action
|
|
- "Install fail2ban" - Installation action
|
|
|
|
**Guidelines:**
|
|
|
|
1. **Start with action verb** - Ensure, Update, Add, Install, Configure, Remove
|
|
2. **Be specific** - "SSH daemon" not just "daemon"
|
|
3. **Add context** - "to be more secure" explains why
|
|
4. **Use present tense** - "Ensure" not "Ensuring"
|
|
5. **Capitalize first word** - "Ensure SSH..." not "ensure ssh..."
|
|
|
|
**When to Use:**
|
|
|
|
- Every task should have a clear name
|
|
- Name describes the desired state, not the implementation
|
|
- Use consistent verbs across the role
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't use vague names ("Configure SSH", "Setup system")
|
|
- Avoid implementation details ("Run lineinfile on sshd_config")
|
|
- Don't use all caps or weird capitalization
|
|
|
|
## File Placement Decisions
|
|
|
|
### Pattern: defaults/ vs vars/ Usage
|
|
|
|
**Description:** Use defaults/ for user-overridable values and vars/ for internal/OS-specific values.
|
|
|
|
**File Paths:**
|
|
|
|
- `defaults/main.yml` - User-facing configuration
|
|
- `vars/Debian.yml` - Debian-specific internal values
|
|
- `vars/RedHat.yml` - RedHat-specific internal values
|
|
|
|
**defaults/main.yml Example:**
|
|
|
|
```yaml
|
|
---
|
|
# User-configurable values (low precedence)
|
|
security_ssh_port: 22
|
|
security_ssh_password_authentication: "no"
|
|
security_fail2ban_enabled: true
|
|
security_autoupdate_enabled: true
|
|
```
|
|
|
|
**vars/Debian.yml Example:**
|
|
|
|
```yaml
|
|
---
|
|
# Internal OS-specific values (high precedence)
|
|
security_ssh_config_path: /etc/ssh/sshd_config
|
|
security_sshd_name: ssh
|
|
```
|
|
|
|
**vars/RedHat.yml Example (inferred structure):**
|
|
|
|
```yaml
|
|
---
|
|
# Internal OS-specific values (high precedence)
|
|
security_ssh_config_path: /etc/ssh/sshd_config
|
|
security_sshd_name: sshd
|
|
```
|
|
|
|
**Decision Matrix:**
|
|
|
|
| Variable Type | Location | Precedence | Use Case |
|
|
|--------------|----------|------------|----------|
|
|
| User configuration | defaults/ | Low (easily overridden) | Settings users customize |
|
|
| OS-specific paths | vars/ | High (shouldn't override) | File paths, service names |
|
|
| Internal logic | vars/ | High | Values role needs to work |
|
|
| Feature toggles | defaults/ | Low | Enable/disable features |
|
|
|
|
**When to Use:**
|
|
|
|
- **defaults/** - Any value users might want to change
|
|
- **vars/** - OS-specific values, internal constants
|
|
- Load vars/ files conditionally by OS family
|
|
- Use include_vars to load appropriate vars file
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't put user-facing config in vars/ (can't be overridden easily)
|
|
- Don't put OS-specific paths in defaults/ (users shouldn't change)
|
|
- Avoid duplicating values between defaults/ and vars/
|
|
|
|
### Pattern: OS-Specific Variable Files
|
|
|
|
**Description:** Create separate variable files for each OS family to handle platform differences.
|
|
|
|
**File Path:** `vars/Debian.yml`, `vars/RedHat.yml`
|
|
|
|
**Loading Pattern:**
|
|
|
|
```yaml
|
|
- name: Include OS-specific variables.
|
|
include_vars: "{{ ansible_os_family }}.yml"
|
|
```
|
|
|
|
**Common OS-Specific Variables:**
|
|
|
|
- Service names (ssh vs sshd)
|
|
- Configuration file paths
|
|
- Package names
|
|
- Default directories
|
|
|
|
**When to Use:**
|
|
|
|
- Different service names across OS families
|
|
- Different file paths or package names
|
|
- OS-specific configuration options
|
|
- Load at start of tasks/main.yml
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't use when: conditionals for every OS difference
|
|
- Avoid complex variable resolution logic
|
|
- Don't hardcode OS-specific values in tasks
|
|
|
|
## Handler Organization
|
|
|
|
### Pattern: Simple Handler Definitions
|
|
|
|
**Description:** Define handlers with clear names and simple actions. Handlers should do one thing well.
|
|
|
|
**File Path:** `handlers/main.yml`
|
|
|
|
**Example Code:**
|
|
|
|
```yaml
|
|
---
|
|
- name: reload systemd
|
|
ansible.builtin.systemd_service:
|
|
daemon_reload: true
|
|
|
|
- name: restart ssh
|
|
ansible.builtin.service:
|
|
name: "{{ security_sshd_name }}"
|
|
state: "{{ security_ssh_restart_handler_state }}"
|
|
|
|
- name: reload fail2ban
|
|
ansible.builtin.service:
|
|
name: fail2ban
|
|
state: reloaded
|
|
```
|
|
|
|
**Key Elements:**
|
|
|
|
1. **Descriptive names** - Action + service (restart ssh, reload fail2ban)
|
|
2. **Single responsibility** - Each handler does one thing
|
|
3. **Configurable state** - Uses variable for restart/reload state
|
|
4. **Lowercase names** - "reload systemd" not "Reload Systemd"
|
|
5. **Service vs systemd_service** - Use appropriate module
|
|
|
|
**Handler Naming Pattern:**
|
|
|
|
```text
|
|
[action] [service/component]
|
|
```
|
|
|
|
Examples:
|
|
|
|
- "restart ssh" - Restart SSH service
|
|
- "reload systemd" - Reload systemd daemon
|
|
- "reload fail2ban" - Reload fail2ban configuration
|
|
|
|
**When to Use:**
|
|
|
|
- Create one handler per service/action combination
|
|
- Use simple, action-oriented names
|
|
- Make handler behavior configurable via variables
|
|
- Use reload instead of restart when possible (less disruptive)
|
|
|
|
**Anti-pattern:**
|
|
|
|
- Don't combine multiple actions in one handler
|
|
- Avoid complex logic in handlers
|
|
- Don't use handlers for non-idempotent actions
|
|
|
|
## Comparison to Virgo-Core Roles
|
|
|
|
### system_user Role
|
|
|
|
**Structure Analysis:**
|
|
|
|
```text
|
|
system_user/
|
|
├── defaults/
|
|
│ └── main.yml ✅
|
|
├── handlers/
|
|
│ └── main.yml (empty - appropriate, no services)
|
|
├── meta/
|
|
│ └── main.yml ✅
|
|
├── tasks/
|
|
│ └── main.yml ✅ (single file appropriate for scope)
|
|
├── templates/
|
|
│ └── sudoers.j2 ✅
|
|
└── README.md ✅
|
|
```
|
|
|
|
**Matches:**
|
|
|
|
- ✅ Proper defaults/ usage
|
|
- ✅ Appropriate task organization (role is simple enough for single file)
|
|
- ✅ Variable naming with role prefix (system_user_*)
|
|
- ✅ Clear task names
|
|
|
|
**Gaps:**
|
|
|
|
- ⚠️ No vars/ directory for OS-specific values (may not be needed)
|
|
- ❌ No molecule/ testing directory
|
|
- ❌ No .github/workflows/ for CI
|
|
|
|
**Priority Actions:**
|
|
|
|
1. **Nice-to-have:** Add vars/ files if supporting multiple OS families (30 min)
|
|
2. **Critical:** Add molecule/ directory (covered in testing-comprehensive.md)
|
|
|
|
### proxmox_access Role
|
|
|
|
**Structure Analysis:**
|
|
|
|
```text
|
|
proxmox_access/
|
|
├── defaults/
|
|
│ └── main.yml ✅
|
|
├── handlers/
|
|
│ └── main.yml ✅ (appropriate handlers defined)
|
|
├── meta/
|
|
│ └── main.yml ✅
|
|
├── tasks/
|
|
│ ├── main.yml ✅ (good routing pattern)
|
|
│ ├── roles.yml ✅
|
|
│ ├── groups.yml ✅
|
|
│ ├── users.yml ✅
|
|
│ ├── tokens.yml ✅
|
|
│ └── acls.yml ✅
|
|
├── templates/
|
|
│ └── terraform_env.sh.j2 ✅
|
|
└── README.md ✅
|
|
```
|
|
|
|
**Matches:**
|
|
|
|
- ✅ Excellent task organization (main.yml as router)
|
|
- ✅ Feature-based task files
|
|
- ✅ Proper variable naming (proxmox_access_*)
|
|
- ✅ Good handler usage
|
|
|
|
**Gaps:**
|
|
|
|
- ❌ No molecule/ testing directory
|
|
- ❌ No .github/workflows/ for CI
|
|
- ⚠️ No vars/ directory (but tasks include OS detection)
|
|
|
|
**Priority Actions:**
|
|
|
|
1. **Critical:** Add molecule/ directory (covered in testing-comprehensive.md)
|
|
2. **Nice-to-have:** Add vars/ files for Proxmox-specific paths (1 hour)
|
|
|
|
### proxmox_network Role
|
|
|
|
**Structure Analysis:**
|
|
|
|
```text
|
|
proxmox_network/
|
|
├── defaults/
|
|
│ └── main.yml ✅
|
|
├── handlers/
|
|
│ └── main.yml ✅ (network reload handler)
|
|
├── meta/
|
|
│ └── main.yml ✅
|
|
├── tasks/
|
|
│ ├── main.yml ✅
|
|
│ ├── bridges.yml ✅
|
|
│ ├── vlans.yml ✅
|
|
│ └── verify.yml ✅ (excellent - verification tasks)
|
|
└── README.md ✅
|
|
```
|
|
|
|
**Matches:**
|
|
|
|
- ✅ Good task organization
|
|
- ✅ Verification tasks (verify.yml) - advanced pattern
|
|
- ✅ Proper handlers for network changes
|
|
- ✅ Variable naming conventions
|
|
|
|
**Gaps:**
|
|
|
|
- ❌ No molecule/ testing directory
|
|
- ❌ No .github/workflows/ for CI
|
|
- ⚠️ No templates/ directory (uses lineinfile, which is fine)
|
|
|
|
**Priority Actions:**
|
|
|
|
1. **Critical:** Add molecule/ directory with network verification (covered in testing-comprehensive.md)
|
|
|
|
## Validation: geerlingguy.docker
|
|
|
|
**Analysis Date:** 2025-10-23
|
|
**Repository:** <https://github.com/geerlingguy/ansible-role-docker>
|
|
|
|
### Directory Organization
|
|
|
|
- **Pattern: Standard Ansible role structure** - ✅ **Confirmed**
|
|
- Docker role has: defaults/, tasks/, handlers/, meta/, molecule/, .github/, vars/
|
|
- No templates/ directory (docker uses copy module with content parameter)
|
|
- Confirms that omitting unused directories is correct pattern
|
|
|
|
### Task Organization
|
|
|
|
- **Pattern: tasks/main.yml as router** - ✅ **Confirmed**
|
|
- main.yml loads OS-specific vars, then includes setup-{RedHat,Suse,Debian}.yml
|
|
- Same conditional include pattern as security role
|
|
- **Observation:** Uses more advanced include_vars with first_found lookup (evolution of simple include_vars pattern)
|
|
|
|
- **Pattern: Feature-based task files** - ✅ **Confirmed**
|
|
- Tasks split by OS family: setup-RedHat.yml, setup-Suse.yml, setup-Debian.yml
|
|
- Additional feature files: docker-compose.yml, docker-users.yml
|
|
- Confirms pattern: Split by OS when logic differs, by feature when optional
|
|
|
|
### Variable Naming
|
|
|
|
- **Pattern: Role-prefixed variables** - ✅ **Confirmed**
|
|
- All variables prefixed with `docker_`: docker_edition, docker_packages, docker_service_state, etc.
|
|
- Confirms naming pattern is universal
|
|
|
|
- **Pattern: Feature grouping** - ✅ **Confirmed**
|
|
- docker_service_* for service management
|
|
- docker_compose_* for compose options
|
|
- docker_apt_* for Debian-specific vars
|
|
- docker_yum_* for RedHat-specific vars
|
|
|
|
### defaults/ vs vars/ Usage
|
|
|
|
- **Pattern: defaults/ for user config, vars/ for OS-specific** - ✅ **Confirmed**
|
|
- defaults/main.yml: All user-configurable options (packages, service state, repo URLs)
|
|
- vars/{RedHat,Debian,Suse}.yml: OS-specific package names and repo details
|
|
- Confirms this is standard practice across all roles
|
|
|
|
### Task Naming Convention
|
|
|
|
- **Pattern: Descriptive action verb + object** - ✅ **Confirmed**
|
|
- "Load OS-specific vars."
|
|
- "Install Docker packages."
|
|
- "Configure Docker daemon options."
|
|
- "Ensure Docker is started and enabled at boot."
|
|
- Same pattern as security/users roles
|
|
|
|
### Advanced Pattern: first_found Lookup
|
|
|
|
- **Pattern Evolution:** Docker role uses advanced vars loading:
|
|
|
|
```yaml
|
|
- name: Load OS-specific vars.
|
|
include_vars: "{{ lookup('first_found', params) }}"
|
|
vars:
|
|
params:
|
|
files:
|
|
- '{{ansible_facts.distribution}}.yml'
|
|
- '{{ansible_facts.os_family}}.yml'
|
|
- main.yml
|
|
paths:
|
|
- 'vars'
|
|
```
|
|
|
|
- **vs security simple pattern:** `include_vars: "{{ ansible_os_family }}.yml"`
|
|
- **Insight:** More complex roles use fallback chain for better distribution support
|
|
- **Recommendation:** Simple pattern for basic roles, first_found for complex multi-OS roles
|
|
|
|
### Key Validation Findings
|
|
|
|
**What Docker Role Confirms:**
|
|
|
|
1. ✅ Standard directory structure is universal
|
|
2. ✅ tasks/main.yml as router is standard
|
|
3. ✅ Role-prefixed variable naming is universal
|
|
4. ✅ defaults/ vs vars/ separation is universal
|
|
5. ✅ Feature grouping in variable names is universal
|
|
6. ✅ Descriptive task naming is universal
|
|
|
|
**What Docker Role Evolves:**
|
|
|
|
1. 🔄 Advanced include_vars with first_found lookup (better than simple include_vars)
|
|
2. 🔄 More OS-specific task files (RedHat, Suse, Debian vs just RedHat/Debian)
|
|
|
|
**Pattern Confidence After Docker Validation:**
|
|
|
|
- **Directory structure:** UNIVERSAL (3/3 roles follow)
|
|
- **Task organization:** UNIVERSAL (3/3 use main.yml as router)
|
|
- **Variable naming:** UNIVERSAL (3/3 use role prefix)
|
|
- **defaults/ vs vars/:** UNIVERSAL (3/3 follow pattern)
|
|
- **OS-specific vars loading:** EVOLVED (first_found is better than simple include)
|
|
|
|
## Validation: geerlingguy.postgresql
|
|
|
|
**Analysis Date:** 2025-10-23
|
|
**Repository:** <https://github.com/geerlingguy/ansible-role-postgresql>
|
|
|
|
### Directory Organization
|
|
|
|
- **Pattern: Standard Ansible role structure** - ✅ **Confirmed**
|
|
- PostgreSQL has: defaults/, tasks/, handlers/, meta/, molecule/, .github/, vars/, templates/
|
|
- Uses templates/ for pg_hba.conf and postgresql.conf (complex config files)
|
|
- **4/4 roles confirm standard structure**
|
|
|
|
### Task Organization
|
|
|
|
- **Pattern: tasks/main.yml as router** - ✅ **Confirmed**
|
|
- main.yml includes: variables.yml, setup-{Archlinux,Debian,RedHat}.yml, initialize.yml, configure.yml
|
|
- imports (not includes) users.yml, databases.yml, users_props.yml for execution order
|
|
- **Insight:** Uses `include_tasks` for conditional includes, `import_tasks` when order matters
|
|
- **4/4 roles use main.yml as router pattern**
|
|
|
|
- **Pattern: Feature-based task files** - ✅ **Confirmed**
|
|
- Tasks split by: OS (setup-*.yml), lifecycle (initialize.yml, configure.yml), entity (users.yml, databases.yml)
|
|
- More task files than simpler roles (8+ files vs 2-3)
|
|
- **Pattern scales:** Complex roles have more task files, organized by feature and OS
|
|
|
|
### Variable Naming
|
|
|
|
- **Pattern: Role-prefixed variables** - ✅ **Confirmed**
|
|
- All variables prefixed with `postgresql_`: postgresql_databases, postgresql_users, postgresql_hba_entries
|
|
- **4/4 roles confirm this is universal**
|
|
|
|
- **Pattern: Feature grouping** - ✅ **Confirmed**
|
|
- postgresql_global_config_* for server config
|
|
- postgresql_hba_* for authentication config
|
|
- postgresql_*_enabled for feature flags
|
|
- **Demonstrates:** Feature grouping works at scale (20+ variables)
|
|
|
|
### defaults/ vs vars/ Usage
|
|
|
|
- **Pattern: defaults/ for user config, vars/ for OS-specific** - ✅ **Confirmed**
|
|
- defaults/main.yml: Extensive user configuration (100+ lines with inline docs)
|
|
- vars/{Archlinux,Debian,RedHat}.yml: OS-specific package names, paths, versions
|
|
- **4/4 roles follow this pattern exactly**
|
|
|
|
### Task Naming Convention
|
|
|
|
- **Pattern: Descriptive action verb + object** - ✅ **Confirmed**
|
|
- "Ensure PostgreSQL Python libraries are installed."
|
|
- "Ensure PostgreSQL is started and enabled on boot."
|
|
- "Set PostgreSQL environment variables."
|
|
- **4/4 roles use identical naming pattern**
|
|
|
|
### Advanced Pattern: include_tasks vs import_tasks
|
|
|
|
- **Pattern Evolution:** PostgreSQL demonstrates when to use each:
|
|
|
|
```yaml
|
|
# Conditional loading - use include_tasks
|
|
- include_tasks: setup-Archlinux.yml
|
|
when: ansible_os_family == 'Archlinux'
|
|
|
|
# Ordered execution - use import_tasks
|
|
- import_tasks: users.yml
|
|
- import_tasks: databases.yml
|
|
- import_tasks: users_props.yml
|
|
```
|
|
|
|
- **New insight:** `include_tasks` = dynamic/conditional, `import_tasks` = static/ordered
|
|
- **Recommendation:** Use import when order matters, include when conditional
|
|
|
|
### Complex Variable Documentation Pattern
|
|
|
|
- **Pattern: Inline documentation in defaults/main.yml** - ✅ **EXCELLENT EXAMPLE**
|
|
- PostgreSQL defaults/ has extensive inline examples for complex structures:
|
|
|
|
```yaml
|
|
postgresql_databases: []
|
|
# - name: exampledb # required; the rest are optional
|
|
# lc_collate: # defaults to 'en_US.UTF-8'
|
|
# lc_ctype: # defaults to 'en_US.UTF-8'
|
|
# encoding: # defaults to 'UTF-8'
|
|
```
|
|
|
|
- **Validates:** Complex dict structures benefit from commented examples in defaults
|
|
- **Best practice:** Show all available keys, even optional ones
|
|
|
|
### Key Validation Findings
|
|
|
|
**What PostgreSQL Role Confirms:**
|
|
|
|
1. ✅ Standard directory structure is universal (4/4 roles)
|
|
2. ✅ tasks/main.yml as router is universal (4/4 roles)
|
|
3. ✅ Role-prefixed variable naming is universal (4/4 roles)
|
|
4. ✅ defaults/ vs vars/ separation is universal (4/4 roles)
|
|
5. ✅ Feature grouping in variable names scales well
|
|
6. ✅ Descriptive task naming is universal (4/4 roles)
|
|
|
|
**What PostgreSQL Role Demonstrates:**
|
|
|
|
1. 🔄 Complex roles have more task files (8+ vs 2-3 for simple roles)
|
|
2. 🔄 include_tasks vs import_tasks have distinct use cases
|
|
3. 🔄 Inline documentation in defaults/ is critical for complex variables
|
|
4. 🔄 templates/ directory becomes important for complex config files
|
|
|
|
**Pattern Confidence After PostgreSQL Validation (4/4 roles):**
|
|
|
|
- **Directory structure:** UNIVERSAL (4/4 roles identical)
|
|
- **Task organization:** UNIVERSAL (4/4 use main.yml as router)
|
|
- **Variable naming:** UNIVERSAL (4/4 use role prefix)
|
|
- **defaults/ vs vars/:** UNIVERSAL (4/4 follow pattern)
|
|
- **Task file count:** CONTEXTUAL (scales with complexity: 2-3 for simple, 8+ for complex)
|
|
- **include vs import:** CLARIFIED (conditional vs ordered)
|
|
|
|
## Validation: geerlingguy.nginx
|
|
|
|
**Analysis Date:** 2025-10-23
|
|
**Repository:** <https://github.com/geerlingguy/ansible-role-nginx>
|
|
|
|
### Directory Organization
|
|
|
|
- **Pattern: Standard Ansible role structure** - ✅ **Confirmed**
|
|
- nginx has: defaults/, tasks/, handlers/, meta/, molecule/, .github/, vars/, templates/
|
|
- **Heavily uses templates/** directory with 3 template files
|
|
- **5/5 roles confirm standard structure**
|
|
|
|
### Template Organization - ✨ NEW INSIGHT
|
|
|
|
- **Pattern: templates/ directory for complex configurations** - ✅ **CONFIRMED & EXPANDED**
|
|
- nginx uses templates/ extensively for configuration management:
|
|
- `nginx.conf.j2` - Main nginx configuration (extensive Jinja2 logic)
|
|
- `vhost.j2` - Virtual host configuration template
|
|
- `nginx.repo.j2` - Repository configuration template
|
|
- **Key insight:** Templates heavily use Jinja2 blocks for extensibility
|
|
|
|
- **Advanced Template Pattern: Jinja2 Block Inheritance**
|
|
- nginx.conf.j2 uses `{% block %}` for template extensibility:
|
|
|
|
```jinja2
|
|
{% block worker %}
|
|
worker_processes {{ nginx_worker_processes }};
|
|
{% endblock %}
|
|
|
|
{% block http_begin %}{% endblock %}
|
|
{% block http_basic %}...{% endblock %}
|
|
{% block http_gzip %}...{% endblock %}
|
|
{% block http_upstream %}...{% endblock %}
|
|
{% block http_includes %}...{% endblock %}
|
|
{% block http_end %}{% endblock %}
|
|
```
|
|
|
|
- Allows users to override specific template sections without replacing entire template
|
|
- README documents how to extend templates using Jinja2 inheritance
|
|
|
|
- **Template Customization Pattern:**
|
|
- Variables for template selection: `nginx_conf_template`, `nginx_vhost_template`
|
|
- Per-vhost template override: `item.template` in vhost definition
|
|
- Users can provide custom templates while falling back to role defaults
|
|
|
|
- **When to Use templates/ vs Other Approaches:**
|
|
- **Use templates/** when:
|
|
- Configuration files have complex structure (nginx.conf, vhost configs)
|
|
- Need conditional content generation
|
|
- Need Jinja2 block inheritance for user extensibility
|
|
- Configuration requires looping over variables (upstreams, vhosts)
|
|
- **Use lineinfile/copy** when:
|
|
- Simple single-line configuration changes (SSH config)
|
|
- Static files that don't need variable substitution
|
|
|
|
### Task Organization
|
|
|
|
- **Pattern: tasks/main.yml as router** - ✅ **Confirmed**
|
|
- main.yml includes: OS-specific setup files, vhosts.yml, main configuration
|
|
- Same conditional include pattern as other roles
|
|
- **5/5 roles use main.yml as router pattern**
|
|
|
|
- **Pattern: OS-specific task files** - ✅ **Confirmed**
|
|
- setup-RedHat.yml, setup-Ubuntu.yml, setup-Debian.yml, setup-FreeBSD.yml, etc.
|
|
- **nginx supports more OS families than previous roles** (FreeBSD, OpenBSD, Suse, Archlinux)
|
|
- Pattern scales to any number of supported platforms
|
|
|
|
### Variable Naming
|
|
|
|
- **Pattern: Role-prefixed variables** - ✅ **Confirmed**
|
|
- All variables prefixed with `nginx_`: nginx_worker_processes, nginx_vhosts, nginx_upstreams
|
|
- **5/5 roles confirm this is universal**
|
|
|
|
- **Pattern: Template path variables** - ✅ **NEW SUB-PATTERN**
|
|
- nginx exposes template paths as variables: `nginx_conf_template`, `nginx_vhost_template`
|
|
- Allows users to override templates without modifying role
|
|
- **Recommendation:** Always make template paths configurable in roles that use templates
|
|
|
|
### defaults/ vs vars/ Usage
|
|
|
|
- **Pattern: defaults/ for user config, vars/ for OS-specific** - ✅ **Confirmed**
|
|
- defaults/main.yml: Extensive user configuration (vhosts, upstreams, worker config)
|
|
- vars/{Debian,RedHat,FreeBSD,etc.}.yml: OS-specific package names, paths, service names
|
|
- **5/5 roles follow this pattern exactly**
|
|
|
|
### Complex Variable Documentation
|
|
|
|
- **Pattern: Inline documentation with examples** - ✅ **EXCELLENT EXAMPLE**
|
|
- nginx_vhosts documented with full example showing all options:
|
|
|
|
```yaml
|
|
nginx_vhosts: []
|
|
# Example vhost below, showing all available options:
|
|
# - listen: "80"
|
|
# server_name: "example.com"
|
|
# root: "/var/www/example.com"
|
|
# index: "index.html index.htm"
|
|
# filename: "example.com.conf"
|
|
# ...
|
|
```
|
|
|
|
- nginx_upstreams similar pattern with all load balancing options shown
|
|
- **Validates:** Complex list-of-dict variables need comprehensive inline examples
|
|
|
|
### Key Validation Findings
|
|
|
|
**What nginx Role Confirms:**
|
|
|
|
1. ✅ Standard directory structure is universal (5/5 roles)
|
|
2. ✅ tasks/main.yml as router is universal (5/5 roles)
|
|
3. ✅ Role-prefixed variable naming is universal (5/5 roles)
|
|
4. ✅ defaults/ vs vars/ separation is universal (5/5 roles)
|
|
5. ✅ Inline variable documentation is universal (5/5 roles)
|
|
6. ✅ OS-specific task organization is universal (5/5 roles)
|
|
|
|
**What nginx Role Demonstrates (✨ NEW INSIGHTS):**
|
|
|
|
1. ✨ **Template organization patterns:** Jinja2 blocks for extensibility
|
|
2. ✨ **Template customization:** Variables for template paths, per-item overrides
|
|
3. ✨ **README template documentation:** Explaining template inheritance
|
|
4. 🔄 Platform support scales: nginx supports 6+ OS families
|
|
5. 🔄 Complex variable documentation with full working examples
|
|
|
|
**Pattern Confidence After nginx Validation (5/5 roles):**
|
|
|
|
- **Directory structure:** UNIVERSAL (5/5 roles identical)
|
|
- **Task organization:** UNIVERSAL (5/5 use main.yml as router)
|
|
- **Variable naming:** UNIVERSAL (5/5 use role prefix)
|
|
- **defaults/ vs vars/:** UNIVERSAL (5/5 follow pattern)
|
|
- **Template organization:** VALIDATED (nginx shows advanced patterns)
|
|
- **Template extensibility:** BEST PRACTICE (Jinja2 blocks for inheritance)
|
|
- **Template path variables:** RECOMMENDED (allow user customization)
|
|
|
|
## Validation: geerlingguy.pip
|
|
|
|
**Analysis Date:** 2025-10-23
|
|
**Repository:** <https://github.com/geerlingguy/ansible-role-pip>
|
|
|
|
### Directory Structure
|
|
|
|
- **Pattern: Minimal role structure** - ✅ **Confirmed**
|
|
- pip has only essential directories: tasks/, defaults/, meta/, molecule/
|
|
- No templates/, handlers/, vars/, or files/ (not needed for this simple role)
|
|
- **Key finding:** Directory structure scales down appropriately for simple roles
|
|
|
|
### Task Organization
|
|
|
|
- **Pattern: Single file tasks** - ✅ **Confirmed**
|
|
- pip role has only tasks/main.yml with 3 tasks total
|
|
- No task splitting needed for minimal roles
|
|
- Each task still properly named and documented
|
|
- **Validates:** tasks/main.yml sufficient for simple roles
|
|
|
|
### Variable Management
|
|
|
|
- **Pattern: Minimal defaults** - ✅ **Confirmed**
|
|
- defaults/main.yml has only 3 variables: pip_package, pip_executable, pip_install_packages
|
|
- All variables properly prefixed with role name (pip_)
|
|
- Simple list structure for pip_install_packages with documented dict options
|
|
- **6/6 roles use role-prefixed variable naming**
|
|
|
|
### Key Validation Findings
|
|
|
|
**What pip Role Confirms:**
|
|
|
|
1. ✅ Directory structure scales appropriately (only include what's needed)
|
|
2. ✅ Single-file tasks acceptable for simple roles (3 tasks in main.yml)
|
|
3. ✅ Role-prefixed variable naming still universal (6/6 roles)
|
|
4. ✅ defaults/ still used even for minimal variables
|
|
5. ✅ No vars/ directory when all variables are user-configurable
|
|
|
|
**Pattern Confidence After pip Validation (6/6 roles):**
|
|
|
|
- **Directory structure:** UNIVERSAL (6/6 roles follow standard, scale appropriately)
|
|
- **Variable naming:** UNIVERSAL (6/6 use role prefix)
|
|
- **defaults/ for user config:** UNIVERSAL (6/6 roles)
|
|
- **Single-file tasks for simple roles:** VALIDATED (pip proves it's acceptable)
|
|
|
|
## Validation: geerlingguy.git
|
|
|
|
**Analysis Date:** 2025-10-23
|
|
**Repository:** <https://github.com/geerlingguy/ansible-role-git>
|
|
|
|
### Directory Structure
|
|
|
|
- **Pattern: Utility role structure** - ✅ **Confirmed**
|
|
- git has: tasks/, defaults/, vars/, meta/, molecule/
|
|
- Added vars/ for OS-specific package names
|
|
- Uses tasks/ for main + import pattern
|
|
- **Key finding:** vars/ appears when OS-specific data needed
|
|
|
|
### Task Organization
|
|
|
|
- **Pattern: Task file imports** - ✅ **Confirmed**
|
|
- git role uses tasks/main.yml as router (4 tasks)
|
|
- tasks/install-from-source.yml imported conditionally
|
|
- Conditional imports based on git_install_from_source flag
|
|
- **Validates:** import_tasks pattern for optional functionality
|
|
|
|
- **Pattern: OS-specific task blocks** - ✅ **Confirmed**
|
|
- Separate tasks for RedHat vs Debian families
|
|
- Conditional execution via ansible_os_family
|
|
- Package installation tasks specific to each OS family
|
|
- **7/7 roles handle OS differences with when conditions**
|
|
|
|
### Variable Management
|
|
|
|
- **Pattern: defaults/ vs vars/ split** - ✅ **Confirmed**
|
|
- defaults/main.yml: User-configurable options (workspace, version, install method)
|
|
- vars/: OS-specific package lists (git_packages for Debian vs RedHat)
|
|
- All variables still prefixed with role name (git_)
|
|
- **7/7 roles use role-prefixed variable naming**
|
|
|
|
- **Pattern: Boolean flags for features** - ✅ **Confirmed**
|
|
- git_install_from_source boolean controls installation method
|
|
- git_install_force_update boolean controls version updates
|
|
- Clear feature flags with sensible defaults
|
|
- **Validates:** Boolean flags for optional features pattern
|
|
|
|
### Key Validation Findings
|
|
|
|
**What git Role Confirms:**
|
|
|
|
1. ✅ vars/ directory for OS-specific non-configurable data (7/7 roles)
|
|
2. ✅ import_tasks for optional/complex functionality (7/7 roles)
|
|
3. ✅ OS-family conditional tasks universal (7/7 roles)
|
|
4. ✅ Boolean feature flags best practice (7/7 roles)
|
|
5. ✅ Task file splitting based on functionality not size
|
|
|
|
**Pattern Confidence After git Validation (7/7 roles):**
|
|
|
|
- **Directory structure:** UNIVERSAL (7/7 roles follow standard)
|
|
- **Task organization:** UNIVERSAL (7/7 use main.yml as router)
|
|
- **Variable naming:** UNIVERSAL (7/7 use role prefix)
|
|
- **defaults/ vs vars/:** UNIVERSAL (7/7 separate user config from OS data)
|
|
- **import_tasks pattern:** UNIVERSAL (7/7 use for complex/optional features)
|
|
- **OS-specific conditionals:** UNIVERSAL (7/7 handle multi-platform)
|
|
|
|
## Summary
|
|
|
|
**Universal Patterns Identified:**
|
|
|
|
1. Standard Ansible role directory structure
|
|
2. tasks/main.yml as router with include_tasks
|
|
3. Feature-based task file organization
|
|
4. Role-prefixed variable names (rolename_feature_attribute)
|
|
5. defaults/ for user config, vars/ for internal/OS-specific values
|
|
6. OS-specific variable files loaded dynamically
|
|
7. Simple, single-purpose handlers
|
|
8. Descriptive task names starting with action verbs
|
|
9. Configuration file validation before applying
|
|
|
|
**Key Takeaways:**
|
|
|
|
- Directory structure is standardized and well-understood
|
|
- Task organization improves maintainability
|
|
- Naming conventions prevent variable conflicts
|
|
- Proper defaults/ vs vars/ usage prevents confusion
|
|
- Handlers should be simple and focused
|
|
- Task files should be feature-based, not too granular
|
|
- Complex roles naturally have more task files (don't fight it)
|
|
- Inline documentation in defaults/ is critical for complex variables
|
|
|
|
**Next Steps:**
|
|
|
|
All three Virgo-Core roles follow good structure patterns. Primary gaps are testing infrastructure
|
|
(covered in testing-comprehensive.md) and CI/CD automation.
|