A Terraform provider for Dokploy
Plugin-framework provider covering projects, environments, applications, Compose stacks, managed databases, domains, mounts, ports, redirects, basic auth, registries, SSH keys, certificates and backup destinations, over Dokploy's tRPC-over-REST API. The shim package exposes the provider to other Go modules, which is how pulumi-dokploy bridges it.
This commit is contained in:
80
.agents/skills/terraform-test/references/CI_CD.md
Normal file
80
.agents/skills/terraform-test/references/CI_CD.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# CI/CD Integration
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Terraform Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
with:
|
||||
terraform_version: 1.9.0
|
||||
|
||||
- run: terraform fmt -check -recursive
|
||||
- run: terraform init
|
||||
- run: terraform validate
|
||||
- name: Run unit tests (plan mode, no credentials needed)
|
||||
run: terraform test -filter=unit_test -verbose
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs: unit-tests
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: hashicorp/setup-terraform@v3
|
||||
with:
|
||||
terraform_version: 1.9.0
|
||||
|
||||
- run: terraform init
|
||||
- name: Run integration tests
|
||||
run: terraform test -filter=integration_test -verbose
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
```
|
||||
|
||||
## GitLab CI
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- validate
|
||||
- test
|
||||
|
||||
terraform-unit-tests:
|
||||
image: hashicorp/terraform:1.9
|
||||
stage: validate
|
||||
before_script:
|
||||
- terraform init
|
||||
script:
|
||||
- terraform fmt -check -recursive
|
||||
- terraform validate
|
||||
- terraform test -filter=unit_test -verbose
|
||||
|
||||
terraform-integration-tests:
|
||||
image: hashicorp/terraform:1.9
|
||||
stage: test
|
||||
before_script:
|
||||
- terraform init
|
||||
script:
|
||||
- terraform test -filter=integration_test -verbose
|
||||
only:
|
||||
- main
|
||||
```
|
||||
|
||||
## Recommended CI Strategy
|
||||
|
||||
- Run unit tests (plan mode + mock tests) on every PR — fast, no credentials needed
|
||||
- Run integration tests only on merge to main or nightly — requires cloud credentials
|
||||
- Use `-filter=unit_test` / `-filter=integration_test` to separate test types based on naming convention
|
||||
- Store cloud credentials as CI secrets, never in code
|
||||
314
.agents/skills/terraform-test/references/EXAMPLES.md
Normal file
314
.agents/skills/terraform-test/references/EXAMPLES.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# Example Test Suite
|
||||
|
||||
Complete example testing a VPC module with unit, integration, and mock tests.
|
||||
|
||||
## Unit Tests (Plan Mode)
|
||||
|
||||
```hcl
|
||||
# tests/vpc_module_unit_test.tftest.hcl
|
||||
|
||||
variables {
|
||||
environment = "test"
|
||||
aws_region = "us-west-2"
|
||||
}
|
||||
|
||||
run "test_defaults" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "10.0.0.0/16"
|
||||
vpc_name = "test-vpc"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
|
||||
error_message = "VPC CIDR should match input"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.enable_dns_hostnames == true
|
||||
error_message = "DNS hostnames should be enabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.tags["Name"] == "test-vpc"
|
||||
error_message = "VPC name tag should match input"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_subnets" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "10.0.0.0/16"
|
||||
vpc_name = "test-vpc"
|
||||
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
private_subnets = ["10.0.10.0/24", "10.0.11.0/24"]
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(aws_subnet.public) == 2
|
||||
error_message = "Should create 2 public subnets"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(aws_subnet.private) == 2
|
||||
error_message = "Should create 2 private subnets"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = alltrue([
|
||||
for subnet in aws_subnet.private :
|
||||
subnet.map_public_ip_on_launch == false
|
||||
])
|
||||
error_message = "Private subnets should not assign public IPs"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_outputs" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "10.0.0.0/16"
|
||||
vpc_name = "test-vpc"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = output.vpc_id != ""
|
||||
error_message = "VPC ID output should not be empty"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^vpc-", output.vpc_id))
|
||||
error_message = "VPC ID should have correct format"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = output.vpc_cidr == "10.0.0.0/16"
|
||||
error_message = "VPC CIDR output should match input"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_invalid_cidr" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "invalid"
|
||||
vpc_name = "test-vpc"
|
||||
}
|
||||
|
||||
expect_failures = [
|
||||
var.vpc_cidr
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Tests (Apply Mode)
|
||||
|
||||
```hcl
|
||||
# tests/vpc_module_integration_test.tftest.hcl
|
||||
|
||||
variables {
|
||||
environment = "integration-test"
|
||||
aws_region = "us-west-2"
|
||||
}
|
||||
|
||||
run "integration_test_vpc_creation" {
|
||||
# command defaults to apply — creates real AWS resources
|
||||
|
||||
variables {
|
||||
vpc_cidr = "10.100.0.0/16"
|
||||
vpc_name = "integration-test-vpc"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.id != ""
|
||||
error_message = "VPC should be created with valid ID"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.state == "available"
|
||||
error_message = "VPC should be in available state"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mock Tests (Plan Mode, No Credentials)
|
||||
|
||||
```hcl
|
||||
# tests/vpc_module_mock_test.tftest.hcl
|
||||
|
||||
mock_provider "aws" {
|
||||
mock_resource "aws_instance" {
|
||||
defaults = {
|
||||
id = "i-1234567890abcdef0"
|
||||
instance_type = "t2.micro"
|
||||
ami = "ami-12345678"
|
||||
public_ip = "203.0.113.1"
|
||||
private_ip = "10.0.1.100"
|
||||
}
|
||||
}
|
||||
|
||||
mock_resource "aws_vpc" {
|
||||
defaults = {
|
||||
id = "vpc-12345678"
|
||||
cidr_block = "10.0.0.0/16"
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
}
|
||||
}
|
||||
|
||||
mock_resource "aws_subnet" {
|
||||
defaults = {
|
||||
id = "subnet-12345678"
|
||||
vpc_id = "vpc-12345678"
|
||||
cidr_block = "10.0.1.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
map_public_ip_on_launch = false
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_ami" {
|
||||
defaults = {
|
||||
id = "ami-0c55b159cbfafe1f0"
|
||||
name = "ubuntu-focal-20.04-amd64"
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_availability_zones" {
|
||||
defaults = {
|
||||
names = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run "test_instance_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
instance_type = "t2.micro"
|
||||
ami_id = "ami-12345678"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_instance.example.instance_type == "t2.micro"
|
||||
error_message = "Instance type should match input variable"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_instance.example.id == "i-1234567890abcdef0"
|
||||
error_message = "Mock should return consistent instance ID"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_data_source_with_mocks" {
|
||||
command = plan
|
||||
|
||||
assert {
|
||||
condition = data.aws_ami.ubuntu.id == "ami-0c55b159cbfafe1f0"
|
||||
error_message = "Mock data source should return predictable AMI ID"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(data.aws_availability_zones.available.names) == 3
|
||||
error_message = "Should return 3 mocked availability zones"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = contains(data.aws_availability_zones.available.names, "us-west-2a")
|
||||
error_message = "Should include us-west-2a in mocked zones"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_outputs_with_mocks" {
|
||||
command = plan
|
||||
|
||||
assert {
|
||||
condition = output.vpc_id == "vpc-12345678"
|
||||
error_message = "VPC ID output should match mocked value"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("^vpc-", output.vpc_id))
|
||||
error_message = "VPC ID output should have correct format"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_conditional_resources_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
create_bastion = true
|
||||
create_nat_gateway = false
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(aws_instance.bastion) == 1
|
||||
error_message = "Bastion should be created when enabled"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(aws_nat_gateway.nat) == 0
|
||||
error_message = "NAT gateway should not be created when disabled"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_tag_inheritance_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
common_tags = {
|
||||
Environment = "test"
|
||||
ManagedBy = "Terraform"
|
||||
}
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = alltrue([
|
||||
for key in keys(var.common_tags) :
|
||||
contains(keys(aws_instance.example.tags), key)
|
||||
])
|
||||
error_message = "All common tags should be present on instance"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_invalid_cidr_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "invalid"
|
||||
}
|
||||
|
||||
expect_failures = [
|
||||
var.vpc_cidr
|
||||
]
|
||||
}
|
||||
|
||||
run "setup_vpc_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_cidr = "10.0.0.0/16"
|
||||
vpc_name = "test-vpc"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
|
||||
error_message = "VPC CIDR should match input"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_subnet_references_vpc_with_mocks" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
vpc_id = run.setup_vpc_with_mocks.vpc_id
|
||||
subnet_cidr = "10.0.1.0/24"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_subnet.example.vpc_id == run.setup_vpc_with_mocks.vpc_id
|
||||
error_message = "Subnet should reference VPC from previous run"
|
||||
}
|
||||
}
|
||||
```
|
||||
171
.agents/skills/terraform-test/references/MOCK_PROVIDERS.md
Normal file
171
.agents/skills/terraform-test/references/MOCK_PROVIDERS.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Mock Providers
|
||||
|
||||
Mock providers simulate provider behavior without creating real infrastructure (Terraform 1.7.0+). Use them for fast, credential-free unit tests.
|
||||
|
||||
## Basic Mock Provider
|
||||
|
||||
```hcl
|
||||
mock_provider "aws" {
|
||||
mock_resource "aws_instance" {
|
||||
defaults = {
|
||||
id = "i-1234567890abcdef0"
|
||||
instance_type = "t2.micro"
|
||||
ami = "ami-12345678"
|
||||
public_ip = "203.0.113.1"
|
||||
private_ip = "10.0.1.100"
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_ami" {
|
||||
defaults = {
|
||||
id = "ami-0c55b159cbfafe1f0"
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_availability_zones" {
|
||||
defaults = {
|
||||
names = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run "test_with_mocks" {
|
||||
command = plan # Mocks only work with plan mode
|
||||
|
||||
assert {
|
||||
condition = aws_instance.example.id == "i-1234567890abcdef0"
|
||||
error_message = "Mock instance ID should match"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Aliased Mock Provider
|
||||
|
||||
```hcl
|
||||
mock_provider "aws" {
|
||||
alias = "mocked"
|
||||
|
||||
mock_resource "aws_s3_bucket" {
|
||||
defaults = {
|
||||
id = "test-bucket-12345"
|
||||
arn = "arn:aws:s3:::test-bucket-12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run "test_with_aliased_mock" {
|
||||
command = plan
|
||||
|
||||
providers = {
|
||||
aws = provider.aws.mocked
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = aws_s3_bucket.example.id == "test-bucket-12345"
|
||||
error_message = "Bucket ID should match mock"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Mock Defaults
|
||||
|
||||
```hcl
|
||||
mock_provider "aws" {
|
||||
mock_resource "aws_instance" {
|
||||
defaults = {
|
||||
id = "i-1234567890abcdef0"
|
||||
arn = "arn:aws:ec2:us-west-2:123456789012:instance/i-1234567890abcdef0"
|
||||
instance_type = "t2.micro"
|
||||
ami = "ami-12345678"
|
||||
availability_zone = "us-west-2a"
|
||||
subnet_id = "subnet-12345678"
|
||||
vpc_security_group_ids = ["sg-12345678"]
|
||||
associate_public_ip_address = true
|
||||
public_ip = "203.0.113.1"
|
||||
private_ip = "10.0.1.100"
|
||||
tags = {}
|
||||
}
|
||||
}
|
||||
|
||||
mock_resource "aws_vpc" {
|
||||
defaults = {
|
||||
id = "vpc-12345678"
|
||||
arn = "arn:aws:ec2:us-west-2:123456789012:vpc/vpc-12345678"
|
||||
cidr_block = "10.0.0.0/16"
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
instance_tenancy = "default"
|
||||
tags = {}
|
||||
}
|
||||
}
|
||||
|
||||
mock_resource "aws_subnet" {
|
||||
defaults = {
|
||||
id = "subnet-12345678"
|
||||
arn = "arn:aws:ec2:us-west-2:123456789012:subnet/subnet-12345678"
|
||||
vpc_id = "vpc-12345678"
|
||||
cidr_block = "10.0.1.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
map_public_ip_on_launch = false
|
||||
tags = {}
|
||||
}
|
||||
}
|
||||
|
||||
mock_resource "aws_s3_bucket" {
|
||||
defaults = {
|
||||
id = "test-bucket-12345"
|
||||
arn = "arn:aws:s3:::test-bucket-12345"
|
||||
bucket = "test-bucket-12345"
|
||||
bucket_domain_name = "test-bucket-12345.s3.amazonaws.com"
|
||||
region = "us-west-2"
|
||||
tags = {}
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_ami" {
|
||||
defaults = {
|
||||
id = "ami-0c55b159cbfafe1f0"
|
||||
name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20210430"
|
||||
architecture = "x86_64"
|
||||
root_device_type = "ebs"
|
||||
virtualization_type = "hvm"
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_availability_zones" {
|
||||
defaults = {
|
||||
names = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||
zone_ids = ["usw2-az1", "usw2-az2", "usw2-az3"]
|
||||
}
|
||||
}
|
||||
|
||||
mock_data "aws_vpc" {
|
||||
defaults = {
|
||||
id = "vpc-12345678"
|
||||
cidr_block = "10.0.0.0/16"
|
||||
enable_dns_hostnames = true
|
||||
enable_dns_support = true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## When to Use Mocks
|
||||
|
||||
**Good fit:**
|
||||
- Testing Terraform logic, conditionals, `for_each`/`count` expressions
|
||||
- Validating variable transformations and output calculations
|
||||
- Local development without cloud credentials
|
||||
- Fast CI/CD feedback loops
|
||||
|
||||
**Not a good fit:**
|
||||
- Validating actual provider API behavior
|
||||
- Testing real resource creation side effects
|
||||
- End-to-end integration testing
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Plan mode only** — mocks don't work with `command = apply`
|
||||
- Mock defaults may not reflect real computed attribute values
|
||||
- Mocks need manual updates when provider schemas change
|
||||
- Can't test real resource dependencies or timing
|
||||
Reference in New Issue
Block a user