-
Notifications
You must be signed in to change notification settings - Fork 2
Module Testing Environment
Testing Terraform is a challenge because it can create or change real infrastructure, and testing Terraform automation with Consul Terraform Sync introduces more complexity. A common approach to test a module for Consul Terraform Sync is to simplify the environment to just Terraform. This can be done by manually emulating how CTS would automate the module and execute Terraform.
Note: You do not need Consul Terraform Sysnc or a running Consul agent to test the integration, but does require familiarity with running Terraform.
- Download and install Terraform CLI version 0.13 or newer.
- Create root module files that would typically be generated by Consul Terraform Sync.
- Execute Terraform commands and verify the output is expected and the network infrastructure is updated accordingly.
This section will guide you to create a Terraform root module that can be used to test a module for Consul Terraform Sync compatibility. You will need to create 3 Terraform configuration files that make up the root module. Create these files in a directory separate from the module you are developing.
- Create a file named
main.tfand copy the code block below to the file. This snippet of configuration includes amoduleblock that calls your module in development as a child module.terraform { required_version = "~>0.13.0" required_providers { # <provider> = { # source = "<namespace>/<provider>" # version = "1.0.0" # } } } # terraform_provider "<provider>" { # attr = value # } module "<name>" { source = "<module source>" # Set the module source to the location of your module for testing. version = "X.Y.Z" services = var.services # Pass required or optional input variables as module arguments. # arg = 5 }
- For any provider used by the module that is being tested, add the provider source and version in the
terraform.required_providersblock. This is a Terraform 0.13 feature. - Include any provider configuration needed for the providers used by the module in
terraform_providerblocks. Consul Terraform Sync relies on passing providers implicitly through inheritance to the child module. - Replace placeholder values in the
moduleblock with information pertaining to the module to test, like name, module source, and version. The module source can be local or remote, and Terraform will either search on disk or download it. - Note that the
servicesinput variable is passed to the module. This satisfies the module spec requirement for Consul Terraform Sync. - Pass any arguments in the
moduleblock to set input variables for the module to test.
Consul Terraform Sync creates the main.tf file by templating all of the above values manually entered. The resulting file would resemble the main.tf below. Note that in this example, CTS sets each attribute from a variable instead of direct assignment. These variables are interpreted and generated based on user configuration for CTS. CTS will also place the corresponding variable declarations in a variables.tf file and set the values in terraform.tfvars. For manual testing purposes, you do not need to abstract variables this way and can set them directly in the main.tf.
main.tf
# This file is generated by Consul Terraform Sync.
#
# The HCL blocks, arguments, variables, and values are derived from the
# operator configuration for Sync. Any manual changes to this file
# may not be preserved and could be overwritten by a subsequent update.
#
# Task: <task name>
# Description: <task description>
terraform {
required_version = "~>0.13.0"
required_providers {
testProvider = {
source = "namespace/testProvider"
version = "1.0.0"
}
}
}
terraform_provider "testProvider" {
attr = var.testProvider.attr
count = var.testProvider.count
}
module "test" {
source = "namespace/cts/test"
version = "0.0.0"
services = var.services
arg1 = var.arg1
arg2 = var.arg2
}
- Create a file named
variables.tfand copy thevariable "services" {}code found in thisvariables.tftemplate file. It would resemble the variable below (this may be not up to date, please refer to the template file).variable "services" { description = "Consul services monitored by Consul Terraform Sync" type = map( object({ id = string name = string kind = string address = string port = number meta = map(string) tags = list(string) namespace = string status = string node = string node_id = string node_address = string node_datacenter = string node_tagged_addresses = map(string) node_meta = map(string) cts_user_defined_meta = map(string) }) ) }
- This is the
servicesvariable declared at the root module that is expected to be compatible with theservicesvariable declared in the module for testing. - If there are any additional input variables referenced in
main.tfspecific to your test, you can add the variables declarations belowvar.services.
Continuing with the main.tf example that Consul Terraform Sync generates, this is the corresponding variables.tf that would be created:
variables.tf
# This file is generated by Consul Terraform Sync.
#
# The HCL blocks, arguments, variables, and values are derived from the
# operator configuration for Sync. Any manual changes to this file
# may not be preserved and could be overwritten by a subsequent update.
#
# Task: <task name>
# Description: <task description>
# Service definition protocol v0
variable "services" {
description = "Consul services monitored by Consul Terraform Sync"
type = map(
object({
id = string
name = string
kind = string
address = string
port = number
meta = map(string)
tags = list(string)
namespace = string
status = string
node = string
node_id = string
node_address = string
node_datacenter = string
node_tagged_addresses = map(string)
node_meta = map(string)
cts_user_defined_meta = map(string)
})
)
}
variable "testProvider" {
default = null
description = "Configuration object for testProvider"
type = object({
attr = string
count = number
})
}- Create a file named
terraform.tfvarsand copy the mock value for theservicesinput variable. The mock value represents service information from the Consul Catalog for 2 services, named "api" and "web", with 2 instances of the service "web". - You can modify the values to test different variants for your module as long as the changes retain the same variable structure.
services = { "api" : { address = "172.17.0.1" id = "api" name = "api" kind = "" port = 80 meta = {} tags = [] namespace = "" status = "passing" node_id = "node_a" node = "foobar" node_address = "192.168.10.10" node_datacenter = "dc1" node_tagged_addresses = { lan = "192.168.10.10" wan = "10.0.10.10" } node_meta = {} cts_user_defined_meta = {} }, "web_1" : { address = "172.17.0.3" id = "web_1" name = "web" kind = "" port = 5000 meta = { foobar_meta_value = "baz" } tags = ["tacos"] namespace = "" status = "passing" node_id = "node_a" node = "foobar" node_address = "192.168.10.10" node_datacenter = "dc1" node_tagged_addresses = { lan = "192.168.10.10" wan = "10.0.10.10" } node_meta = { somekey = "somevalue" } cts_user_defined_meta = {} }, "web_2" : { address = "172.17.0.3" id = "web_2" name = "web" kind = "" port = 5000 meta = { foobar_meta_value = "baz" } tags = ["burrito"] namespace = "" status = "passing" node_id = "node_b" node = "foobarbaz" node_address = "192.168.10.11" node_datacenter = "dc1" node_tagged_addresses = { lan = "192.168.10.11" wan = "10.0.10.10" } node_meta = {} cts_user_defined_meta = {} } }
- Set any additional configuration that may be required for your setup and testing the module. This may include setting up real infrastructure to interface with and preparing configuration and authentication for the provider(s).
- Run
terraform initto setup the local Terraform environment. - Run
terraform planandterraform applyto execute changes to your network infrastructure. - Verify that the infrastructure is updated with the services values.
- Update the
servicesvalue in this file in between Terraform runs to emulate dynamic changes discovered from Consul Catalog by Consul Terraform Sync. This could be adding or removing a service or service instance.