Skip to content

Commit 605b39e

Browse files
tosin2013claude
andcommitted
docs: Add kcli prefix documentation for developer awareness
Updates documentation to ensure developers use get_kcli_prefix() helper: CLAUDE.md: - Added new rule #6: "Always use get_kcli_prefix() for kcli commands" - Added detailed "kcli commands - IMPORTANT" section with example code - Documents QUBINODE_KCLI_SUDO env var and auto-detection behavior airflow/dags/templates/dag_template.py: - Added IMPORTANT section in module docstring about kcli prefix - Import get_kcli_prefix from dag_helpers - Define KCLI = get_kcli_prefix() at module level - Updated create_resources, delete_resources, status_resources tasks to use {KCLI}kcli pattern with f-strings This ensures future developers copying the template will automatically get the correct pattern for CI/CD compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 335bafd commit 605b39e

File tree

2 files changed

+72
-20
lines changed

2 files changed

+72
-20
lines changed

CLAUDE.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,34 @@ Families:
9292
1. ASCII-only log prefixes: `[OK]`, `[ERROR]`, `[WARN]`, `[INFO]`, `[SKIP]`
9393
1. No string concatenation in `bash_command`
9494
1. No Unicode/emoji in bash commands
95+
1. **Always use `get_kcli_prefix()` for kcli commands** (see below)
96+
97+
**kcli commands - IMPORTANT:**
98+
99+
kcli requires root/sudo privileges for VM operations. Use the helper from `dag_helpers.py`:
100+
101+
```python
102+
from dag_helpers import get_ssh_conn_id, get_kcli_prefix
103+
104+
# Get prefix once at module level (evaluated at DAG parse time)
105+
KCLI = get_kcli_prefix() # Returns "sudo " or "" based on environment
106+
107+
# Use in SSHOperator commands (f-string required)
108+
create_vm = SSHOperator(
109+
task_id="create_vm",
110+
ssh_conn_id=get_ssh_conn_id(),
111+
command=f"""
112+
{KCLI}kcli list vm
113+
{KCLI}kcli create vm myvm -i centos9stream
114+
{KCLI}kcli info vm myvm
115+
""",
116+
dag=dag,
117+
)
118+
```
119+
120+
The prefix is controlled by:
121+
- `QUBINODE_KCLI_SUDO=true|false` - Explicit control
122+
- Auto-detect: Uses sudo if `QUBINODE_SSH_USER` is not root
95123

96124
**SSH execution pattern (ADR-0046):**
97125

