Skip to content

Latest commit

 

History

History
467 lines (329 loc) · 12.6 KB

File metadata and controls

467 lines (329 loc) · 12.6 KB

Manual E2E Test: Render Command

This guide provides step-by-step instructions for manually testing the render command and verifying that it generates identical artifacts to the provision command.

📋 Overview

The render command generates deployment artifacts without provisioning infrastructure. This allows you to:

  • Preview what will be deployed before committing to infrastructure provisioning
  • Generate artifacts for manual deployment
  • Inspect and validate configuration before provisioning
  • Compare artifacts between different configurations

Key Principle: The render command should generate identical artifacts to those created during provisioning, except for IP addresses (which come from real infrastructure during provision).

🎯 Test Objectives

This manual test verifies:

  1. Artifact Generation: render command successfully creates all deployment artifacts
  2. Artifact Completeness: All 8 service templates are rendered (OpenTofu, Ansible, Docker Compose, Tracker, Prometheus, Grafana, Caddy, Backup)
  3. Artifact Equivalence: Artifacts match those created by provision command (except IP addresses)
  4. Dual Input Modes: Both --env-name and --env-file modes work correctly
  5. Idempotency: Multiple render calls produce consistent results

📝 Test Prerequisites

  • LXD configured: Required for provision comparison (see LXD Setup)
  • Test SSH keys: Use fixtures/testing_rsa and fixtures/testing_rsa.pub
  • Clean workspace: No existing test environments
# Verify no existing test environment
cargo run -- list

# Check dependencies
cargo run --bin dependency-installer check

🔄 Test Workflow

Test 1: Render with Environment Name (Full Comparison)

This test compares render command output with actual provision command output.

Step 1: Create Environment

# Generate configuration template
cargo run -- create template --provider lxd envs/render-test.json

# Customize the template
nano envs/render-test.json

Required customizations:

{
  "environment": {
    "name": "render-test"
  },
  "ssh_credentials": {
    "private_key_path": "fixtures/testing_rsa",
    "public_key_path": "fixtures/testing_rsa.pub",
    "username": "torrust",
    "port": 22
  },
  "provider": {
    "provider": "lxd",
    "profile_name": "torrust-profile-render-test"
  }
}
# Create environment (state: Created)
cargo run -- create environment --env-file envs/render-test.json

Expected output:

✅ Environment 'render-test' created successfully!

State: Created

Verify environment state:

cargo run -- show render-test

Should show state: Created

Step 2: Render Artifacts

# Render artifacts with a test IP address to a preview directory
cargo run -- render --env-name render-test --instance-ip 192.168.1.100 --output-dir ./render-test-preview

Expected output:

⏳ [1/3] Validating input parameters...
⏳   ✓ Done (took Xms)
⏳ [2/3] Loading configuration...
⏳   ✓ Done (took Xms)
⏳ [3/3] Generating deployment artifacts...
⏳   ✓ Done (took Xms)
✅
Deployment artifacts generated successfully!

  Source: Environment: render-test
  Target IP: 192.168.1.100
  Output: ./render-test-preview

Next steps:
  - Review artifacts in the output directory
  - Use 'provision' command to deploy infrastructure
  - Or use artifacts manually with your deployment tools

Save the rendered artifacts location:

RENDER_PREVIEW_DIR="render-test-preview"

Step 3: Verify Rendered Artifacts

# List generated artifacts
ls -la $RENDER_PREVIEW_DIR/

# Should contain:
# - tofu/           - OpenTofu infrastructure code
# - ansible/        - Ansible playbooks and inventory
# - docker-compose/ - Docker Compose configuration
# - tracker/        - Tracker configuration
# - prometheus/     - Prometheus configuration
# - grafana/        - Grafana provisioning
# - caddy/          - Caddy reverse proxy config (if HTTPS enabled)
# - backup/         - Backup scripts (if enabled)

Check OpenTofu configuration:

cat $RENDER_PREVIEW_DIR/tofu/main.tf
# Should contain LXD VM configuration

Check Ansible inventory:

cat $RENDER_PREVIEW_DIR/ansible/inventory.yml
# Should contain: ansible_host: 192.168.1.100

Check Docker Compose:

cat $RENDER_PREVIEW_DIR/docker-compose/docker-compose.yml
# Should contain service definitions

Step 4: Provision Environment (For Comparison)

# Provision infrastructure - this will create a real VM
cargo run -- provision render-test

Expected output:

✅ Environment 'render-test' provisioned successfully!

  Instance IP: <ACTUAL_IP>
  SSH Port: 22
  Instance Name: render-test-<ID>

State: Provisioned

Save the actual IP for comparison:

# Get actual IP from show command
ACTUAL_IP=$(cargo run -- show render-test | grep "IP Address" | awk '{print $3}')
echo "Actual IP: $ACTUAL_IP"

Note: Provisioning will also generate artifacts in build/render-test/ with the actual IP.

Step 5: Compare Render vs Provision Artifacts

# After provision completes, compare render preview with provision output
# Preview directory: render-test-preview (from render command)
# Provision directory: build/render-test/ (from provision command)
diff -r $RENDER_PREVIEW_DIR build/render-test/

Expected differences:

  1. Ansible inventory - IP addresses:

    # Rendered version
    ansible_host: 192.168.1.100  # Test IP
    
    # Provisioned version
    ansible_host: 10.x.x.x       # Actual VM IP
  2. No other differences - All other files should be identical

Detailed comparison:

# Compare OpenTofu configurations (should be identical)
diff $RENDER_PREVIEW_DIR/tofu/main.tf build/render-test/tofu/main.tf

