init(opencode-ansible): scaffold dedicated Ansible repo for opencode propagation
This commit is contained in:
commit
991be3dd90
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# OpenCode Ansible
|
||||
|
||||
Dedicated Ansible orchestration repo for propagating opencode (oh-my-openagent) across the fleet.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
opencode-ansible/
|
||||
├── ansible.cfg # Ansible configuration
|
||||
├── inventory/
|
||||
│ ├── opencode.yml # Main inventory
|
||||
│ └── group_vars/
|
||||
│ └── opencode.yml # Group-wide defaults
|
||||
├── host_vars/ # Per-host overrides
|
||||
├── playbooks/ # Deployment playbooks
|
||||
├── roles/ # Ansible roles (if needed)
|
||||
├── templates/ # Jinja2 templates
|
||||
├── scripts/ # CLI wrapper scripts
|
||||
├── callback_plugins/ # Audit logging
|
||||
├── audit/ # JSONL audit trail
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Check readiness of a host
|
||||
ansible-playbook -i inventory/opencode.yml playbooks/readiness.yml --limit crew2
|
||||
|
||||
# Full deployment pipeline
|
||||
bash scripts/full-deploy.sh --limit crew2
|
||||
|
||||
# Verify deployment
|
||||
ansible-playbook -i inventory/opencode.yml playbooks/verify.yml --limit crew2
|
||||
```
|
||||
|
||||
## Playbooks
|
||||
|
||||
| Playbook | Purpose |
|
||||
|----------|---------|
|
||||
| `readiness.yml` | Pre-install host assessment |
|
||||
| `bootstrap.yml` | Install prerequisites and service accounts |
|
||||
| `install.yml` | Install opencode binary/runtime |
|
||||
| `config.yml` | Deploy configuration and sync repos |
|
||||
| `service.yml` | Deploy and enable systemd services |
|
||||
| `verify.yml` | Post-deployment health checks |
|
||||
| `rollback.yml` | Revert opencode deployment |
|
||||
|
||||
## Target Hosts
|
||||
|
||||
crew2, crewsupport, llm01, erpnext, poweredget150, a10, noc, kenny-GE76-Raider-11UE
|
||||
33
ansible.cfg
Normal file
33
ansible.cfg
Normal file
@ -0,0 +1,33 @@
|
||||
[defaults]
|
||||
# Inventory
|
||||
inventory = inventory/opencode.yml
|
||||
|
||||
# Connection
|
||||
host_key_checking = False
|
||||
timeout = 30
|
||||
|
||||
# Output
|
||||
stdout_callback = yaml
|
||||
bin_ansible_callbacks = True
|
||||
|
||||
# Callback plugins (audit logging)
|
||||
callback_plugins = callback_plugins
|
||||
callbacks_enabled = audit_log
|
||||
|
||||
# Retry files — disable, use audit log instead
|
||||
retry_files_enabled = False
|
||||
|
||||
# Forks — fleet is small, keep low
|
||||
forks = 5
|
||||
|
||||
# Logging (in addition to audit callback)
|
||||
log_path = audit/ansible.log
|
||||
|
||||
# Vault — reference existing netman vault password file
|
||||
vault_password_file = /home/kenny/nc/ansible/.ansible/vault_pass.txt
|
||||
|
||||
[privilege_escalation]
|
||||
become = False
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
0
audit/.gitkeep
Normal file
0
audit/.gitkeep
Normal file
107
callback_plugins/audit_log.py
Normal file
107
callback_plugins/audit_log.py
Normal file
@ -0,0 +1,107 @@
|
||||
# audit_log.py — Ansible callback plugin for fleet-ops audit trail
|
||||
# Logs each playbook run to /audit/<date>-<playbook>.jsonl
|
||||
# Format: one JSON object per line (JSONL), one file per playbook per day
|
||||
|
||||
from __future__ import annotations
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
name: audit_log
|
||||
type: notification
|
||||
short_description: Write structured audit log to /audit/
|
||||
description:
|
||||
- Appends a JSON record for each playbook run to /audit/<date>-<playbook>.jsonl
|
||||
- Fields: timestamp, completed, playbook, check_mode, outcome, summary, tasks
|
||||
options: {}
|
||||
"""
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = "notification"
|
||||
CALLBACK_NAME = "audit_log"
|
||||
CALLBACK_NEEDS_WHITELIST = True
|
||||
|
||||
AUDIT_DIR = "/audit"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._playbook_name = "unknown"
|
||||
self._start_time = None
|
||||
self._check_mode = False
|
||||
self._task_results: list[dict] = []
|
||||
|
||||
def v2_playbook_on_start(self, playbook):
|
||||
self._playbook_name = os.path.basename(playbook._file_name)
|
||||
self._start_time = datetime.now(timezone.utc).isoformat()
|
||||
self._task_results = []
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
self._check_mode = play.check_mode
|
||||
|
||||
def _record(self, result, status: str):
|
||||
entry = {
|
||||
"host": result._host.name,
|
||||
"task": result._task.get_name(),
|
||||
"status": status,
|
||||
}
|
||||
if status == "failed":
|
||||
entry["msg"] = str(result._result.get("msg", ""))
|
||||
self._task_results.append(entry)
|
||||
|
||||
def v2_runner_on_ok(self, result):
|
||||
self._record(result, "ok")
|
||||
|
||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||
self._record(result, "failed" if not ignore_errors else "ignored")
|
||||
|
||||
def v2_runner_on_unreachable(self, result):
|
||||
self._record(result, "unreachable")
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
self._record(result, "skipped")
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
end_time = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
summary = {}
|
||||
for host in stats.processed:
|
||||
summary[host] = {
|
||||
"ok": stats.ok.get(host, 0),
|
||||
"changed": stats.changed.get(host, 0),
|
||||
"failed": stats.failures.get(host, 0),
|
||||
"unreachable": stats.dark.get(host, 0),
|
||||
"skipped": stats.skipped.get(host, 0),
|
||||
}
|
||||
|
||||
any_failed = any(
|
||||
v["failed"] > 0 or v["unreachable"] > 0 for v in summary.values()
|
||||
)
|
||||
outcome = "failed" if any_failed else "success"
|
||||
if self._check_mode:
|
||||
outcome = f"check-{outcome}"
|
||||
|
||||
log_entry = {
|
||||
"timestamp": self._start_time,
|
||||
"completed": end_time,
|
||||
"playbook": self._playbook_name,
|
||||
"check_mode": self._check_mode,
|
||||
"outcome": outcome,
|
||||
"summary": summary,
|
||||
"tasks": self._task_results,
|
||||
}
|
||||
|
||||
os.makedirs(self.AUDIT_DIR, exist_ok=True)
|
||||
date_str = (self._start_time or end_time)[:10]
|
||||
playbook_stem = self._playbook_name.replace(".yml", "").replace(".yaml", "")
|
||||
log_file = os.path.join(self.AUDIT_DIR, f"{date_str}-{playbook_stem}.jsonl")
|
||||
|
||||
try:
|
||||
with open(log_file, "a") as f:
|
||||
f.write(json.dumps(log_entry) + "\n")
|
||||
except OSError as e:
|
||||
self._display.warning(f"audit_log: could not write to {log_file}: {e}")
|
||||
0
docs/.gitkeep
Normal file
0
docs/.gitkeep
Normal file
0
host_vars/.gitkeep
Normal file
0
host_vars/.gitkeep
Normal file
3
inventory/group_vars/opencode.yml
Normal file
3
inventory/group_vars/opencode.yml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
# OpenCode Propagation: Group-wide Defaults
|
||||
# TODO: Implement in Task 2
|
||||
3
inventory/opencode.yml
Normal file
3
inventory/opencode.yml
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
# OpenCode Propagation: Main Inventory
|
||||
# TODO: Implement in Task 2
|
||||
4
playbooks/bootstrap.yml
Normal file
4
playbooks/bootstrap.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Bootstrap Prerequisites
|
||||
# Installs prerequisites (Python, Node.js, service accounts, etc.)
|
||||
# TODO: Implement in Task 8
|
||||
4
playbooks/config.yml
Normal file
4
playbooks/config.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Config and Repo Sync
|
||||
# Deploys configuration and syncs Gitea repos on target hosts
|
||||
# TODO: Implement in Task 10
|
||||
4
playbooks/install.yml
Normal file
4
playbooks/install.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Install OpenCode
|
||||
# Installs opencode binary/runtime on target hosts
|
||||
# TODO: Implement in Task 9
|
||||
4
playbooks/readiness.yml
Normal file
4
playbooks/readiness.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Readiness Assessment
|
||||
# Verifies target hosts are ready for opencode installation
|
||||
# TODO: Implement in Task 7
|
||||
5
playbooks/rollback.yml
Normal file
5
playbooks/rollback.yml
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
# OpenCode Propagation: Rollback
|
||||
# Reverts opencode deployment to clean state
|
||||
# Requires --extra-vars "confirm_rollback=yes" to run
|
||||
# TODO: Implement in Task 15
|
||||
4
playbooks/service.yml
Normal file
4
playbooks/service.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Service Deployment
|
||||
# Deploys and enables systemd user services for opencode
|
||||
# TODO: Implement in Task 11
|
||||
4
playbooks/verify.yml
Normal file
4
playbooks/verify.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
# OpenCode Propagation: Verification and Health Checks
|
||||
# Post-deployment verification and health checks
|
||||
# TODO: Implement in Task 12
|
||||
0
roles/.gitkeep
Normal file
0
roles/.gitkeep
Normal file
0
scripts/.gitkeep
Normal file
0
scripts/.gitkeep
Normal file
0
templates/.gitkeep
Normal file
0
templates/.gitkeep
Normal file
Loading…
x
Reference in New Issue
Block a user