diff --git a/docs/dev/terraform.md b/docs/dev/terraform.md new file mode 100644 index 0000000000..41b876da7f --- /dev/null +++ b/docs/dev/terraform.md @@ -0,0 +1,109 @@ +# Embedded Terraform in the Installer + +The installer runs terraform in order to provision the infrastructure for a cluster. However, there is a design goal +for the installer that the user performing the installation should not be required to have any software pre-installed +in their environment. This design goal precludes the user from, for example, being required to have any containerization +software available. To get around this for terraform, the installer embeds within its binary the needed terraform +binaries. At installation time, the terraform binaries are extracted from the installer into the local file system. + +## Design + +### Building terraform binaries + +The terraform binary and all of the terraform provider binaries are built from the ./terraform directory. For example, +the aws terraform provider is built from ./terraform/aws. The terraform binary is built from ./terraform/terraform. +Each of the terraform providers is compressed into its own zip file to reduce the size of the installer binary. +Terraform can use the zipped terraform providers as is without the installer having to unzip the providers first. + +Building and zipping the terraform binaries significantly increases the time that it takes to build the installer +binary. However, they only need to be built when updates are made to the versions used. If you do not need or want to +re-build the terraform binaries, then set the SKIP_TERRAFORM environment variable to non-empty value. + +```shell +SKIP_TERRAFORM=1 ./hack/build.sh +``` + +### Embedding terraform binaries + +After the terraform providers are built, they are each copied into the ./pkg/terraform/providers/mirror directory. The +./pkg/terraform/providers directory is embedded into the installer. To make things simpler, we automatically assign +every terraform provider to the openshift/local registry and use a version of 1.0.0. This keeps us from having to track +the actual registry name of the provider or the actual version used. This does not have any effect on the behavior of +terraform since the installer tightly controls which providers are made available. As an example, when building for +linux on amd64, the aws terraform provider would be placed at +./pkg/terraform/providers/mirror/openshift/local/aws/terraform-provider-aws_1.0.0_linux_amd64.zip. The terraform binary +is copied into ./pkg/terraform/terraform to be embedded into the installer as well. + +### Extracting terraform binaries + +When the installer is provisioning the infrastructure for a cluster, the installer extracts the necessary terraform +binaries to the local file system. Every terraform stage must define which terraform providers are needed for the stage. +The installer will use that information to (1) build a version.tf file to tell terraform which providers to use and (2) +extract only the needed providers. + +For example, the "bootstrap" gcp stage requires the google and ignition terraform providers. The installer will extract +the terraform binary to ${stage-dir}/bin/terraform. The installer will extract the google and ignition terraform +providers to ${stage-dir}/plugins. The terraform exec is configured to use ${stage-dir}/plugins as the explicit +directory for the plugins. + +## Adding a new terraform provider + +To add a new terraform provider, create a directory under ./terraform. For example, if you want to add a terraform +provider named mycloud, then you would create the ./terraform/mycloud directory. + +### Public terraform provider + +If the terraform provider you want to add is a public terraform provider, create a tools.go file with an unnamed import +to the main package of the terraform provider. Add a go.mod file with a require statement to the terraform provider +module. + +For example, let's say that you are adding the mycloud terraform provider. The mycloud terraform provider is in the +github.com/hashicorp/terraform-provider-mycloud repository. The main package of the terraform provider is +github.com/hashicorp/terraform-provider-mycloud. The version of the terraform provider is v1.2.3. + +```go +// ./terraform/mycloud/tools.go +package main + +import ( + _ "github.com/hashicorp/terraform-provider-mycloud" +) +``` + +```go +// ./terraform/mycloud/go.mod +module aws + +go 1.17 + +require github.com/hashicorp/terraform-provider-mycloud v1.2.3 +``` + +After creating the directory and adding the tools.go and go.mod file, run the following commands in the mycloud +sub-pacakge. +1. `go mod tidy` +2. `go mod vendor` + +When there is a tools.go file, the Makefile will build the terraform provider from the package referenced in the unnamed +import in the tools.go file. For the example mycloud terraform provider, the terraform provider will be built from the +./terraform/mycloud/vendor/github.com/hashicorp/terraform/provider-mycloud directory. + +### Custom terraform provider + +If the terraform provider you want to add is a custom terraform provider, then create a main.go file for the custom +terraform provider. + +When there is a main.go file, the Makefile will build the terraform provider from the top-level package of the custom +terraform provider. + +## Update the version of terraform or a provider + +To update the version of terraform or a provider, modify the version in the go.mod file for the relevant sub-package. +To update the version of terraform, change the version of the required module in ./terraform/terraform/go.mod. To update +the version of the aws terraform provider, change the version of the required module in ./terraform/aws/go.mod. + +After updating the require statement in the go.mod file, perform the following actions in the sub-package. +1. Remove the require stanza in the go.mod file for the indirect requires. +2. Match the replaces in the go.mod file with the replaces from the go.mod file in the upstream terraform provider. +3. Run `go mod tidy`. +4. Run `go mod vendor`.