Skip to content

Intent Testing Guide

This document covers testing the intent-based operations system and records the validation results.

Overview

Intents are declarative YAML files that define infrastructure operations with: - Policy checks: Prerequisites that must pass before execution - Confirmation: User approval for risky operations - Steps: Sequential commands to execute - Verification: Post-execution checks - Rollback: Automatic recovery on failure - Audit logging: Cryptographically-chained event log

Running Intent Tests

Single Intent Test

# Dry run (shows what would happen)
/root/tower-fleet/scripts/run-intent.sh <intent.yaml> --params key=value --dry-run

# Execute with confirmation
/root/tower-fleet/scripts/run-intent.sh <intent.yaml> --params key=value --confirm

Parameter Format

Parameters are space-separated key=value pairs:

# Correct
--params app=notes-app replicas=2

# Incorrect (comma-separated)
--params app=notes-app,replicas=2

Test Results (2024-12-30)

Tested against notes-app deployed to K8s cluster.

Passing Intents

Intent Risk Description Duration
create-nextjs-app high Scaffold new Next.js app with auth ~2 min
health-check read-only Check single app health <1s
health-check-all low Check all apps in namespace ~2s
check-logs read-only Retrieve pod logs <1s
observe-app read-only Full observability (pods, logs, metrics, events) ~1s
restart-app medium Rolling restart deployment ~12s
scale-app medium Scale replica count ~15s
rollback-app high Revert to previous revision ~27s
deploy-app high Full CI/CD pipeline ~2.5 min

Skipped Intents

Intent Reason
backup-app No Velero backups configured
restore-app No Velero backups configured
verify-backups No Velero backups configured
migrate-schema Would need pending migration file

Fixes Applied During Testing

1. Shell Escaping in Template Resolution

Problem: Template engine wraps parameter values in single quotes for shell safety. When YAML also had double quotes around ${params.*}, commands broke.

Example:

# Before (broken)
command: echo "${params.app}"
# Resolved to: echo "'notes-app'" (nested quotes)

# After (fixed)
command: |
  app=${params.app}
  echo "$app"

Files fixed: - /root/tower-fleet/intents/examples/create-nextjs-app.yaml - /root/tower-fleet/intents/examples/scale-app.yaml

2. Verify Check Quoting

Problem: In scale-app.yaml, the verify check compared shell variable $current with template-resolved ${params.replicas} which became '2' (quoted).

Before:

command: |
  current=$(kubectl get deployment ... -o jsonpath='{.status.availableReplicas}')
  [ "$current" = "${params.replicas}" ]
  # Comparison: "2" = "'2'" (fails)

After:

command: |
  replicas=${params.replicas}
  current=$(kubectl get deployment ... -o jsonpath='{.status.availableReplicas}')
  [ "$current" = "$replicas" ]
  # Comparison: "2" = "2" (passes)

3. Scaffold Script Dependencies

Problem: scaffold-nextjs.sh was missing dependencies required by shadcn components.

Fix: Added to npm install:

npm install ... class-variance-authority @radix-ui/react-slot

Problem: Layout template referenced wrong export name.

Fix: Changed DevBanner to DevAuthBanner.

Intent System Validation

State Machine

All states verified working:

received → policy_check → confirmation → executing → verifying → completed
                                                              ↘ failed

Features Verified

Feature Status Notes
Policy prereqs Commands checked before execution
Locking Prevents concurrent operations on same resource
Confirmation --confirm flag or interactive prompt
Step execution Sequential with timeout enforcement
Output capture stdout/stderr captured in audit log
Verification Post-execution checks run
Auto-rollback Triggered on verify failure
Audit logging JSONL with SHA-256 hash chain

Audit Log Example

# View audit log for specific request
cat /root/tower-fleet/logs/intents/2025-12-30/req_*.jsonl | jq .

Each event includes: - event_hash: SHA-256 of current event - prev_hash: Hash of previous event (chain integrity) - timestamp: UTC timestamp - outcome: success/failure status

Writing Testable Intents

Best Practices

  1. Use local variable assignment pattern:

    exec: |
      app=${params.app}
      ns=${params.namespace}
      kubectl get deployment "$app" -n "${ns:-$app}"
    

  2. Quote shell variables, not template variables:

    # Good
    command: |
      value=${params.value}
      echo "$value"
    
    # Bad
    command: echo "${params.value}"
    

  3. Provide sensible defaults:

    params:
      namespace: ""  # Defaults to app name in commands via ${ns:-${params.app}}
    

  4. Include meaningful verify checks:

    verify:
      - name: pods_ready
        type: exec
        command: |
          app=${params.app}
          kubectl get pods -l app="$app" -o jsonpath='{.items[*].status.phase}' | grep -q Running
    

Troubleshooting

Intent Fails on Prereq Check

# Run with verbose output
/root/tower-fleet/scripts/run-intent.sh intent.yaml --params ... -v

Check that: - Required parameters are provided - Resources exist (deployment, namespace) - kubectl/docker are accessible

Verify Check Fails After Successful Execution

Common causes: - Quoting mismatch (template vs shell variables) - Timing issue (resource not ready yet) - Wrong jsonpath selector

Debug by running verify command manually:

kubectl get deployment myapp -o jsonpath='{.status.availableReplicas}'

Lock Not Released

Locks auto-expire after TTL (default 900s). To manually release:

rm /root/tower-fleet/locks/intent-*.lock

See Also