From 6f60f1b269e1508125dd0df571d0326b86d283d4 Mon Sep 17 00:00:00 2001 From: Eugene Dementyev Date: Mon, 17 Jun 2019 15:00:28 +1200 Subject: [PATCH] Allows setting terraform_binary in terragrunt.hcl Related to #743 --- README.md | 6 +++ .../upgrading_to_terragrunt_0.19.x.md | 13 ++++++ cli/args.go | 2 +- cli/cli_app.go | 11 ++++- config/config.go | 40 ++++++++++------- options/options.go | 43 ++++++++++--------- 6 files changed, 77 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index e9a20a14b4..e981103bf3 100644 --- a/README.md +++ b/README.md @@ -1988,6 +1988,12 @@ The `skip` flag must be set explicitly in terragrunt modules that should be skip `terragrunt.hcl` file that is included by another `terragrunt.hcl` file, only the `terragrunt.hcl` file that explicitly set `skip = true` will be skipped. +#### terraform_binary + +The terragrunt `terraform_binary` string option can be used to override the default terraform binary path (which is `terraform`). + +The precedence is as follows: `--terragrunt-tfpath` command line option -> `TERRAGRUNT_TFPATH` env variable -> `terragrunt.hcl` in the module directory -> included `terragrunt.hcl` + ### Clearing the Terragrunt cache Terragrunt creates a `.terragrunt-cache` folder in the current working directory as its scratch directory. It downloads diff --git a/_docs/migration_guides/upgrading_to_terragrunt_0.19.x.md b/_docs/migration_guides/upgrading_to_terragrunt_0.19.x.md index 635f2b7891..44ed9ee94b 100644 --- a/_docs/migration_guides/upgrading_to_terragrunt_0.19.x.md +++ b/_docs/migration_guides/upgrading_to_terragrunt_0.19.x.md @@ -27,6 +27,7 @@ to Terragrunt 0.19.x and newer: 1. [Use first-class expressions](#use-first-class-expressions) 1. [Check attributes vs blocks usage](#check-attributes-vs-blocks-usage) 1. [Rename a few built-in functions ](#rename-a-few-built-in-functions) +1. [Use terraform \<0.12](#use-older-terraform) Check out [this PR in the terragrunt-infrastructure-live-example repo](https://github.com/gruntwork-io/terragrunt-infrastructure-live-example/pull/17) for an example of what the code @@ -272,3 +273,15 @@ Two built-in functions were renamed: 1. `get_parent_tfvars_dir()` is now called `get_parent_terragrunt_dir()`. Make sure to make the corresponding updates in your `terragrunt.hcl` file! + +### Use older Terraform + +Although it is not officiall supported and not tested, it is still possible to use terraform<0.12 with terragrunt >=0.19. + +Just install a different version of terraform into a directory of your choice outside of `PATH` and specify path to the binary in `terragrunt.hcl` as `terraform_binary`: + +```hcl + +terraform_binary = "~/bin/terraform-v11/terraform" + +``` diff --git a/cli/args.go b/cli/args.go index 8da1d3d9ec..aba83fe34f 100644 --- a/cli/args.go +++ b/cli/args.go @@ -71,7 +71,7 @@ func parseTerragruntOptionsFromArgs(args []string, writer, errWriter io.Writer) return nil, err } if terraformPath == "" { - terraformPath = "terraform" + terraformPath = options.TERRAFORM_DEFAULT_PATH } terraformSource, err := parseStringArg(args, OPT_TERRAGRUNT_SOURCE, os.Getenv("TERRAGRUNT_SOURCE")) diff --git a/cli/cli_app.go b/cli/cli_app.go index d28d53bc86..2807e66d5b 100644 --- a/cli/cli_app.go +++ b/cli/cli_app.go @@ -146,8 +146,8 @@ AUTHOR(S): var MODULE_REGEX = regexp.MustCompile(`module[[:blank:]]+".+"`) // This uses the constraint syntax from https://github.com/hashicorp/go-version -// This version of Terragrunt only works with Terraform 0.12.0 and above -const DEFAULT_TERRAFORM_VERSION_CONSTRAINT = ">= v0.12.0" +// This version of Terragrunt was tested to work with Terraform 0.12.0 and above only +const DEFAULT_TERRAFORM_VERSION_CONSTRAINT = ">= v0.11.0" const TERRAFORM_EXTENSION_GLOB = "*.tf" @@ -237,10 +237,17 @@ func runTerragrunt(terragruntOptions *options.TerragruntOptions) error { } terragruntConfig, err := config.ReadTerragruntConfig(terragruntOptions) + if err != nil { return err } + if terragruntOptions.TerraformPath == options.TERRAFORM_DEFAULT_PATH { // not changed from default + if terragruntConfig.TerraformBinary != "" { + terragruntOptions.TerraformPath = terragruntConfig.TerraformBinary + } + } + if terragruntConfig.Skip { terragruntOptions.Logger.Printf("Skipping terragrunt module %s due to skip = true.", terragruntOptions.TerragruntConfigPath) diff --git a/config/config.go b/config/config.go index 51c1184966..b338a3e976 100644 --- a/config/config.go +++ b/config/config.go @@ -21,13 +21,14 @@ const DefaultTerragruntConfigPath = "terragrunt.hcl" // TerragruntConfig represents a parsed and expanded configuration type TerragruntConfig struct { - Terraform *TerraformConfig - RemoteState *remote.RemoteState - Dependencies *ModuleDependencies - PreventDestroy bool - Skip bool - IamRole string - Inputs map[string]interface{} + Terraform *TerraformConfig + TerraformBinary string + RemoteState *remote.RemoteState + Dependencies *ModuleDependencies + PreventDestroy bool + Skip bool + IamRole string + Inputs map[string]interface{} } func (conf *TerragruntConfig) String() string { @@ -37,14 +38,15 @@ func (conf *TerragruntConfig) String() string { // terragruntConfigFile represents the configuration supported in a Terragrunt configuration file (i.e. // terragrunt.hcl) type terragruntConfigFile struct { - Terraform *TerraformConfig `hcl:"terraform,block"` - Inputs *cty.Value `hcl:"inputs,attr"` - Include *IncludeConfig `hcl:"include,block"` - RemoteState *remoteStateConfigFile `hcl:"remote_state,block"` - Dependencies *ModuleDependencies `hcl:"dependencies,block"` - PreventDestroy *bool `hcl:"prevent_destroy,attr"` - Skip *bool `hcl:"skip,attr"` - IamRole *string `hcl:"iam_role,attr"` + Terraform *TerraformConfig `hcl:"terraform,block"` + TerraformBinary *string `hcl:"terraform_binary,attr"` + Inputs *cty.Value `hcl:"inputs,attr"` + Include *IncludeConfig `hcl:"include,block"` + RemoteState *remoteStateConfigFile `hcl:"remote_state,block"` + Dependencies *ModuleDependencies `hcl:"dependencies,block"` + PreventDestroy *bool `hcl:"prevent_destroy,attr"` + Skip *bool `hcl:"skip,attr"` + IamRole *string `hcl:"iam_role,attr"` } // Configuration for Terraform remote state as parsed from a terragrunt.hcl config file @@ -338,6 +340,10 @@ func mergeConfigWithIncludedConfig(config *TerragruntConfig, includedConfig *Ter includedConfig.IamRole = config.IamRole } + if config.TerraformBinary != "" { + includedConfig.TerraformBinary = config.TerraformBinary + } + if config.Inputs != nil { includedConfig.Inputs = mergeInputs(config.Inputs, includedConfig.Inputs) } @@ -490,6 +496,10 @@ func convertToTerragruntConfig(terragruntConfigFromFile *terragruntConfigFile, c terragruntConfig.Terraform = terragruntConfigFromFile.Terraform terragruntConfig.Dependencies = terragruntConfigFromFile.Dependencies + if terragruntConfigFromFile.TerraformBinary != nil { + terragruntConfig.TerraformBinary = *terragruntConfigFromFile.TerraformBinary + } + if terragruntConfigFromFile.PreventDestroy != nil { terragruntConfig.PreventDestroy = *terragruntConfigFromFile.PreventDestroy } diff --git a/options/options.go b/options/options.go index e07db7a0e9..a3d95acaf8 100644 --- a/options/options.go +++ b/options/options.go @@ -21,6 +21,9 @@ var TERRAFORM_COMMANDS_WITH_SUBCOMMAND = []string{ const DEFAULT_MAX_FOLDERS_TO_CHECK = 100 +// TERRAFORM_DEFAULT_PATH just takes terraform from the path +const TERRAFORM_DEFAULT_PATH = "terraform" + const TerragruntCacheDir = ".terragrunt-cache" // TerragruntOptions represents options that configure the behavior of the Terragrunt program @@ -121,7 +124,7 @@ func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, erro return &TerragruntOptions{ TerragruntConfigPath: terragruntConfigPath, - TerraformPath: "terraform", + TerraformPath: TERRAFORM_DEFAULT_PATH, TerraformCommand: "", AutoInit: true, NonInteractive: false, @@ -134,15 +137,15 @@ func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, erro DownloadDir: downloadDir, IgnoreDependencyErrors: false, IgnoreExternalDependencies: false, - Writer: os.Stdout, - ErrWriter: os.Stderr, - MaxFoldersToCheck: DEFAULT_MAX_FOLDERS_TO_CHECK, - AutoRetry: true, - MaxRetryAttempts: DEFAULT_MAX_RETRY_ATTEMPTS, - Sleep: DEFAULT_SLEEP, - RetryableErrors: util.CloneStringList(RETRYABLE_ERRORS), - ExcludeDirs: []string{}, - IncludeDirs: []string{}, + Writer: os.Stdout, + ErrWriter: os.Stderr, + MaxFoldersToCheck: DEFAULT_MAX_FOLDERS_TO_CHECK, + AutoRetry: true, + MaxRetryAttempts: DEFAULT_MAX_RETRY_ATTEMPTS, + Sleep: DEFAULT_SLEEP, + RetryableErrors: util.CloneStringList(RETRYABLE_ERRORS), + ExcludeDirs: []string{}, + IncludeDirs: []string{}, RunTerragrunt: func(terragruntOptions *TerragruntOptions) error { return errors.WithStackTrace(RunTerragruntCommandNotSet) }, @@ -201,16 +204,16 @@ func (terragruntOptions *TerragruntOptions) Clone(terragruntConfigPath string) * IamRole: terragruntOptions.IamRole, IgnoreDependencyErrors: terragruntOptions.IgnoreDependencyErrors, IgnoreExternalDependencies: terragruntOptions.IgnoreExternalDependencies, - Writer: terragruntOptions.Writer, - ErrWriter: terragruntOptions.ErrWriter, - MaxFoldersToCheck: terragruntOptions.MaxFoldersToCheck, - AutoRetry: terragruntOptions.AutoRetry, - MaxRetryAttempts: terragruntOptions.MaxRetryAttempts, - Sleep: terragruntOptions.Sleep, - RetryableErrors: util.CloneStringList(terragruntOptions.RetryableErrors), - ExcludeDirs: terragruntOptions.ExcludeDirs, - IncludeDirs: terragruntOptions.IncludeDirs, - RunTerragrunt: terragruntOptions.RunTerragrunt, + Writer: terragruntOptions.Writer, + ErrWriter: terragruntOptions.ErrWriter, + MaxFoldersToCheck: terragruntOptions.MaxFoldersToCheck, + AutoRetry: terragruntOptions.AutoRetry, + MaxRetryAttempts: terragruntOptions.MaxRetryAttempts, + Sleep: terragruntOptions.Sleep, + RetryableErrors: util.CloneStringList(terragruntOptions.RetryableErrors), + ExcludeDirs: terragruntOptions.ExcludeDirs, + IncludeDirs: terragruntOptions.IncludeDirs, + RunTerragrunt: terragruntOptions.RunTerragrunt, } }