Skip to content

Commit

Permalink
Prevent init command from parallel running if plugin cache is used (#…
Browse files Browse the repository at this point in the history
…2698)

* fix: prevent terraform `init` from running in parallel

* chore: docs update

* chore: docs update

* chore: improved plugin cache detection
  • Loading branch information
levkohimins authored Sep 1, 2023
1 parent ace8cbd commit ba56d84
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/_docs/04_reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ included directories with `terragrunt-include-dir`.
**Environment Variable**: `TERRAGRUNT_PARALLELISM`

When passed in, limit the number of modules that are run concurrently to this number during *-all commands.
The exception is the `terraform init` command, which is always executed sequentially if the [terraform plugin cache](https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache) is used. This is because the terraform plugin cache is not guaranteed to be concurrency safe.


### terragrunt-debug
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-cidr v1.1.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-versions v1.0.1 // indirect
github.com/armon/go-metrics v0.3.10 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
Expand Down Expand Up @@ -126,6 +127,7 @@ require (
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/panicwrap v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/owenrumney/go-sarif v1.1.1 // indirect
Expand All @@ -147,6 +149,7 @@ require (
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/term v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw=
github.com/apparentlymart/go-versions v1.0.1 h1:ECIpSn0adcYNsBfSRwdDdz9fWlL+S/6EUd9+irwkBgU=
github.com/apparentlymart/go-versions v1.0.1/go.mod h1:YF5j7IQtrOAOnsGkniupEA5bfCjzd7i14yu0shZavyM=
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
Expand Down Expand Up @@ -782,6 +783,7 @@ github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE=
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
Expand Down Expand Up @@ -1040,6 +1042,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
13 changes: 13 additions & 0 deletions shell/run_shell_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import (
"os/exec"
"os/signal"
"strings"
"sync"
"syscall"
"time"

"github.com/gruntwork-io/go-commons/collections"
"github.com/gruntwork-io/go-commons/errors"
"github.com/gruntwork-io/terragrunt/options"
"github.com/gruntwork-io/terragrunt/terraform"
"github.com/gruntwork-io/terragrunt/util"
"github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus"
Expand All @@ -31,6 +34,8 @@ var terraformCommandsThatNeedPty = []string{
"console",
}

var terraformInitMutex sync.Mutex

// Run the given Terraform command
func RunTerraformCommand(terragruntOptions *options.TerragruntOptions, args ...string) error {
needPTY, err := isTerraformCommandThatNeedsPty(args)
Expand Down Expand Up @@ -70,6 +75,14 @@ func RunShellCommandWithOutput(
command string,
args ...string,
) (*CmdOutput, error) {
// Terrafrom `init` command with the plugin cache directory is not guaranteed to be concurrency safe.
// The provider installer's behavior in environments with multiple terraform init calls is undefined.
// Thus, terraform `init` commands must be executed sequentially, even if `--terragrunt-parallelism` is greater than 1.
if command == "terraform" && collections.ListContainsElement(args, "init") && terraform.IsPluginCacheUsed() {
defer terraformInitMutex.Unlock()
terraformInitMutex.Lock()
}

terragruntOptions.Logger.Debugf("Running command: %s %s", command, strings.Join(args, " "))
if suppressStdout {
terragruntOptions.Logger.Debugf("Command output will be suppressed.")
Expand Down
18 changes: 18 additions & 0 deletions terraform/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package terraform

import (
"os"
"strings"

"github.com/hashicorp/terraform/command/cliconfig"
)

// IsPluginCacheUsed returns true if the terraform plugin cache dir is specified, https://developer.hashicorp.com/terraform/cli/config/config-file#provider-plugin-cache
func IsPluginCacheUsed() bool {
if strings.TrimSpace(os.Getenv("TF_PLUGIN_CACHE_DIR")) != "" {
return true
}

cfg, _ := cliconfig.LoadConfig()
return cfg.PluginCacheDir != ""
}

0 comments on commit ba56d84

Please sign in to comment.