# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is Ansible playbook for installing, upgrading, and monitoring the Perforce Helix Core Server Deployment Package (SDP). `README.md` is the user-facing reference for variables, server types, secrets handling, and the helper-script catalog — consult it before adding new variables or roles. ## This repo is in Perforce, not git The workspace is at `//guest/russell_jackson/ansible-sdp/`. Files on disk are read-only until opened with `p4 edit`. To commit: ``` p4 edit p4 add p4 submit -d "message" ``` `p4 status` reports files needing reconcile. Do not reflexively run `git` — there is no git remote. ## Run convention Every helper script wraps the same `ansible-playbook main-playbook.yml --extra-vars '{"":true}' --vault-password-file password.txt --limit $1` invocation. The script name maps 1:1 to a single flow-control flag (`install.sh` → `install_perforce`, `upgrade.sh` → `update_perforce`, `sudo.sh` → `update_sudo`, etc. — see README). Targets are server or group names from `inventories/p4-sdp-install.yml`. Always run `./ping.sh ` first for a connectivity check. ## Architecture — two-phase playbook `main-playbook.yml` is intentionally bifurcated: 1. **Roles section** — runs the entire `perforce-sdp-install` role when `install_perforce: true`. The role's `tasks/main.yml` includes `dependencies.yml` (if `update_dependencies`), then `install.yml` (if `new_sdp`), then `cron.yml` unconditionally, then drops the replica SSH key. So `./install.sh` is heavyweight and idempotent. 2. **Tasks section** — uses `import_role tasks_from: ` to surgically run a single task file (`update.yml`, `dependencies.yml`, `cron.yml`, `sudo.yml`, `cert.yml`) gated by its own `update_*` flag. The non-`install.sh` helper scripts go through this path. When adding a new operation, decide which phase it belongs in. New "full install" steps go in the role; new "surgical update" operations get their own task file + flow flag + helper script. ## Variable layering (lowest to highest precedence) 1. `roles/perforce-sdp-install/defaults/main.yml` — package lists, clean commands, defaults like `install_broker: false`. 2. `inventories/group_vars/central/main.yml` — fleet-wide: Perforce version, admin user, domain, `commit_dns`, `install_broker: true`. Its own header notes that vars here cannot be overridden by *group* vars elsewhere — only by host_vars. 3. `inventories/host_vars/.yml` — per-server: DNS name, `perforce_server_type`, volume paths, and any per-host connection overrides (`ansible_user`, `ansible_ssh_private_key_file`, `ansible_become`). 4. `--extra-vars` from the helper scripts — flow flags only. `ansible.cfg` defines a fleet-wide `remote_user` and `private_key_file`; one-off hosts (e.g. AWS test boxes with a `.pem`) override these in their host_vars rather than editing `ansible.cfg`. ## Things that bite - **Volume vars are path *fragments*, not paths.** `perforce_online_metadata_volume: "mnt/p4meta"` — no leading slash. The role adds the slash. The directories must already exist (mounted EBS, NFS, or plain dirs for test rigs) — the role does not create them. - **Placeholder files in `roles/perforce-sdp-install/files/`** (`perforce-license`, `certificate.txt`, `privatekey.txt`, `id_rsa`) are committed dummies. For test hosts, set `copy_license: false` and `install_broker: false` in host_vars so the install doesn't try to use them. Real values must be dropped in for any real deployment. - **`id_rsa` is supposed to be vault-encrypted** per README, but in this checkout it's plaintext. `tasks/main.yml` copies it with `decrypt: yes`, which is a no-op on unencrypted content so the install works either way. If you re-encrypt it, the existing `password.txt` must decrypt it. - **`perforce_server_type` drives cron content.** `cron.yml` selects job sets by type (`p4d_master`, `p4d_edge`, `p4d_replica`, `p4d_standby`, `p4d_edgerep`, `p4broker`, `p4proxy`). Picking the wrong type silently installs the wrong maintenance jobs. - **Ubuntu 26.04+ has no `/etc/sysctl.conf`.** The sysctl tuning block writes to `/etc/sysctl.d/99-perforce.conf` (drop-in) and `restart_sysctl` runs `sysctl --system`. Follow the same drop-in pattern for any new system-tuning tasks. - **Sudoers files use a `Cmnd_Alias`** (see `templates/sudoers_perforce_user.j2`) — modern sudo rejects `*` in command arguments, so enumerate full commands explicitly and validate via `visudo -cf` in the task's `validate:`. - **`install.sh` is idempotent.** When a task fails mid-run, fix the task and re-run the same script — earlier steps will be no-ops or skip cleanly.