# Compare Docker Compose (should be identical)
diff $RENDER_PREVIEW_DIR/docker-compose/docker-compose.yml \
     build/render-test/docker-compose/docker-compose.yml

# Compare Ansible inventory (only IP should differ)
diff $RENDER_PREVIEW_DIR/ansible/inventory.yml \
     build/render-test/ansible/inventory.yml

Step 6: Test Idempotency

# Try to render again to the same output directory (should fail without --force)
cargo run -- render --env-name render-test --instance-ip 192.168.1.100 --output-dir ./render-test-preview

# Should fail with: "Output directory already exists"

# With --force flag, should succeed
cargo run -- render --env-name render-test --instance-ip 192.168.1.100 --output-dir ./render-test-preview --force

# Should succeed without errors
# Artifacts should remain unchanged

Step 7: Cleanup

# Destroy the infrastructure
cargo run -- destroy render-test

# Purge the environment data
cargo run -- purge render-test --force

# Remove preview artifacts
rm -rf $RENDER_PREVIEW_DIR

Test 2: Render with Configuration File (No Environment Creation)

This test verifies the --env-file mode which generates artifacts directly from a config file.

Step 1: Generate Configuration File

# Create config file (don't create environment)
cargo run -- create template --provider lxd envs/render-direct.json

# Customize configuration
nano envs/render-direct.json

Set environment name:

{
  "environment": {
    "name": "render-direct"
  }
}

Step 2: Render from Config File

# Render directly from config file (no environment creation)
cargo run -- render --env-file envs/render-direct.json --instance-ip 192.168.1.200 --output-dir ./render-direct-artifacts

Expected output:

✅
Deployment artifacts generated successfully!

  Source: Config file: envs/render-direct.json
  Target IP: 192.168.1.200
  Output: ./render-direct-artifacts

Step 3: Verify Artifacts

# Check artifacts were generated
ls -la render-direct-artifacts/

# Verify IP in Ansible inventory
grep "ansible_host" render-direct-artifacts/ansible/inventory.yml
# Should show: ansible_host: 192.168.1.200

Step 4: Verify No Environment Created

# List environments - render-direct should NOT exist
cargo run -- list

# Should NOT show 'render-direct' environment

Step 5: Cleanup

# Remove artifacts
rm -rf render-direct-artifacts

✅ Success Criteria

Test 1 (Environment Name Mode):

  • ✅ Environment created successfully
  • ✅ Render command succeeds with valid IP
  • ✅ All 8 service artifacts generated in build/render-test/
  • ✅ Provision command succeeds
  • ✅ Rendered artifacts match provisioned artifacts (except IP addresses)
  • ✅ Only Ansible inventory shows IP difference (192.168.1.100 vs actual IP)
  • ✅ Idempotent - multiple renders succeed without errors

Test 2 (Config File Mode):

  • ✅ Render from config file succeeds without creating environment
  • ✅ All artifacts generated in ./render-direct-artifacts/
  • ✅ IP address correctly set in Ansible inventory
  • ✅ Environment NOT created in data directory

🐛 Common Issues and Solutions

Issue: Command fails with "Environment not found"

Cause: Environment doesn't exist or is in wrong state

Solution:

# Check environment exists and is in Created state
cargo run -- show <env-name>

# If not in Created state, cannot render

Issue: Output directory already exists

Cause: Target output directory from previous render

Solution:

# Remove old directory
rm -rf ./my-output-dir

# Or use --force to overwrite
cargo run -- render --env-name test --instance-ip 192.168.1.100 --output-dir ./my-output-dir --force

Issue: IP validation error

Cause: Invalid IP format provided

Solution:

# Use valid IPv4 or IPv6 format
cargo run -- render --env-name test --instance-ip 192.168.1.100 --output-dir ./test-preview  # Valid
cargo run -- render --env-name test --instance-ip invalid-ip --output-dir ./test-preview     # Invalid

Issue: Render shows provisioned environment message

Cause: Environment already provisioned (not in Created state)

Expected behavior: Render command should inform you where existing artifacts are located

Solution: This is informational, not an error. The artifacts are already in build/<env-name>/ from the provision command.


📊 Verification Checklist

After completing both tests, verify:

Artifact Completeness

  • tofu/ directory contains infrastructure code
  • ansible/ directory contains playbooks and inventory
  • docker-compose/ directory contains service definitions
  • tracker/ directory contains tracker.toml
  • prometheus/ directory contains prometheus.yml
  • grafana/ directory contains provisioning configs
  • caddy/ directory exists (if HTTPS enabled)
  • backup/ directory exists (if backup enabled)

Artifact Equivalence

  • OpenTofu configs identical between render and provision
  • Docker Compose files identical between render and provision
  • Ansible playbooks identical between render and provision
  • Only Ansible inventory IP differs (test IP vs actual IP)
  • Tracker configuration identical
  • Prometheus configuration identical
  • Grafana provisioning identical

Command Behavior

  • Render succeeds with --env-name for Created environments
  • Render succeeds with --env-file without creating environment
  • Render is idempotent (can be called multiple times)
  • Render provides clear success messages
  • Render validates IP address format
  • Render fails gracefully for non-existent environments
  • Render fails gracefully for missing config files

🔗 Related Documentation


📝 Notes

  • IP Addresses: The render command requires an IP address because:

    • In Created state, infrastructure doesn't exist yet
    • With config file, no infrastructure is ever created
    • IP is needed for Ansible inventory generation
  • Output Directory: Artifacts are generated in the user-specified output directory (via --output-dir flag). This separates preview artifacts from deployment artifacts in build/<env-name>/.

  • State Independence: Render is a read-only operation - it never changes environment state

  • Artifact Parity: This test is critical for ensuring the render command is a true "preview" of what provision will generate