diff --git a/modules/dummy/.terraform-docs.yml b/modules/dummy/.terraform-docs.yml new file mode 100644 index 000000000..3a69365ff --- /dev/null +++ b/modules/dummy/.terraform-docs.yml @@ -0,0 +1,48 @@ +formatter: "markdown" + +version: "" + +header-from: docs/header.md +footer-from: docs/footer.md + +recursive: + enabled: false + path: modules + include-main: true + +sections: + hide: [] + show: [] + +content: "" + +output: + file: "README.md" + mode: inject + template: |- + + {{ .Content }} + + +output-values: + enabled: false + from: "" + +sort: + enabled: true + by: name + +settings: + anchor: true + color: true + default: true + description: false + escape: true + hide-empty: false + html: true + indent: 2 + lockfile: true + read-comments: true + required: true + sensitive: true + type: true diff --git a/modules/dummy/README.md b/modules/dummy/README.md index aa740c9e3..4985cad71 100644 --- a/modules/dummy/README.md +++ b/modules/dummy/README.md @@ -1,102 +1,128 @@ -# Terraform Diagnostic Executor Module + +# **Dummy Terraform Module** -This module is designed for DevOps testing and CI/CD pipeline validation. It provides features to simulate network latency (sleep) and catastrophic failures (crash) at both the terraform plan and terraform apply stages. +## Overview -It leverages the external data source (for plan-time checks) and null_resource with local-exec provisioners (for apply-time actions). +This Terraform module is designed for DevOps testing and CI/CD pipeline validation. It provides controls to simulate latency and failures at every phase of the Terraform lifecycle — plan, apply, and destroy — without affecting real infrastructure. -## Prerequisites +The module uses the `external` data source to execute a Node.js script for plan-time diagnostics and `null_resource` with `local-exec` provisioners for apply-time and destroy-time behavior. This makes it easy to reproduce slow or failing pipeline runs in a controlled, repeatable way. -- Terraform: Required version ~> 1.0. +Typical use cases include testing CI/CD pipeline timeouts, verifying failure-handling logic, and validating destroy-phase behavior in automated workflows. -- Node.js: Must be installed and accessible via the PATH on the machine running terraform plan and terraform apply. +## Key Features -- Required Providers: hashicorp/external and hashicorp/null. +- **Plan-time controls**: Simulate slow or failing `terraform plan` runs via a Node.js script executed by the `external` data source. +- **Apply-time controls**: Introduce configurable delays or intentional failures during `terraform apply` using `null_resource` provisioners. +- **Destroy-time controls**: Reliably gate sleep and crash behavior during `terraform destroy`, always present in state so destroy provisioners execute as expected. +- **Deterministic destroy ordering**: The crash resource depends on the sleep resource, ensuring sleep always runs before crash when both are enabled. +- **Input validation**: The `sleep_on_destroy` variable is validated to require a non-negative integer, preventing silent misconfigurations. -## Module Usage +## Basic Usage -The module requires four primary boolean and numeric variables to control its diagnostic behavior. +### Simulate apply-time delay and destroy-time failure -Example +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" -```terraform -module "diagnostic_test" { - source = "./node-executor" - - # Base input instance_name = "ci-pipeline-check" - # PLAN TIME CONTROLS - sleep_on_plan = 5 # Delays 'terraform plan' by 5 seconds - crash_on_plan = false # Set to true to force 'terraform plan' failure - - # APPLY TIME CONTROLS - sleep_on_apply = 10 # Delays 'terraform apply' by 10 seconds - crash_on_apply = false # Set to true to force 'terraform apply' failure -} - -output "plan_status" { - value = module.diagnostic_test.plan_message + sleep_on_apply = 10 + crash_on_destroy = true } ``` +### Simulate plan-time crash and destroy-time delay -## Inputs +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" -## Inputs + instance_name = "ci-pipeline-check" -| Name | Description | Type | Default | Control Phase | -|---|---|---|---|---| -| `instance_name` | A unique identifier for the diagnostic run. | `string` | `"default-test"` | Both | -| `sleep_on_plan` | Duration in seconds to delay the `terraform plan` execution. | `number` | `0` | Plan | -| `crash_on_plan` | If set to true, forces `terraform plan` to fail immediately. | `bool` | `false` | Plan | -| `sleep_on_apply` | Duration in seconds to delay the `terraform apply` execution. | `number` | `0` | Apply | -| `crash_on_apply` | If set to true, forces `terraform apply` to fail immediately. | `bool` | `false` | Apply | + crash_on_plan = true + sleep_on_destroy = 30 +} +``` +## File Structure -## Outputs +``` +dummy/ +├── .terraform-docs.yml # terraform-docs configuration +├── README.md # Auto-generated documentation +├── main.tf # Module resources and provisioners +├── variables.tf # Input variable definitions +├── outputs.tf # Output value definitions +├── script.js # Node.js script for plan-time diagnostics +├── _examples/ # Usage examples +│ └── basic/ # Basic configuration example +│ └── main.tf +└── docs/ # Documentation source files + ├── header.md # This file + └── footer.md # Additional resources and support +``` -| Name | Description | Value Source | -| ----- | ----- | ----- | -| `plan_message` | A status message from the plan-time script execution. | `data.external` | -| `plan_timestamp` | The timestamp when the plan-time script executed. | `data.external` | -| `plan_slept_for_s` | The actual seconds the script slept during plan. | `data.external` | -| `plan_crashed` | Status indicating if the crash logic was executed during plan. | `data.external` | +## Requirements +No requirements. -## Diagnostic Scenarios +## Providers -### 1. Test Plan Failure +| Name | Version | +|------|---------| +| [external](#provider\_external) | n/a | +| [null](#provider\_null) | n/a | -To test how your CI/CD pipeline handles a plan failure: +## Modules -```terraform -# Set crash_on_plan to true via command line -terraform plan -var="crash_on_plan=true" -``` +No modules. +## Resources +| Name | Type | +|------|------| +| [null_resource.conditional_crash](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_destroy_crash](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_destroy_sleep](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.conditional_sleep](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.diagnostic_base_logic](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [external_external.script_executor](https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/external) | data source | -Expected Result: The terraform plan command will exit with a non-zero exit code (failure) as the data "external" source executes the Node.js script, which calls process.exit(1). +## Inputs -### 2. Test Apply Failure +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [crash\_on\_apply](#input\_crash\_on\_apply) | Set to true to force a non-zero exit code during the 'apply' phase. | `bool` | `false` | no | +| [crash\_on\_destroy](#input\_crash\_on\_destroy) | Set to true to force a non-zero exit code during the 'destroy' phase. | `bool` | `false` | no | +| [crash\_on\_plan](#input\_crash\_on\_plan) | Set to true to force a non-zero exit code during the 'plan' phase. | `bool` | `false` | no | +| [instance\_name](#input\_instance\_name) | A unique identifier for this module instance. | `string` | n/a | yes | +| [sleep\_on\_apply](#input\_sleep\_on\_apply) | Number of seconds to sleep during the 'apply' phase. | `number` | `0` | no | +| [sleep\_on\_destroy](#input\_sleep\_on\_destroy) | Number of seconds to sleep during the 'destroy' phase. | `number` | `0` | no | +| [sleep\_on\_plan](#input\_sleep\_on\_plan) | Number of seconds to sleep during the 'plan' phase. | `number` | `0` | no | -To test infrastructure deployment failure: +## Outputs -```terraform -# Set crash_on_apply to true via command line -terraform apply -auto-approve -var="crash_on_apply=true" -``` +| Name | Description | +|------|-------------| +| [apply\_resource\_id](#output\_apply\_resource\_id) | The ID of the base null resource, indicating apply logic executed. | +| [script\_message](#output\_script\_message) | The message returned by the external script (ran during plan). | +| [script\_timestamp](#output\_script\_timestamp) | The timestamp returned by the external script (ran during plan). | -Expected Result: The terraform apply command will fail when it attempts to create the null_resource.diagnostic_crash_logic because its local-exec provisioner executes the exit 1 shell command. +## Examples -### 3. Test Apply Timeout/Latency +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples): -To test pipeline timeouts during execution: +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples/basic) - Minimal configuration showing plan, apply, and destroy controls -``` -# Set a 30 second delay during the apply phase -terraform apply -auto-approve -var="sleep_on_apply=30" -``` +## Resources + +- **Terraform null provider**: [https://registry.terraform.io/providers/hashicorp/null/latest](https://registry.terraform.io/providers/hashicorp/null/latest) +- **Terraform external provider**: [https://registry.terraform.io/providers/hashicorp/external/latest](https://registry.terraform.io/providers/hashicorp/external/latest) +- **Terraform local-exec provisioner**: [https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec](https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec) +## Support +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). + diff --git a/modules/dummy/_examples/basic/main.tf b/modules/dummy/_examples/basic/main.tf new file mode 100644 index 000000000..d8d3cb816 --- /dev/null +++ b/modules/dummy/_examples/basic/main.tf @@ -0,0 +1,29 @@ +# Basic example: simulate a 5-second delay during apply. +# Useful for validating CI/CD pipeline timeout and failure-handling logic. +# Set crash_on_destroy = true to test destroy-time failure behavior. + +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + # PLAN TIME CONTROLS + sleep_on_plan = 0 + crash_on_plan = false + + # APPLY TIME CONTROLS + sleep_on_apply = 5 # Delays 'terraform apply' by 5 seconds + crash_on_apply = false + + # DESTROY TIME CONTROLS + sleep_on_destroy = 0 + crash_on_destroy = false # Set to true to force 'terraform destroy' failure +} + +output "script_message" { + value = module.dummy.script_message +} + +output "apply_resource_id" { + value = module.dummy.apply_resource_id +} diff --git a/modules/dummy/docs/footer.md b/modules/dummy/docs/footer.md new file mode 100644 index 000000000..38fafb99a --- /dev/null +++ b/modules/dummy/docs/footer.md @@ -0,0 +1,15 @@ +## Examples + +For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples): + +- [Basic](https://github.com/prefapp/tfm/tree/main/modules/dummy/_examples/basic) - Minimal configuration showing plan, apply, and destroy controls + +## Resources + +- **Terraform null provider**: [https://registry.terraform.io/providers/hashicorp/null/latest](https://registry.terraform.io/providers/hashicorp/null/latest) +- **Terraform external provider**: [https://registry.terraform.io/providers/hashicorp/external/latest](https://registry.terraform.io/providers/hashicorp/external/latest) +- **Terraform local-exec provisioner**: [https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec](https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec) + +## Support + +For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues). diff --git a/modules/dummy/docs/header.md b/modules/dummy/docs/header.md new file mode 100644 index 000000000..08d599f6f --- /dev/null +++ b/modules/dummy/docs/header.md @@ -0,0 +1,63 @@ +# **Dummy Terraform Module** + +## Overview + +This Terraform module is designed for DevOps testing and CI/CD pipeline validation. It provides controls to simulate latency and failures at every phase of the Terraform lifecycle — plan, apply, and destroy — without affecting real infrastructure. + +The module uses the `external` data source to execute a Node.js script for plan-time diagnostics and `null_resource` with `local-exec` provisioners for apply-time and destroy-time behavior. This makes it easy to reproduce slow or failing pipeline runs in a controlled, repeatable way. + +Typical use cases include testing CI/CD pipeline timeouts, verifying failure-handling logic, and validating destroy-phase behavior in automated workflows. + +## Key Features + +- **Plan-time controls**: Simulate slow or failing `terraform plan` runs via a Node.js script executed by the `external` data source. +- **Apply-time controls**: Introduce configurable delays or intentional failures during `terraform apply` using `null_resource` provisioners. +- **Destroy-time controls**: Reliably gate sleep and crash behavior during `terraform destroy`, always present in state so destroy provisioners execute as expected. +- **Deterministic destroy ordering**: The crash resource depends on the sleep resource, ensuring sleep always runs before crash when both are enabled. +- **Input validation**: The `sleep_on_destroy` variable is validated to require a non-negative integer, preventing silent misconfigurations. + +## Basic Usage + +### Simulate apply-time delay and destroy-time failure + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + sleep_on_apply = 10 + crash_on_destroy = true +} +``` + +### Simulate plan-time crash and destroy-time delay + +```hcl +module "dummy" { + source = "git::https://github.com/prefapp/tfm.git//modules/dummy" + + instance_name = "ci-pipeline-check" + + crash_on_plan = true + sleep_on_destroy = 30 +} +``` + +## File Structure + +``` +dummy/ +├── .terraform-docs.yml # terraform-docs configuration +├── README.md # Auto-generated documentation +├── main.tf # Module resources and provisioners +├── variables.tf # Input variable definitions +├── outputs.tf # Output value definitions +├── script.js # Node.js script for plan-time diagnostics +├── _examples/ # Usage examples +│ └── basic/ # Basic configuration example +│ └── main.tf +└── docs/ # Documentation source files + ├── header.md # This file + └── footer.md # Additional resources and support +``` diff --git a/modules/dummy/main.tf b/modules/dummy/main.tf index bf1c9e70c..9d34dca0e 100644 --- a/modules/dummy/main.tf +++ b/modules/dummy/main.tf @@ -3,13 +3,13 @@ data "external" "script_executor" { # Execute the node binary directly for better reliability and bypass complex shell nesting issues. program = [ - "node", + "node", "${path.module}/script.js" ] # Pass only the variables relevant to the PLAN phase to the script query = { - instance_name = var.instance_name + instance_name = var.instance_name sleep_duration = var.sleep_on_plan enable_crash = var.crash_on_plan } @@ -30,11 +30,11 @@ resource "null_resource" "conditional_sleep" { triggers = { run_always = timestamp() } - + provisioner "local-exec" { # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - + interpreter = ["sh", "-c"] + # Echo message and then sleep. We use count[0] to access the instance. command = "echo '--- APPLY-TIME DELAY --- Sleeping for ${var.sleep_on_apply} seconds...' && sleep ${var.sleep_on_apply}" when = create @@ -44,7 +44,7 @@ resource "null_resource" "conditional_sleep" { # 3. Conditional Crash: This resource is only created if 'crash_on_apply' is true. resource "null_resource" "conditional_crash" { count = var.crash_on_apply ? 1 : 0 - + # Force re-creation on every apply to ensure the provisioner runs triggers = { run_always = timestamp() @@ -52,30 +52,54 @@ resource "null_resource" "conditional_crash" { provisioner "local-exec" { # Executes the command using a standard sh interpreter - interpreter = ["sh", "-c"] - + interpreter = ["sh", "-c"] + # Echo message and then use 'exit 1' to deliberately cause failure command = "echo '--- APPLY-TIME CRASH --- INTENTIONAL FAILURE' && exit 1" when = create } } -# --- Module Outputs --- +# 4. Conditional Sleep on DESTROY +resource "null_resource" "conditional_destroy_sleep" { + count = 1 -# Output from the PLAN-TIME script -output "script_message" { - description = "The message returned by the external script (ran during plan)." - value = data.external.script_executor.result.message -} + triggers = { + sleep_on_destroy = var.sleep_on_destroy + } -# Output from the PLAN-TIME script -output "script_timestamp" { - description = "The timestamp returned by the external script (ran during plan)." - value = data.external.script_executor.result.timestamp + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = <<-EOT + if [ "${self.triggers.sleep_on_destroy}" -gt 0 ]; then + echo '--- DESTROY-TIME DELAY --- Sleeping for ${self.triggers.sleep_on_destroy} seconds...' + sleep ${self.triggers.sleep_on_destroy} + else + echo '--- DESTROY-TIME DELAY --- Skipping sleep; sleep_on_destroy <= 0 ---' + fi + EOT + } } -# Check to ensure the base apply-time resource was created -output "apply_resource_id" { - description = "The ID of the base null resource, indicating apply logic executed." - value = null_resource.diagnostic_base_logic.id +# 5. Conditional Crash on DESTROY +resource "null_resource" "conditional_destroy_crash" { + count = 1 + + triggers = { + crash_on_destroy = var.crash_on_destroy + } + + provisioner "local-exec" { + when = destroy + interpreter = ["sh", "-c"] + command = <<-EOT + if [ "${self.triggers.crash_on_destroy}" = "true" ]; then + echo '--- DESTROY-TIME CRASH --- INTENTIONAL FAILURE' + exit 1 + else + echo '--- DESTROY-TIME CRASH --- Skipping crash; crash_on_destroy = false ---' + fi + EOT + } } diff --git a/modules/dummy/outputs.tf b/modules/dummy/outputs.tf new file mode 100644 index 000000000..4d5cb96d5 --- /dev/null +++ b/modules/dummy/outputs.tf @@ -0,0 +1,14 @@ +output "script_message" { + description = "The message returned by the external script (ran during plan)." + value = data.external.script_executor.result.message +} + +output "script_timestamp" { + description = "The timestamp returned by the external script (ran during plan)." + value = data.external.script_executor.result.timestamp +} + +output "apply_resource_id" { + description = "The ID of the base null resource, indicating apply logic executed." + value = null_resource.diagnostic_base_logic.id +} diff --git a/modules/dummy/variables.tf b/modules/dummy/variables.tf index 09fa11316..1c6212e43 100644 --- a/modules/dummy/variables.tf +++ b/modules/dummy/variables.tf @@ -32,3 +32,21 @@ variable "crash_on_plan" { default = false } +# 5. Sleep during 'terraform destroy' +variable "sleep_on_destroy" { + description = "Number of seconds to sleep during the 'destroy' phase." + type = number + default = 0 + + validation { + condition = var.sleep_on_destroy >= 0 && floor(var.sleep_on_destroy) == var.sleep_on_destroy + error_message = "sleep_on_destroy must be a non-negative integer number of seconds." + } +} + +# 6. Crash during 'terraform destroy' +variable "crash_on_destroy" { + description = "Set to true to force a non-zero exit code during the 'destroy' phase." + type = bool + default = false +}