Initial commit — OpenClaw VM infrastructure
- ansible/: VM provisioning playbooks and roles - provision-vm.yml: create KVM VM from Ubuntu cloud image - install.yml: install OpenClaw on guest (upstream) - customize.yml: swappiness, virtiofs fstab, linger - roles/vm/: libvirt domain XML, cloud-init templates - inventory.yml + host_vars/zap.yml: zap instance config - backup-openclaw-vm.sh: daily rsync + MinIO upload - restore-openclaw-vm.sh: full redeploy from scratch - README.md: full operational documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
29
ansible/tests/Dockerfile.ubuntu2404
Normal file
29
ansible/tests/Dockerfile.ubuntu2404
Normal file
@@ -0,0 +1,29 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install Ansible and dependencies
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ansible \
|
||||
python3 \
|
||||
python3-apt \
|
||||
sudo \
|
||||
systemd \
|
||||
git \
|
||||
curl \
|
||||
ca-certificates \
|
||||
acl \
|
||||
gpg \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy project into container
|
||||
COPY . /opt/ansible
|
||||
WORKDIR /opt/ansible
|
||||
|
||||
# Install Ansible Galaxy collections
|
||||
RUN ansible-galaxy collection install -r requirements.yml
|
||||
|
||||
# Default: run the test entrypoint
|
||||
ENTRYPOINT ["bash", "tests/entrypoint.sh"]
|
||||
68
ansible/tests/README.md
Normal file
68
ansible/tests/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Docker CI Test Harness
|
||||
|
||||
This directory contains a Docker-based CI test harness for the Ansible playbook. It validates convergence, correctness, and idempotency by running the playbook inside an Ubuntu 24.04 container.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
bash tests/run-tests.sh
|
||||
|
||||
# Or specify a distro (currently only ubuntu2404 available)
|
||||
bash tests/run-tests.sh ubuntu2404
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
The test harness runs three sequential tests:
|
||||
|
||||
1. **Convergence**: Runs the playbook with `ci_test=true` to verify it completes without errors
|
||||
2. **Verification**: Runs `verify.yml` to assert the system is in the expected state
|
||||
3. **Idempotency**: Runs the playbook a second time and verifies `changed=0`
|
||||
|
||||
## Files
|
||||
|
||||
- `Dockerfile.ubuntu2404` - Ubuntu 24.04 container with Ansible pre-installed
|
||||
- `entrypoint.sh` - Test execution script (convergence → verification → idempotency)
|
||||
- `verify.yml` - Post-convergence assertions (user exists, packages installed, directories created, etc.)
|
||||
- `run-tests.sh` - Local test runner script
|
||||
|
||||
## CI Test Mode
|
||||
|
||||
The `ci_test` variable skips tasks that require:
|
||||
- Docker-in-Docker (Docker CE installation)
|
||||
- Kernel access (UFW/iptables firewall)
|
||||
- systemd services (loginctl, daemon installation)
|
||||
- External package installation (openclaw app install)
|
||||
|
||||
Everything else runs normally: package installation, user creation, Node.js/pnpm setup, directory structure, config file rendering, etc.
|
||||
|
||||
## What Gets Tested
|
||||
|
||||
| Component | Tested? | Notes |
|
||||
|-----------|---------|-------|
|
||||
| System packages (35+) | ✅ Yes | Full apt install |
|
||||
| User creation + config | ✅ Yes | User, .bashrc, sudoers, SSH dir |
|
||||
| Node.js + pnpm | ✅ Yes | Full install + version check |
|
||||
| Directory structure | ✅ Yes | All .openclaw/* dirs with perms |
|
||||
| Git global config | ✅ Yes | Aliases, default branch |
|
||||
| Vim config | ✅ Yes | Template rendering |
|
||||
| Docker CE install | ❌ No | Needs Docker-in-Docker |
|
||||
| UFW / iptables | ❌ No | Needs kernel access |
|
||||
| fail2ban / systemd | ❌ No | Needs running systemd |
|
||||
| Tailscale | ❌ No | Disabled by default already |
|
||||
| OpenClaw app install | ❌ No | External package |
|
||||
| Idempotency | ✅ Yes | Second run must have 0 changes |
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - All tests passed
|
||||
- `1` - Test failure (convergence failed, verification failed, or idempotency check failed)
|
||||
|
||||
## Development
|
||||
|
||||
To add tests for additional distributions:
|
||||
1. Create `Dockerfile.<distro>` (e.g., `Dockerfile.debian12`)
|
||||
2. Run: `bash tests/run-tests.sh <distro>`
|
||||
|
||||
The test harness automatically builds the image and runs the test suite.
|
||||
30
ansible/tests/entrypoint.sh
Executable file
30
ansible/tests/entrypoint.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PLAYBOOK_ARGS=(-e ci_test=true -e ansible_become=false --connection=local)
|
||||
|
||||
# --- Step 1: Convergence ---
|
||||
echo "===> Step 1: Convergence test"
|
||||
ansible-playbook playbook.yml "${PLAYBOOK_ARGS[@]}"
|
||||
echo "===> Convergence: PASSED"
|
||||
|
||||
# --- Step 2: Verification ---
|
||||
echo "===> Step 2: Verification"
|
||||
ansible-playbook tests/verify.yml "${PLAYBOOK_ARGS[@]}"
|
||||
echo "===> Verification: PASSED"
|
||||
|
||||
# --- Step 3: Idempotency ---
|
||||
echo "===> Step 3: Idempotency test"
|
||||
IDEMPOTENCY_OUT=$(ansible-playbook playbook.yml "${PLAYBOOK_ARGS[@]}" 2>&1)
|
||||
echo "$IDEMPOTENCY_OUT"
|
||||
|
||||
CHANGED=$(echo "$IDEMPOTENCY_OUT" | tail -n 5 | grep -oP 'changed=\K[0-9]+' | head -1)
|
||||
if [ "${CHANGED:-1}" -eq 0 ]; then
|
||||
echo "===> Idempotency: PASSED (0 changed)"
|
||||
else
|
||||
echo "===> Idempotency: FAILED (changed=$CHANGED)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "===> All tests passed"
|
||||
11
ansible/tests/run-tests.sh
Executable file
11
ansible/tests/run-tests.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DISTRO="${1:-ubuntu2404}"
|
||||
IMAGE="openclaw-ansible-test:${DISTRO}"
|
||||
|
||||
echo "Building test image (${DISTRO})..."
|
||||
docker build -t "$IMAGE" -f "tests/Dockerfile.${DISTRO}" .
|
||||
|
||||
echo "Running tests..."
|
||||
docker run --rm "$IMAGE"
|
||||
76
ansible/tests/verify.yml
Normal file
76
ansible/tests/verify.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
- name: Verify playbook results
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
openclaw_user: openclaw
|
||||
openclaw_home: /home/openclaw
|
||||
|
||||
tasks:
|
||||
- name: Verify openclaw user exists
|
||||
ansible.builtin.command: "id {{ openclaw_user }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Verify critical packages installed
|
||||
ansible.builtin.command: "dpkg -s {{ item }}"
|
||||
loop: [git, curl, vim, jq, tmux, tree, htop]
|
||||
changed_when: false
|
||||
|
||||
- name: Verify Node.js installed
|
||||
ansible.builtin.command: node --version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify pnpm installed
|
||||
ansible.builtin.command: pnpm --version
|
||||
changed_when: false
|
||||
|
||||
- name: Verify openclaw directory structure
|
||||
ansible.builtin.stat:
|
||||
path: "{{ item.path }}"
|
||||
loop:
|
||||
- { path: "{{ openclaw_home }}/.openclaw", mode: "0755" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/sessions" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/credentials", mode: "0700" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/data" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/logs" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/agents" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/agents/main" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/agents/main/agent", mode: "0700" }
|
||||
- { path: "{{ openclaw_home }}/.openclaw/workspace" }
|
||||
- { path: "{{ openclaw_home }}/.ssh", mode: "0700" }
|
||||
register: dir_checks
|
||||
|
||||
- name: Assert directories exist
|
||||
ansible.builtin.assert:
|
||||
that: item.stat.exists and item.stat.isdir
|
||||
fail_msg: "Directory missing: {{ item.item.path }}"
|
||||
loop: "{{ dir_checks.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.path }}"
|
||||
|
||||
- name: Assert restricted directories have correct permissions
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- dir_checks.results[2].stat.mode == '0700'
|
||||
- dir_checks.results[7].stat.mode == '0700'
|
||||
fail_msg: "credentials and agents/main/agent dirs should be 0700"
|
||||
|
||||
- name: Verify sudoers file exists and is valid
|
||||
ansible.builtin.command: "visudo -cf /etc/sudoers.d/{{ openclaw_user }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Verify global vim config exists
|
||||
ansible.builtin.stat:
|
||||
path: /etc/vim/vimrc.local
|
||||
register: vimrc
|
||||
- ansible.builtin.assert:
|
||||
that: vimrc.stat.exists
|
||||
|
||||
- name: Verify git global config
|
||||
ansible.builtin.command: git config --global init.defaultBranch
|
||||
changed_when: false
|
||||
register: git_branch
|
||||
- ansible.builtin.assert:
|
||||
that: git_branch.stdout == 'main'
|
||||
Reference in New Issue
Block a user