Skip to content

Commit

Permalink
Updated after review
Browse files Browse the repository at this point in the history
Apart from small fixes, this:
- adds deprecation check for command line arguments, and gives a
  warning if `--terragrunt-debug` is used;
- imports `logrus` under its own name to avoid confusion;

Related: gruntwork-io#432
  • Loading branch information
amnk committed Feb 2, 2021
1 parent fb27578 commit c750278
Show file tree
Hide file tree
Showing 18 changed files with 99 additions and 56 deletions.
29 changes: 27 additions & 2 deletions cli/args.go
Expand Up @@ -123,6 +123,16 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri

debug := parseBooleanArg(args, OPT_TERRAGRUNT_DEBUG, false)

// Those correspond to logrus levels
logLevel, err := parseStringArg(args, OPT_TERRAGRUNT_LOGLEVEL, options.DEFAULT_LOG_LEVEL)
if err != nil {
return nil, err
}

if debug {
logLevel = "debug"
}

opts, err := options.NewTerragruntOptions(filepath.ToSlash(terragruntConfigPath))
if err != nil {
return nil, err
Expand All @@ -143,7 +153,7 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri
opts.TerraformCommand = util.FirstArg(opts.TerraformCliArgs)
opts.WorkingDir = filepath.ToSlash(workingDir)
opts.DownloadDir = filepath.ToSlash(downloadDir)
opts.Logger = util.CreateLogEntryWithWriter(errWriter, "", debug)
opts.Logger = util.CreateLogEntryWithWriter(errWriter, "", logLevel)
opts.RunTerragrunt = RunTerragrunt
opts.Source = terraformSource
opts.SourceUpdate = sourceUpdate
Expand All @@ -169,7 +179,6 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri
opts.Parallelism = parallelism
opts.Check = parseBooleanArg(args, OPT_TERRAGRUNT_CHECK, os.Getenv("TERRAGRUNT_CHECK") == "true")
opts.HclFile = filepath.ToSlash(terragruntHclFilePath)
opts.Debug = debug
opts.AwsProviderPatchOverrides = awsProviderPatchOverrides

return opts, nil
Expand Down Expand Up @@ -290,11 +299,24 @@ func filterTerragruntArgs(args []string) []string {
return out
}

// isDeprecatedOption checks if provided option is deprecated, and returns its substitution
// TODO: ideally, it would be better to make this return (string, err)
func isDeprecatedOption(optionName string) string {
newOption, deprecated := DEPRECATED_ARGUMENTS[optionName]
if deprecated {
logger := util.CreateLogEntry("", "debug")
logger.Warnf("Command line option %s is deprecated, please consider using %s", optionName, newOption)
return newOption
}
return optionName
}

// Find a boolean argument (e.g. --foo) of the given name in the given list of arguments. If it's present, return true.
// If it isn't, return defaultValue.
func parseBooleanArg(args []string, argName string, defaultValue bool) bool {
for _, arg := range args {
if arg == fmt.Sprintf("--%s", argName) {
argName = isDeprecatedOption(argName)
return true
}
}
Expand All @@ -306,6 +328,7 @@ func parseBooleanArg(args []string, argName string, defaultValue bool) bool {
func parseStringArg(args []string, argName string, defaultValue string) (string, error) {
for i, arg := range args {
if arg == fmt.Sprintf("--%s", argName) {
argName = isDeprecatedOption(argName)
if (i + 1) < len(args) {
return args[i+1], nil
} else {
Expand All @@ -321,6 +344,7 @@ func parseStringArg(args []string, argName string, defaultValue string) (string,
func parseIntArg(args []string, argName string, envValue string, envProvided bool, defaultValue int) (int, error) {
for i, arg := range args {
if arg == fmt.Sprintf("--%s", argName) {
argName = isDeprecatedOption(argName)
if (i + 1) < len(args) {
return strconv.Atoi(args[i+1])
} else {
Expand All @@ -342,6 +366,7 @@ func parseMultiStringArg(args []string, argName string, defaultValue []string) (

for i, arg := range args {
if arg == fmt.Sprintf("--%s", argName) {
argName = isDeprecatedOption(argName)
if (i + 1) < len(args) {
stringArgs = append(stringArgs, args[i+1])
} else {
Expand Down
13 changes: 11 additions & 2 deletions cli/cli_app.go
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
log "github.com/sirupsen/logrus"

"regexp"
"strings"
"time"
Expand All @@ -20,6 +20,7 @@ import (
"github.com/gruntwork-io/terragrunt/util"
"github.com/mattn/go-zglob"
"github.com/urfave/cli"
"github.com/sirupsen/logrus"
)

const OPT_TERRAGRUNT_CONFIG = "terragrunt-config"
Expand All @@ -44,6 +45,7 @@ const OPT_TERRAGRUNT_CHECK = "terragrunt-check"
const OPT_TERRAGRUNT_HCLFMT_FILE = "terragrunt-hclfmt-file"
const OPT_TERRAGRUNT_DEBUG = "terragrunt-debug"
const OPT_TERRAGRUNT_OVERRIDE_ATTR = "terragrunt-override-attr"
const OPT_TERRAGRUNT_LOGLEVEL="terragrunt-log-level"

var ALL_TERRAGRUNT_BOOLEAN_OPTS = []string{
OPT_NON_INTERACTIVE,
Expand Down Expand Up @@ -127,6 +129,11 @@ var TERRAFORM_COMMANDS_THAT_DO_NOT_NEED_INIT = []string{
"graph-dependencies",
}

// DEPRECATED_ARGUMENTS is a map of deprecated arguments to the argument that replace them.
var DEPRECATED_ARGUMENTS = map[string]string{
OPT_TERRAGRUNT_DEBUG: OPT_TERRAGRUNT_LOGLEVEL,
}

// Struct is output as JSON by 'terragrunt-info':
type TerragruntInfoGroup struct {
ConfigPath string
Expand Down Expand Up @@ -246,6 +253,8 @@ func runApp(cliContext *cli.Context) (finalErr error) {
return err
}

terragruntOptions.Logger.Debugf("Terragrunt Version: %s", cliContext.App.Version)

shell.PrepareConsole(terragruntOptions)

givenCommand := cliContext.Args().First()
Expand Down Expand Up @@ -601,7 +610,7 @@ func shouldCopyLockFile(args []string) bool {

// Terraform 0.14 now generates a lock file when you run `terraform init`.
// If any such file exists, this function will copy the lock file to the destination folder
func copyLockFile(sourceFolder string, destinationFolder string, logger *log.Entry) error {
func copyLockFile(sourceFolder string, destinationFolder string, logger *logrus.Entry) error {
sourceLockFilePath := util.JoinPath(sourceFolder, util.TerraformLockFile)
destinationLockFilePath := util.JoinPath(destinationFolder, util.TerraformLockFile)

Expand Down
6 changes: 3 additions & 3 deletions cli/download_source.go
Expand Up @@ -3,7 +3,6 @@ package cli
import (
"fmt"
"io/ioutil"
log "github.com/sirupsen/logrus"
"net/url"
"os"
"regexp"
Expand All @@ -16,6 +15,7 @@ import (
"github.com/gruntwork-io/terragrunt/options"
"github.com/gruntwork-io/terragrunt/util"
"github.com/hashicorp/go-getter"
"github.com/sirupsen/logrus"
urlhelper "github.com/hashicorp/go-getter/helper/url"
)

Expand Down Expand Up @@ -185,7 +185,7 @@ func writeVersionFile(terraformSource *TerraformSource) error {
// 1. Always download source URLs pointing to local file paths.
// 2. Only download source URLs pointing to remote paths if /T/W/H doesn't already exist or, if it does exist, if the
// version number in /T/W/H/.terragrunt-source-version doesn't match the current version.
func ProcessTerraformSource(source string, downloadDir string, workingDir string, logger *log.Entry) (*TerraformSource, error) {
func ProcessTerraformSource(source string, downloadDir string, workingDir string, logger *logrus.Entry) (*TerraformSource, error) {

canonicalWorkingDir, err := util.CanonicalPath(workingDir, "")
if err != nil {
Expand Down Expand Up @@ -279,7 +279,7 @@ func getForcedGetter(sourceUrl string) (string, string) {
// (//), which typically represents the root of a modules repo (e.g. github.com/foo/infrastructure-modules) and the
// path is everything after the double slash. If there is no double-slash in the URL, the root repo is the entire
// sourceUrl and the path is an empty string.
func splitSourceUrl(sourceUrl *url.URL, logger *log.Entry) (*url.URL, string, error) {
func splitSourceUrl(sourceUrl *url.URL, logger *logrus.Entry) (*url.URL, string, error) {
pathSplitOnDoubleSlash := strings.SplitN(sourceUrl.Path, "//", 2)

if len(pathSplitOnDoubleSlash) > 1 {
Expand Down
4 changes: 2 additions & 2 deletions codegen/generate.go
Expand Up @@ -87,7 +87,7 @@ func WriteToFile(terragruntOptions *options.TerragruntOptions, basePath string,
if err := ioutil.WriteFile(targetPath, []byte(contentsToWrite), 0644); err != nil {
return errors.WithStackTrace(err)
}
terragruntOptions.Logger.Infof("Generated file %s.", targetPath)
terragruntOptions.Logger.Debugf("Generated file %s.", targetPath)
return nil
}

Expand All @@ -114,7 +114,7 @@ func shouldContinueWithFileExists(terragruntOptions *options.TerragruntOptions,
return false, err
}
if !wasGenerated {
terragruntOptions.Logger.Debugf("ERROR: The file path %s already exists and was not generated by terragrunt.", path)
terragruntOptions.Logger.Errorf("ERROR: The file path %s already exists and was not generated by terragrunt.", path)
return false, errors.WithStackTrace(GenerateFileExistsError{path: path})
}
// Since file was generated by terragrunt, continue.
Expand Down
4 changes: 2 additions & 2 deletions config/config_helpers.go
Expand Up @@ -210,9 +210,9 @@ func runCommand(args []string, include *IncludeConfig, terragruntOptions *option
}

if suppressOutput {
terragruntOptions.Logger.Infof("run_cmd output: [REDACTED]")
terragruntOptions.Logger.Debugf("run_cmd output: [REDACTED]")
} else {
terragruntOptions.Logger.Infof("run_cmd output: [%s]", cmdOutput.Stdout)
terragruntOptions.Logger.Debugf("run_cmd output: [%s]", cmdOutput.Stdout)
}

return cmdOutput.Stdout, nil
Expand Down
4 changes: 2 additions & 2 deletions config/config_test.go
Expand Up @@ -376,7 +376,7 @@ include {
opts := options.TerragruntOptions{
TerragruntConfigPath: "../test/fixture-parent-folders/terragrunt-in-root/child/sub-child/sub-sub-child/" + DefaultTerragruntConfigPath,
NonInteractive: true,
Logger: util.CreateLogEntry("", false),
Logger: util.CreateLogEntry("", "warn"),
}

terragruntConfig, err := ParseConfigString(config, &opts, nil, opts.TerragruntConfigPath)
Expand Down Expand Up @@ -1251,7 +1251,7 @@ terraform {
TerragruntConfigPath: "../test/fixture-parent-folders/terragrunt-in-root/child/" + DefaultTerragruntConfigPath,
NonInteractive: true,
MaxFoldersToCheck: 5,
Logger: util.CreateLogEntry("", false),
Logger: util.CreateLogEntry("", "warn"),
}

terragruntConfig, err := ParseConfigString(config, &opts, nil, DefaultTerragruntConfigPath)
Expand Down
10 changes: 5 additions & 5 deletions config/locals.go
Expand Up @@ -64,7 +64,7 @@ func evaluateLocalsBlock(

locals, diags := decodeLocalsBlock(localsBlock)
if diags.HasErrors() {
terragruntOptions.Logger.Debugf("Encountered error while decoding locals block into name expression pairs.")
terragruntOptions.Logger.Errorf("Encountered error while decoding locals block into name expression pairs.")
diagsWriter.WriteDiagnostics(diags)
return nil, errors.WithStackTrace(diags)
}
Expand All @@ -90,15 +90,15 @@ func evaluateLocalsBlock(
diagsWriter,
)
if err != nil {
terragruntOptions.Logger.Debugf("Encountered error while evaluating locals.")
terragruntOptions.Logger.Errorf("Encountered error while evaluating locals.")
return nil, err
}
}
if len(locals) > 0 {
// This is an error because we couldn't evaluate all locals
terragruntOptions.Logger.Debugf("Not all locals could be evaluated:")
terragruntOptions.Logger.Errorf("Not all locals could be evaluated:")
for _, local := range locals {
terragruntOptions.Logger.Debugf("\t- %s", local.Name)
terragruntOptions.Logger.Errorf("\t- %s", local.Name)
}
return nil, errors.WithStackTrace(CouldNotEvaluateAllLocalsError{})
}
Expand Down Expand Up @@ -135,7 +135,7 @@ func attemptEvaluateLocals(

evaluatedLocalsAsCty, err := convertValuesMapToCtyVal(evaluatedLocals)
if err != nil {
terragruntOptions.Logger.Debugf("Could not convert evaluated locals to the execution context to evaluate additional locals")
terragruntOptions.Logger.Errorf("Could not convert evaluated locals to the execution context to evaluate additional locals")
return nil, evaluatedLocals, false, err
}
evalCtx := CreateTerragruntEvalContext(
Expand Down
4 changes: 2 additions & 2 deletions configstack/running_module.go
Expand Up @@ -218,9 +218,9 @@ func (module *runningModule) waitForDependencies() error {

if doneDependency.Err != nil {
if module.Module.TerragruntOptions.IgnoreDependencyErrors {
module.Module.TerragruntOptions.Logger.Debugf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too. However, because of --terragrunt-ignore-dependency-errors, module %s will run anyway.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, module.Module.Path)
module.Module.TerragruntOptions.Logger.Errorf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too. However, because of --terragrunt-ignore-dependency-errors, module %s will run anyway.", doneDependency.Module.Path, module.Module.Path, module.Module.Path, module.Module.Path)
} else {
module.Module.TerragruntOptions.Logger.Debugf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too.", doneDependency.Module.Path, module.Module.Path, module.Module.Path)
module.Module.TerragruntOptions.Logger.Errorf("Dependency %s of module %s just finished with an error. Module %s will have to return an error too.", doneDependency.Module.Path, module.Module.Path, module.Module.Path)
return DependencyFinishedWithError{module.Module, doneDependency.Module, doneDependency.Err}
}
} else {
Expand Down
10 changes: 5 additions & 5 deletions docs/_docs/02_features/debugging.md
Expand Up @@ -16,11 +16,11 @@ Terragrunt and Terraform usually play well together in helping you
write DRY, re-usable infrastructure. But how do we figure out what
went wrong in the rare case that they _don't_ play well?

Terragrunt provides a debug mode you can access through the `--terragrunt-debug`
Terragrunt provides a way to configure logging level through the `--terragrunt-log-level`
command flag. For example you could use it like this to debug an `apply`
that's producing unexpected output:

$ terragrunt apply --terragrunt-debug
$ terragrunt apply --terragrunt-log-level debug

Running this command will do two things for you:
- Output a file named `terragrunt-debug.tfvars.json` to your terragrunt working
Expand All @@ -30,7 +30,7 @@ Running this command will do two things for you:
`terragrunt`. This will help you to determine where the problem's root cause
lies.

The flag's goal is to help you determine which of these three major areas is the
Using "debug" log-level is helpful when you want determine which of these three major areas is the
root cause of your problem:
1. Misconfiguration of your infrastructure code.
2. An error in `terragrunt`.
Expand Down Expand Up @@ -93,7 +93,7 @@ You perform a `terragrunt apply`, and find that `outputs.task_ids` has 7
elements, but you know that the cluster only has 4 VMs in it! What's happening?
Let's figure it out. Run this:

$ terragrunt apply --terragrunt-debug
$ terragrunt apply --terragrunt-log-level debug

After applying, you will see this output on standard error

Expand Down Expand Up @@ -126,7 +126,7 @@ Oops! It says `max` when it should be `min`. If we fix `ecs-cluster/outputs.tf`
we should be golden! We fix the problem in time to take a nice afternoon walk in
the sun.

In this example we've seen how `terragrunt debug` can help us root cause issues
In this example we've seen how setting `debug` log-level can help us root cause issues
in dependency and local variable resolution.

<!-- See
Expand Down
2 changes: 1 addition & 1 deletion main.go
Expand Up @@ -30,7 +30,7 @@ func checkForErrorsAndExit(err error) {
if err == nil {
os.Exit(0)
} else {
logger := util.CreateLogEntry("", true)
logger := util.CreateLogEntry("", "debug")
if os.Getenv("TERRAGRUNT_DEBUG") != "" {
logger.Errorln(errors.PrintErrorWithStackTrace(err))
} else {
Expand Down
17 changes: 12 additions & 5 deletions options/options.go
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/gruntwork-io/terragrunt/errors"
"github.com/gruntwork-io/terragrunt/util"
"github.com/hashicorp/go-version"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus"
)

var TERRAFORM_COMMANDS_WITH_SUBCOMMAND = []string{
Expand All @@ -28,6 +28,9 @@ const DEFAULT_PARALLELISM = math.MaxInt32
// TERRAFORM_DEFAULT_PATH just takes terraform from the path
const TERRAFORM_DEFAULT_PATH = "terraform"

// DEFAULT_LOG_LEVEL defines default log level for Terragrunt
const DEFAULT_LOG_LEVEL = "warn"

const TerragruntCacheDir = ".terragrunt-cache"

const DefaultTFDataDir = ".terraform"
Expand Down Expand Up @@ -71,7 +74,10 @@ type TerragruntOptions struct {
WorkingDir string

// Basic log entry
Logger *log.Entry
Logger *logrus.Entry

// Log level
LogLevel string

// Environment variables at runtime
Env map[string]string
Expand Down Expand Up @@ -159,7 +165,7 @@ type TerragruntOptions struct {

// Create a new TerragruntOptions object with reasonable defaults for real usage
func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, error) {
logger := util.CreateLogEntry("", false)
logger := util.CreateLogEntry("", DEFAULT_LOG_LEVEL)

workingDir, downloadDir, err := DefaultWorkingAndDownloadDirs(terragruntConfigPath)
if err != nil {
Expand All @@ -176,6 +182,7 @@ func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, erro
TerraformCliArgs: []string{},
WorkingDir: workingDir,
Logger: logger,
LogLevel: DEFAULT_LOG_LEVEL,
Env: map[string]string{},
Source: "",
SourceUpdate: false,
Expand Down Expand Up @@ -219,7 +226,7 @@ func NewTerragruntOptionsForTest(terragruntConfigPath string) (*TerragruntOption
opts, err := NewTerragruntOptions(terragruntConfigPath)

if err != nil {
logger := util.CreateLogEntry("", false)
logger := util.CreateLogEntry("", "warn")
logger.Printf("error: %v\n", errors.WithStackTrace(err))
return nil, err
}
Expand Down Expand Up @@ -248,7 +255,7 @@ func (terragruntOptions *TerragruntOptions) Clone(terragruntConfigPath string) *
NonInteractive: terragruntOptions.NonInteractive,
TerraformCliArgs: util.CloneStringList(terragruntOptions.TerraformCliArgs),
WorkingDir: workingDir,
Logger: util.CreateLogEntryWithWriter(terragruntOptions.ErrWriter, workingDir, terragruntOptions.Debug),
Logger: util.CreateLogEntryWithWriter(terragruntOptions.ErrWriter, workingDir, terragruntOptions.LogLevel),
Env: util.CloneStringMap(terragruntOptions.Env),
Source: terragruntOptions.Source,
SourceUpdate: terragruntOptions.SourceUpdate,
Expand Down
1 change: 0 additions & 1 deletion remote/remote_state.go
Expand Up @@ -112,7 +112,6 @@ func (remoteState *RemoteState) differsFrom(existingBackend *TerraformBackend, t
}

if !terraformStateConfigEqual(existingBackend.Config, remoteState.Config) {
terragruntOptions.Logger.Infof("Backend config has changed (Set environment variable TG_LOG=debug to have terragrunt log the changes)")
terragruntOptions.Logger.Debugf("Changed from %s to %s", existingBackend.Config, remoteState.Config)
return true
}
Expand Down

0 comments on commit c750278

Please sign in to comment.