airflow/dags/templates/dag_template.py

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,35 @@
1111
1212
Author: [Your Name]
1313
Created: [Date]
14+
15+
IMPORTANT - kcli Commands:
16+
kcli requires root/sudo privileges for VM operations.
17+
Always use get_kcli_prefix() from dag_helpers.py:
18+
19+
from dag_helpers import get_kcli_prefix
20+
KCLI = get_kcli_prefix() # Returns "sudo " or "" based on environment
21+
22+
Then use f-strings in commands:
23+
command=f"{KCLI}kcli list vm"
1424
"""
1525

1626
from datetime import datetime, timedelta
1727
from airflow import DAG
1828
from airflow.operators.bash import BashOperator
1929
from airflow.operators.python import BranchPythonOperator, PythonOperator
2030

31+
# Import helpers for portable DAGs
32+
# See dag_helpers.py for documentation
33+
from dag_helpers import get_ssh_conn_id, get_kcli_prefix
34+
35+
# =============================================================================
36+
# kcli Prefix Configuration
37+
# =============================================================================
38+
# IMPORTANT: kcli requires root privileges. This prefix adds "sudo " when
39+
# running as a non-root user (e.g., in CI/CD pipelines).
40+
# Controlled by QUBINODE_KCLI_SUDO env var or auto-detected from SSH user.
41+
KCLI = get_kcli_prefix()
42+
2143
# =============================================================================
2244
# DAG Configuration
2345
# =============================================================================
@@ -186,36 +208,38 @@ def log_completion(**context) -> None:
186208

187209
# Task: Create resources
188210
# ADR-0046: Wrap kcli/virsh commands in SSH
211+
# NOTE: Uses KCLI prefix for sudo support in CI/CD environments
189212
create_resources = BashOperator(
190213
task_id="create_resources",
191-
bash_command="""
214+
bash_command=f"""
192215
echo "========================================"
193216
echo "Creating Resources"
194217
echo "========================================"
195218
196-
VM_NAME="{{ params.vm_name }}"
197-
VM_PROFILE="{{ params.vm_profile }}"
198-
TARGET_SERVER="{{ params.target_server }}"
219+
VM_NAME="{{{{ params.vm_name }}}}"
220+
VM_PROFILE="{{{{ params.vm_profile }}}}"
221+
TARGET_SERVER="{{{{ params.target_server }}}}"
199222
200223
# Generate VM name if not provided
201224
if [ -z "$VM_NAME" ]; then
202-
VM_NAME="${VM_PROFILE}-$(date +%s | md5sum | head -c 5)"
225+
VM_NAME="${{VM_PROFILE}}-$(date +%s | md5sum | head -c 5)"
203226
echo "[INFO] Generated VM name: $VM_NAME"
204227
fi
205228
206229
# ADR-0046: Execute kcli via SSH
230+
# Using KCLI prefix for sudo support when running as non-root
207231
echo "[INFO] Creating VM via kcli..."
208232
ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
209-
"kcli create vm -p ${VM_PROFILE} ${VM_NAME}" || {
233+
"{KCLI}kcli create vm -p ${{VM_PROFILE}} ${{VM_NAME}}" || {{
210234
echo "[ERROR] Failed to create VM"
211235
exit 1
212-
}
236+
}}
213237
214238
# Wait for VM to get IP
215239
echo "[INFO] Waiting for VM to get IP address..."
216-
for i in {1..30}; do
240+
for i in {{1..30}}; do
217241
VM_IP=$(ssh -o StrictHostKeyChecking=no root@localhost \
218-
"kcli info vm ${VM_NAME} 2>/dev/null | grep '^ip:' | awk '{print \\$2}' | head -1")
242+
"{KCLI}kcli info vm ${{VM_NAME}} 2>/dev/null | grep '^ip:' | awk '{{print \\$2}}' | head -1")
219243
if [ -n "$VM_IP" ]; then
220244
echo "[OK] VM IP: $VM_IP"
221245
break
@@ -239,19 +263,19 @@ def log_completion(**context) -> None:
239263
# Task: Delete resources
240264
delete_resources = BashOperator(
241265
task_id="delete_resources",
242-
bash_command="""
266+
bash_command=f"""
243267
echo "========================================"
244268
echo "Deleting Resources"
245269
echo "========================================"
246270
247-
VM_NAME="{{ params.vm_name }}"
248-
VM_PROFILE="{{ params.vm_profile }}"
271+
VM_NAME="{{{{ params.vm_name }}}}"
272+
VM_PROFILE="{{{{ params.vm_profile }}}}"
249273
250274
# Find VM by profile if name not provided
251275
if [ -z "$VM_NAME" ]; then
252276
echo "[INFO] Looking for VMs matching profile: $VM_PROFILE"
253277
VM_NAME=$(ssh -o StrictHostKeyChecking=no root@localhost \
254-
"kcli list vm 2>/dev/null | grep ${VM_PROFILE} | tail -1 | awk '{print \\$2}'" 2>/dev/null)
278+
"{KCLI}kcli list vm 2>/dev/null | grep ${{VM_PROFILE}} | tail -1 | awk '{{print \\$2}}'" 2>/dev/null)
255279
fi
256280
257281
if [ -z "$VM_NAME" ]; then
@@ -262,10 +286,10 @@ def log_completion(**context) -> None:
262286
# ADR-0046: Execute kcli via SSH
263287
echo "[INFO] Deleting VM: $VM_NAME"
264288
ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
265-
"kcli delete vm ${VM_NAME} -y" || {
289+
"{KCLI}kcli delete vm ${{VM_NAME}} -y" || {{
266290
echo "[ERROR] Failed to delete VM"
267291
exit 1
268-
}
292+
}}
269293
270294
echo ""
271295
echo "[OK] VM $VM_NAME deleted successfully"
@@ -276,25 +300,25 @@ def log_completion(**context) -> None:
276300
# Task: Check status
277301
status_resources = BashOperator(
278302
task_id="status_resources",
279-
bash_command="""
303+
bash_command=f"""
280304
echo "========================================"
281305
echo "Resource Status"
282306
echo "========================================"
283307
284-
VM_NAME="{{ params.vm_name }}"
285-
VM_PROFILE="{{ params.vm_profile }}"
308+
VM_NAME="{{{{ params.vm_name }}}}"
309+
VM_PROFILE="{{{{ params.vm_profile }}}}"
286310
287311
# List VMs matching profile
288312
echo "[INFO] VMs matching profile '$VM_PROFILE':"
289313
ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
290-
"kcli list vm 2>/dev/null | grep -E 'Name|${VM_PROFILE}'" || echo "No VMs found"
314+
"{KCLI}kcli list vm 2>/dev/null | grep -E 'Name|${{VM_PROFILE}}'" || echo "No VMs found"
291315
292316
# If specific VM name provided, show details
293317
if [ -n "$VM_NAME" ]; then
294318
echo ""
295319
echo "[INFO] Details for VM: $VM_NAME"
296320
ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
297-
"kcli info vm ${VM_NAME} 2>/dev/null" || echo "VM not found"
321+
"{KCLI}kcli info vm ${{VM_NAME}} 2>/dev/null" || echo "VM not found"
298322
fi
299323
300324
echo ""

0 commit comments

Comments
 (0)