Skip to content

Commit

Permalink
refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
msvechla committed Jun 10, 2019
1 parent 4235479 commit b68b1ef
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 42 deletions.
21 changes: 6 additions & 15 deletions cli/cli_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ func checkDeprecated(command string, terragruntOptions *options.TerragruntOption
// terragrunt command
func runCommand(command string, terragruntOptions *options.TerragruntOptions) (finalEff error) {
if isMultiModuleCommand(command) {
return runMultiModuleCommand(command, terragruntOptions)
err := runMultiModuleCommand(command, terragruntOptions)

// wait until all detailed errors have been printed
<-configstack.DonePrintingDetailedError
return err
}
return runTerragrunt(terragruntOptions)
}
Expand Down Expand Up @@ -685,20 +689,7 @@ func applyAll(terragruntOptions *options.TerragruntOptions) error {
}

if shouldApplyAll {
stack.SetupErrorChannel()
go stack.CollectModuleErrors()
err := stack.Apply(terragruntOptions)
close(stack.ErrorChan)

terragruntOptions.Logger.Printf("Encountered the following root-causes: \n")
for module, errList := range stack.ErrorMap {
terragruntOptions.Logger.Printf("Module: %s \n", module)
for _, err := range errList {
terragruntOptions.Logger.Printf("- %s \n", err)
}
}

return err
return stack.Apply(terragruntOptions)
}

return nil
Expand Down
1 change: 0 additions & 1 deletion configstack/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type TerraformModule struct {
TerragruntOptions *options.TerragruntOptions
AssumeAlreadyApplied bool
FlagExcluded bool
ErrorChan chan map[string]error
}

// Render this module as a human-readable string
Expand Down
16 changes: 8 additions & 8 deletions configstack/running_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ type runningModule struct {
Dependencies map[string]*runningModule
NotifyWhenDone []*runningModule
FlagExcluded bool
ErrorChan chan map[string]error
OutStream bytes.Buffer
Writer io.Writer
}
Expand All @@ -53,7 +52,6 @@ func newRunningModule(module *TerraformModule) *runningModule {
Dependencies: map[string]*runningModule{},
NotifyWhenDone: []*runningModule{},
FlagExcluded: module.FlagExcluded,
ErrorChan: module.ErrorChan,
Writer: module.TerragruntOptions.Writer,
}
}
Expand Down Expand Up @@ -137,7 +135,6 @@ func removeFlagExcluded(modules map[string]*runningModule) map[string]*runningMo
Err: module.Err,
NotifyWhenDone: module.NotifyWhenDone,
Status: module.Status,
ErrorChan: module.ErrorChan,
Writer: module.Module.TerragruntOptions.Writer,
}

