@@ -16,12 +16,21 @@ import (
16
16
// Represents a stack of Terraform modules (i.e. folders with Terraform templates) that you can "spin up" or
17
17
// "spin down" in a single command
18
18
type Stack struct {
19
- Path string
20
- Modules []* TerraformModule
21
- ErrorMap map [string ][]error
22
- ErrorChan chan map [string ]error
19
+ Path string
20
+ Modules []* TerraformModule
23
21
}
24
22
23
+ var (
24
+ // DetailedErrorMap is a map which contains the module name and matching detailed error messages
25
+ DetailedErrorMap map [string ][]string
26
+ // DetailedErrorChan is the channel which processes all detailed error messages
27
+ DetailedErrorChan chan map [string ]string
28
+ // DonePrintingDetailedError is a channel which blocks the go routine until all detailed errors have been displayed
29
+ DonePrintingDetailedError chan bool
30
+ // OutputMessageSeparator is the string used for separating the different module outputs
31
+ OutputMessageSeparator = strings .Repeat ("-" , 132 )
32
+ )
33
+
25
34
// Render this stack as a human-readable string
26
35
func (stack * Stack ) String () string {
27
36
modules := []string {}
@@ -116,32 +125,21 @@ func FindStackInSubfolders(terragruntOptions *options.TerragruntOptions) (*Stack
116
125
// Set the command in the TerragruntOptions object of each module in this stack to the given command.
117
126
func (stack * Stack ) setTerraformCommand (command []string ) {
118
127
for _ , module := range stack .Modules {
119
- module .ErrorChan = stack .ErrorChan
120
128
module .TerragruntOptions .TerraformCliArgs = append (command , module .TerragruntOptions .TerraformCliArgs ... )
121
129
module .TerragruntOptions .TerraformCommand = util .FirstArg (command )
122
130
}
123
131
}
124
132
125
- func (stack * Stack ) SetupErrorChannel () {
126
- stack .ErrorMap = make (map [string ][]error )
127
- stack .ErrorChan = make (chan map [string ]error )
128
- }
129
-
130
- func (stack * Stack ) CollectModuleErrors () {
131
- for errorMap := range stack .ErrorChan {
132
- for module , err := range errorMap {
133
- stack .ErrorMap [module ] = append (stack .ErrorMap [module ], err )
134
- }
135
- }
136
- }
137
-
138
133
// Find all the Terraform modules in the folders that contain the given Terragrunt config files and assemble those
139
134
// modules into a Stack object that can be applied or destroyed in a single command
140
135
func createStackForTerragruntConfigPaths (path string , terragruntConfigPaths []string , terragruntOptions * options.TerragruntOptions , howThesePathsWereFound string ) (* Stack , error ) {
141
136
if len (terragruntConfigPaths ) == 0 {
142
137
return nil , errors .WithStackTrace (NoTerraformModulesFound )
143
138
}
144
139
140
+ // configure the channels for collecting detailed error messages
141
+ setupDetailedErrorChannel (terragruntOptions )
142
+
145
143
modules , err := ResolveTerraformModules (terragruntConfigPaths , terragruntOptions , howThesePathsWereFound )
146
144
if err != nil {
147
145
return nil , err
@@ -155,6 +153,47 @@ func createStackForTerragruntConfigPaths(path string, terragruntConfigPaths []st
155
153
return stack , nil
156
154
}
157
155
156
+ // setupDetailedErrorChannel configures the channels responsible for storing detailed error messages
157
+ func setupDetailedErrorChannel (terragruntOptions * options.TerragruntOptions ) {
158
+ DetailedErrorMap = make (map [string ][]string )
159
+ DetailedErrorChan = make (chan map [string ]string )
160
+ DonePrintingDetailedError = make (chan bool )
161
+
162
+ // process all detailed error messages
163
+ go collectModuleErrors (terragruntOptions )
164
+ }
165
+
166
+ // collectModuleErrors listens on the DetailedErrorChan and maps errors to their matching modules
167
+ func collectModuleErrors (terragruntOptions * options.TerragruntOptions ) {
168
+ for {
169
+ errorMap , more := <- DetailedErrorChan
170
+ if more {
171
+ for module , err := range errorMap {
172
+ DetailedErrorMap [module ] = append (DetailedErrorMap [module ], err )
173
+ }
174
+ } else {
175
+ printDetailedErrorSummary (terragruntOptions )
176
+ DonePrintingDetailedError <- true
177
+ return
178
+ }
179
+ }
180
+ }
181
+
182
+ // printDetailedErrorSummary logs the detailed error messages
183
+ func printDetailedErrorSummary (terragruntOptions * options.TerragruntOptions ) {
184
+ summaryMessage := []string {}
185
+ summaryMessage = append (summaryMessage , "Encountered the following root-causes:" )
186
+ for module , errSlice := range DetailedErrorMap {
187
+ summaryMessage = append (summaryMessage , OutputMessageSeparator )
188
+ summaryMessage = append (summaryMessage , fmt .Sprintf ("Module %s:" , module ))
189
+ for _ , err := range errSlice {
190
+ summaryMessage = append (summaryMessage , err )
191
+ }
192
+ }
193
+
194
+ terragruntOptions .Logger .Printf ("%s \n " , strings .Join (summaryMessage , "\n " ))
195
+ }
196
+
158
197
// Custom error types
159
198
160
199
var NoTerraformModulesFound = fmt .Errorf ("Could not find any subfolders with Terragrunt configuration files" )
0 commit comments