Jumping into a newly forked project can be a difficult task, maybe even frightening! Now, think about jumping into a production-ready project with nine years of legacy code base there. On top of that, you have the task of taking one of the project’s new features from experimental to production-ready.
This is our journey with the OpenTofu testing feature.
It’s an interesting introduction to what’s going on behind the scenes at OpenTofu, as we work on our own pipelines. In this case, we are taking raw, experimental code and turning it into something that keeps up with (and possibly outperforms) Terraform v1.6.0 with the drop-in replacement, OpenTofu v1.6.alpha.
As part of HashiCorp Terraform License changes, we joined the OpenTofu initiative. And one of our first tasks is to get OpenTofu up-to-date with the upcoming Terraform 1.6.0, which means getting the testing feature from experimental to production-ready.
At the time of the fork, the testing feature did already exist in the codebase. However, it was still in an experimental state, without any documentation as to how the feature worked, and without much test coverage on the test feature’s own capabilities.
That meant we had to figure out the purpose of what seemed like a WIP testing feature, the pros and cons of this approach, and figure out what was missing to get the feature out of “development hell”
We started out mapping the feature by doing a few different things:
First, we read previous tests to understand how the feature behaves in different scenarios. Next, we thoroughly read through its code to get its ins and outs. Then, we tried it out ourselves to get a feel of using the new testing capability.
These are important, as you don’t want to just jump in and start changing code based on what you think it should look like. You need to understand the architecture and intent of the original implementer, so that the code you write complements it, rather than fighting it.
After this effort, we had a full grasp of the testing feature. It was a framework built to help the users test out their configuration in modules in an end-to-end manner, per module. It makes sure the modules act as expected in common cases, while predictably and safely responding to failure conditions, like a misconfiguration.
The testing feature introduces *.tftest.hcl
files. These 1) describe your testing suite and 2) support a specific subset of HCL blocks. The most important block there is the new run
block, which constitutes a test run.
When executing tofu test
, each run
block executes a tofu plan
or tofu apply
behind the scenes, running your module with the configuration specified for the test, and actually creating resources in your cloud (in the case of apply
).
After each run, it performs validations; i.e., it makes sure that 1) all assertions pass, 2) none of the checks are failing, and 3) the plan
/apply
has finished successfully.
After tofu is done performing all the runs and tests, it attempts to destroy all the resources that were created as part of that tofu test
run.
How did we approach it?
Throughout our initial testing and code reading, we made a list of feature behaviors that were not covered with tests in the codebase, and also listed behaviors that we felt had the potential to not work properly.
We mostly relied on the code we read, and our knowledge of legacy Terraform and prior issues. For most of those, we ended up creating pull requests, adding test coverage, or actually fixing bugs.
During our time testing out the feature, we encountered some bugs, and came up with some suggestions to actually improve the feature on top of what was already in the pre-forked codebase. Below are some examples of such bugs that we ended up fixing:
Bug 1: Sensitive Value in run
block
Bug description: tofu test
crashes when evaluating a sensitive value inside a run
block’s assertions.
While playing around with our manual QA scenarios, we found out that running tofu test
can, in a certain scenario, crash the program. Specifically, this happens when the configuration includes a run
block that has an assertion, and that assertion itself relies on a sensitive value (see the code sample below for main.tftest.hcl).
This isn’t ideal, as a crash in tofu test
means that there are now resources in your cloud, for which tofu has no recollection.
You could achieve this crash by running the following configuration:
main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.14.0"
}
}
}
resource "aws_secretsmanager_secret" "my_secret" {
name = "my_secret"
}
resource "aws_secretsmanager_secret_version" "my_secret_version" {
secret_