Expand Down Expand Up @@ -181,13 +178,17 @@ func collectErrors(modules map[string]*runningModule) error {
for _, module := range modules {
if module.Err != nil {
errs = append(errs, module.Err)
fmt.Printf("----- ERRROR TYPE %T ----- \n", module.Err)

// send all non dependency errors to the DetailedErrorChan
if _, isDepErr := module.Err.(DependencyFinishedWithError); !isDepErr {
module.ErrorChan <- map[string]error{module.Module.Path: fmt.Errorf("%s", module.OutStream.String())}
DetailedErrorChan <- map[string]string{module.Module.Path: module.OutStream.String()}
}
}
}

// close the DetailedErrorChan after all errors have been sent
close(DetailedErrorChan)

if len(errs) == 0 {
return nil
} else {
Expand Down Expand Up @@ -241,8 +242,6 @@ func (module *runningModule) runNow() error {

}

var separator = strings.Repeat("-", 132)

// Record that a module has finished executing and notify all of this module's dependencies
func (module *runningModule) moduleFinished(moduleErr error) {
if moduleErr == nil {
Expand All @@ -251,7 +250,8 @@ func (module *runningModule) moduleFinished(moduleErr error) {
module.Module.TerragruntOptions.Logger.Printf("Module %s has finished with an error: %v", module.Module.Path, moduleErr)
}

fmt.Fprintf(module.Writer, "%s\n%v\n\n%v\n", separator, module.Module.Path, module.OutStream.String())
// print the separated module output
fmt.Fprintf(module.Writer, "%s\n%v\n\n%v\n", OutputMessageSeparator, module.Module.Path, module.OutStream.String())

module.Status = Finished
module.Err = moduleErr
Expand Down
75 changes: 57 additions & 18 deletions configstack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@ import (
// Represents a stack of Terraform modules (i.e. folders with Terraform templates) that you can "spin up" or
// "spin down" in a single command
type Stack struct {
Path string
Modules []*TerraformModule
ErrorMap map[string][]error
ErrorChan chan map[string]error
Path string
Modules []*TerraformModule
}

var (
// DetailedErrorMap is a map which contains the module name and matching detailed error messages
DetailedErrorMap map[string][]string
// DetailedErrorChan is the channel which processes all detailed error messages
DetailedErrorChan chan map[string]string
// DonePrintingDetailedError is a channel which blocks the go routine until all detailed errors have been displayed
DonePrintingDetailedError chan bool
// OutputMessageSeparator is the string used for separating the different module outputs
OutputMessageSeparator = strings.Repeat("-", 132)
)

// Render this stack as a human-readable string
func (stack *Stack) String() string {
modules := []string{}
Expand Down Expand Up @@ -116,32 +125,21 @@ func FindStackInSubfolders(terragruntOptions *options.TerragruntOptions) (*Stack
// Set the command in the TerragruntOptions object of each module in this stack to the given command.
func (stack *Stack) setTerraformCommand(command []string) {
for _, module := range stack.Modules {
module.ErrorChan = stack.ErrorChan
module.TerragruntOptions.TerraformCliArgs = append(command, module.TerragruntOptions.TerraformCliArgs...)
module.TerragruntOptions.TerraformCommand = util.FirstArg(command)
}
}

func (stack *Stack) SetupErrorChannel() {
stack.ErrorMap = make(map[string][]error)
stack.ErrorChan = make(chan map[string]error)
}

func (stack *Stack) CollectModuleErrors() {
for errorMap := range stack.ErrorChan {
for module, err := range errorMap {
stack.ErrorMap[module] = append(stack.ErrorMap[module], err)
}
}
}

// Find all the Terraform modules in the folders that contain the given Terragrunt config files and assemble those
// modules into a Stack object that can be applied or destroyed in a single command
func createStackForTerragruntConfigPaths(path string, terragruntConfigPaths []string, terragruntOptions *options.TerragruntOptions, howThesePathsWereFound string) (*Stack, error) {
if len(terragruntConfigPaths) == 0 {
return nil, errors.WithStackTrace(NoTerraformModulesFound)
}

// configure the channels for collecting detailed error messages
setupDetailedErrorChannel(terragruntOptions)

modules, err := ResolveTerraformModules(terragruntConfigPaths, terragruntOptions, howThesePathsWereFound)
if err != nil {
return nil, err
Expand All @@ -155,6 +153,47 @@ func createStackForTerragruntConfigPaths(path string, terragruntConfigPaths []st
return stack, nil
}

// setupDetailedErrorChannel configures the channels responsible for storing detailed error messages
func setupDetailedErrorChannel(terragruntOptions *options.TerragruntOptions) {
DetailedErrorMap = make(map[string][]string)
DetailedErrorChan = make(chan map[string]string)
DonePrintingDetailedError = make(chan bool)

// process all detailed error messages
go collectModuleErrors(terragruntOptions)
}

// collectModuleErrors listens on the DetailedErrorChan and maps errors to their matching modules
func collectModuleErrors(terragruntOptions *options.TerragruntOptions) {
for {
errorMap, more := <-DetailedErrorChan
if more {
for module, err := range errorMap {
DetailedErrorMap[module] = append(DetailedErrorMap[module], err)
}
} else {
printDetailedErrorSummary(terragruntOptions)
DonePrintingDetailedError <- true
return
}
}
}

// printDetailedErrorSummary logs the detailed error messages
func printDetailedErrorSummary(terragruntOptions *options.TerragruntOptions) {
summaryMessage := []string{}
summaryMessage = append(summaryMessage, "Encountered the following root-causes:")
for module, errSlice := range DetailedErrorMap {
summaryMessage = append(summaryMessage, OutputMessageSeparator)
summaryMessage = append(summaryMessage, fmt.Sprintf("Module %s:", module))
for _, err := range errSlice {
summaryMessage = append(summaryMessage, err)
}
}

terragruntOptions.Logger.Printf("%s \n", strings.Join(summaryMessage, "\n"))
}

// Custom error types

var NoTerraformModulesFound = fmt.Errorf("Could not find any subfolders with Terragrunt configuration files")
Expand Down

0 comments on commit b68b1ef

Please sign in to comment.