torrust-tracker-deployer 0.1.0

Torrust Tracker Deployer - Deployment Infrastructure with Ansible and OpenTofu
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# 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]../README.md)
- **Test SSH keys**: Use `fixtures/testing_rsa` and `fixtures/testing_rsa.pub`
- **Clean workspace**: No existing test environments

```bash
# 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

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

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

**Required customizations**:

```json
{
  "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"
  }
}
```

```bash
# Create environment (state: Created)
cargo run -- create environment --env-file envs/render-test.json
```

**Expected output**:

```text
✅ Environment 'render-test' created successfully!

State: Created
```

**Verify environment state**:

```bash
cargo run -- show render-test
```

Should show state: `Created`

#### Step 2: Render Artifacts

```bash
# 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**:

```text
⏳ [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**:

```bash
RENDER_PREVIEW_DIR="render-test-preview"
```

#### Step 3: Verify Rendered Artifacts

```bash
# 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**:

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

**Check Ansible inventory**:

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

**Check Docker Compose**:

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

#### Step 4: Provision Environment (For Comparison)

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

**Expected output**:

```text
✅ 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**:

```bash
# 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

```bash
# 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:

   ```bash
   # 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**:

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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**:

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

#### Step 2: Render from Config File

```bash
# 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**:

```text
✅
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

```bash
# 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

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

# Should NOT show 'render-direct' environment
```

#### Step 5: Cleanup

```bash
# Remove artifacts
rm -rf render-direct-artifacts
```

---

## ✅ Success Criteria

**Test 1 (Environment Name Mode)**:

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

**Test 2 (Config File Mode)**:

- [x] ✅ Render from config file succeeds without creating environment
- [x] ✅ All artifacts generated in `./render-direct-artifacts/`
- [x] ✅ IP address correctly set in Ansible inventory
- [x] ✅ 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**:

```bash
# 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**:

```bash
# 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**:

```bash
# 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

- [Complete Manual Testing Guide]README.md - Full deployment workflow testing
- [Render Command Reference]../../user-guide/commands/render.md - Command documentation
- [E2E Testing Overview]../README.md - Automated E2E test documentation
- [Troubleshooting Guide]../troubleshooting.md - Common issues and solutions

---

## 📝 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