opentofu-modules
SKILL.md
OpenTofu Modules & Testing
Write OpenTofu modules and tests for the homelab infrastructure. Modules live in infrastructure/modules/, tests in infrastructure/modules/<name>/tests/.
Quick Reference
# Run tests for a module
task tg:test-<module> # e.g., task tg:test-config
# Format all HCL
task tg:fmt
# Version pinned in .opentofu-version (currently 1.11.2)
Module Structure
Every module MUST have:
infrastructure/modules/<name>/
├── variables.tf # Input definitions with descriptions and validations
├── main.tf # Primary resources and locals
├── outputs.tf # Output definitions
├── versions.tf # Provider and OpenTofu version constraints
└── tests/ # Test directory
└── *.tftest.hcl
Test File Structure
Use .tftest.hcl extension. Define top-level variables for defaults inherited by all run blocks.
# Top-level variables set defaults for ALL run blocks
variables {
name = "test-cluster"
features = ["gateway-api", "longhorn"]
networking = {
id = 1
internal_tld = "internal.test.local"
# ... other required fields
}
# Default machine - inherited unless overridden
machines = {
node1 = {
cluster = "test-cluster"
type = "controlplane"
install = { selector = "disk.model = *" }
interfaces = [{
id = "eth0"
hardwareAddr = "aa:bb:cc:dd:ee:01"
addresses = [{ ip = "192.168.10.101" }]
}]
}
}
}
run "descriptive_test_name" {
command = plan # Use plan mode - no real resources created
variables {
features = ["prometheus"] # Only override what differs
}
assert {
condition = output.some_value == "expected"
error_message = "Descriptive failure message"
}
}
Key Patterns
Use command = plan
Always use plan mode for tests. This validates configuration without creating resources.
Variable Inheritance
Only include variables in run blocks when they differ from defaults. Minimizes duplication.
# CORRECT: Override only what changes
run "feature_enabled" {
command = plan
variables {
features = ["prometheus"]
}
assert { ... }
}
# AVOID: Repeating all variables
run "feature_enabled" {
command = plan
variables {
name = "test-cluster" # Unnecessary - inherited
features = ["prometheus"]
machines = { ... } # Unnecessary - inherited
}
}
Assert Against Outputs
Reference module outputs in assertions, not internal resources.
assert {
condition = length(output.machines) == 2
error_message = "Expected 2 machines"
}
assert {
condition = output.talos.kubernetes_version == "1.32.0"
error_message = "Version mismatch"
}
Test Feature Flags
Test both enabled and disabled states:
run "feature_enabled" {
command = plan
variables { features = ["longhorn"] }
assert {
condition = alltrue([
for m in output.talos.talos_machines :
contains(m.install.extensions, "iscsi-tools")
])
error_message = "Extension should be added when feature enabled"
}
}
run "feature_disabled" {
command = plan
variables { features = [] }
assert {
condition = alltrue([
for m in output.talos.talos_machines :
!contains(m.install.extensions, "iscsi-tools")
])
error_message = "Extension should not be present without feature"
}
}
Test Validations
Use expect_failures to verify variable validation rules:
run "invalid_version_rejected" {
command = plan
variables {
versions = {
talos = "1.9.0" # Missing v prefix - should fail
# ...
}
}
expect_failures = [var.versions]
}
Common Assertions
# Check length
condition = length(output.items) == 3
# Check key exists
condition = contains(keys(output.map), "expected_key")
# Check value in list
condition = contains(output.list, "expected_value")
# Check string contains
condition = strcontains(output.config, "expected_substring")
# Check all items match
condition = alltrue([for item in output.list : item.enabled == true])
# Check any item matches
condition = anytrue([for item in output.list : item.name == "target"])
# Nested check with labels/annotations
condition = anytrue([
for label in output.machines["node1"].labels :
label.key == "expected-label" && label.value == "expected-value"
])
Test Organization
Organize tests by concern:
plan.tftest.hcl- Basic structure and output validationvalidation.tftest.hcl- Input validation rulesfeature_<name>.tftest.hcl- Feature flag behavioredge_cases.tftest.hcl- Boundary conditions
Detailed Reference
For OpenTofu testing syntax, mock providers, and advanced patterns, see: references/opentofu-testing.md
Weekly Installs
55
Repository
ionfury/homelabGitHub Stars
22
First Seen
Jan 25, 2026
Security Audits
Installed on
gemini-cli55
github-copilot55
codex55
opencode55
amp54
kimi-cli54