Skip to content

Commit

Permalink
Merge branch 'main' into feat/custom-log-format
Browse files Browse the repository at this point in the history
  • Loading branch information
levkohimins committed Nov 15, 2024
2 parents 15a8c6c + 4389886 commit f42ce1f
Show file tree
Hide file tree
Showing 35 changed files with 908 additions and 179 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ workflows:
only: /^v.*/
context:
- AWS__PHXDEVOPS__circle-ci-test
- AWS__PHXDEVOPS__terragrunt-oidc-test
- GCP__automated-tests
- GITHUB__PAT__gruntwork-ci
- APPLE__OSX__code-signing
Expand All @@ -770,6 +771,7 @@ workflows:
only: /^v.*/
context:
- AWS__PHXDEVOPS__circle-ci-test
- AWS__PHXDEVOPS__terragrunt-oidc-test
- GCP__automated-tests
- GITHUB__PAT__gruntwork-ci
- APPLE__OSX__code-signing
Expand All @@ -779,6 +781,7 @@ workflows:
only: /^v.*/
context:
- AWS__PHXDEVOPS__circle-ci-test
- AWS__PHXDEVOPS__terragrunt-oidc-test
- GCP__automated-tests
- GITHUB__PAT__gruntwork-ci
- APPLE__OSX__code-signing
Expand Down
15 changes: 15 additions & 0 deletions cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ func initialSetup(cliCtx *cli.Context, opts *options.TerragruntOptions) error {
opts.ExcludeByDefault = true
}

if !opts.ExcludeByDefault && len(opts.ModulesThatInclude) > 0 {
opts.Logger.Debugf("Modules that include set. Excluding by default.")
opts.ExcludeByDefault = true
}

if !opts.ExcludeByDefault && len(opts.UnitsReading) > 0 {
opts.Logger.Debugf("Units that read set. Excluding by default.")
opts.ExcludeByDefault = true
}

if !opts.ExcludeByDefault && opts.StrictInclude {
opts.Logger.Debugf("Strict include set. Excluding by default.")
opts.ExcludeByDefault = true
}

opts.IncludeDirs, err = util.GlobCanonicalPath(opts.WorkingDir, opts.IncludeDirs...)
if err != nil {
return err
Expand Down
59 changes: 35 additions & 24 deletions cli/commands/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,30 +62,6 @@ const (
TerragruntIAMWebIdentityTokenFlagName = "terragrunt-iam-web-identity-token"
TerragruntIAMWebIdentityTokenEnvName = "TERRAGRUNT_IAM_ASSUME_ROLE_WEB_IDENTITY_TOKEN"

TerragruntIgnoreDependencyErrorsFlagName = "terragrunt-ignore-dependency-errors"
TerragruntIgnoreDependencyErrorsEnvName = "TERRAGRUNT_IGNORE_DEPENDENCY_ERRORS"

TerragruntIgnoreDependencyOrderFlagName = "terragrunt-ignore-dependency-order"
TerragruntIgnoreDependencyOrderEnvName = "TERRAGRUNT_IGNORE_DEPENDENCY_ORDER"

TerragruntIgnoreExternalDependenciesFlagName = "terragrunt-ignore-external-dependencies"
TerragruntIgnoreExternalDependenciesEnvName = "TERRAGRUNT_IGNORE_EXTERNAL_DEPENDENCIES"

TerragruntIncludeExternalDependenciesFlagName = "terragrunt-include-external-dependencies"
TerragruntIncludeExternalDependenciesEnvName = "TERRAGRUNT_INCLUDE_EXTERNAL_DEPENDENCIES"

TerragruntExcludesFileFlagName = "terragrunt-excludes-file"
TerragruntExcludesFileEnvName = "TERRAGRUNT_EXCLUDES_FILE"

TerragruntExcludeDirFlagName = "terragrunt-exclude-dir"
TerragruntExcludeDirEnvName = "TERRAGRUNT_EXCLUDE_DIR"

TerragruntIncludeDirFlagName = "terragrunt-include-dir"
TerragruntIncludeDirEnvName = "TERRAGRUNT_INCLUDE_DIR"

TerragruntStrictIncludeFlagName = "terragrunt-strict-include"
TerragruntStrictIncludeEnvName = "TERRAGRUNT_STRICT_INCLUDE"

TerragruntParallelismFlagName = "terragrunt-parallelism"
TerragruntParallelismEnvName = "TERRAGRUNT_PARALLELISM"

Expand Down Expand Up @@ -125,6 +101,35 @@ const (
TerragruntNoDestroyDependenciesCheckFlagEnvName = "TERRAGRUNT_NO_DESTROY_DEPENDENCIES_CHECK"
TerragruntNoDestroyDependenciesCheckFlagName = "terragrunt-no-destroy-dependencies-check"

// Queue related flags

TerragruntIgnoreDependencyErrorsFlagName = "terragrunt-ignore-dependency-errors"
TerragruntIgnoreDependencyErrorsEnvName = "TERRAGRUNT_IGNORE_DEPENDENCY_ERRORS"

TerragruntIgnoreDependencyOrderFlagName = "terragrunt-ignore-dependency-order"
TerragruntIgnoreDependencyOrderEnvName = "TERRAGRUNT_IGNORE_DEPENDENCY_ORDER"

TerragruntIgnoreExternalDependenciesFlagName = "terragrunt-ignore-external-dependencies"
TerragruntIgnoreExternalDependenciesEnvName = "TERRAGRUNT_IGNORE_EXTERNAL_DEPENDENCIES"

TerragruntIncludeExternalDependenciesFlagName = "terragrunt-include-external-dependencies"
TerragruntIncludeExternalDependenciesEnvName = "TERRAGRUNT_INCLUDE_EXTERNAL_DEPENDENCIES"

TerragruntExcludesFileFlagName = "terragrunt-excludes-file"
TerragruntExcludesFileEnvName = "TERRAGRUNT_EXCLUDES_FILE"

TerragruntExcludeDirFlagName = "terragrunt-exclude-dir"
TerragruntExcludeDirEnvName = "TERRAGRUNT_EXCLUDE_DIR"

TerragruntIncludeDirFlagName = "terragrunt-include-dir"
TerragruntIncludeDirEnvName = "TERRAGRUNT_INCLUDE_DIR"

TerragruntStrictIncludeFlagName = "terragrunt-strict-include"
TerragruntStrictIncludeEnvName = "TERRAGRUNT_STRICT_INCLUDE"

TerragruntUnitsReadingFlagName = "terragrunt-queue-include-units-reading"
TerragruntUnitsReadingEnvName = "TERRAGRUNT_QUEUE_INCLUDE_UNITS_READING"

// Logs related flags/envs

TerragruntLogLevelFlagName = "terragrunt-log-level"
Expand Down Expand Up @@ -470,6 +475,12 @@ func NewGlobalFlags(opts *options.TerragruntOptions) cli.Flags {
Destination: &opts.ModulesThatInclude,
Usage: "If flag is set, 'run-all' will only run the command against Terragrunt modules that include the specified file.",
},
&cli.SliceFlag[string]{
Name: TerragruntUnitsReadingFlagName,
EnvVar: TerragruntUnitsReadingEnvName,
Destination: &opts.UnitsReading,
Usage: "If flag is set, 'run-all' will only run the command against Terragrunt units that read the specified file via an HCL function.",
},
&cli.BoolFlag{
Name: TerragruntFailOnStateBucketCreationFlagName,
EnvVar: TerragruntFailOnStateBucketCreationEnvName,
Expand Down
77 changes: 75 additions & 2 deletions cli/commands/terraform/creds/providers/externalcmd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/gruntwork-io/terragrunt/cli/commands/terraform/creds/providers"
"github.com/gruntwork-io/terragrunt/cli/commands/terraform/creds/providers/amazonsts"
"github.com/gruntwork-io/terragrunt/internal/errors"
"github.com/gruntwork-io/terragrunt/options"
"github.com/gruntwork-io/terragrunt/shell"
Expand Down Expand Up @@ -66,17 +67,29 @@ func (provider *Provider) GetCredentials(ctx context.Context) (*providers.Creden
}

if resp.AWSCredentials != nil {
if envs := resp.AWSCredentials.Envs(provider.terragruntOptions); envs != nil {
if envs := resp.AWSCredentials.Envs(ctx, provider.terragruntOptions); envs != nil {
provider.terragruntOptions.Logger.Debugf("Obtaining AWS credentials from the %s.", provider.Name())
maps.Copy(creds.Envs, envs)
}

return creds, nil
}

if resp.AWSRole != nil {
if envs := resp.AWSRole.Envs(ctx, provider.terragruntOptions); envs != nil {
provider.terragruntOptions.Logger.Debugf("Assuming AWS role %s using the %s.", resp.AWSRole.RoleARN, provider.Name())
maps.Copy(creds.Envs, envs)
}

return creds, nil
}

return creds, nil
}

type Response struct {
AWSCredentials *AWSCredentials `json:"awsCredentials"`
AWSRole *AWSRole `json:"awsRole"`
Envs map[string]string `json:"envs"`
}

Expand All @@ -86,7 +99,67 @@ type AWSCredentials struct {
SessionToken string `json:"SESSION_TOKEN"`
}

func (creds *AWSCredentials) Envs(opts *options.TerragruntOptions) map[string]string {
type AWSRole struct {
RoleARN string `json:"roleARN"`
RoleSessionName string `json:"roleSessionName"`
Duration int64 `json:"duration"`
WebIdentityToken string `json:"webIdentityToken"`
}

func (role *AWSRole) Envs(ctx context.Context, opts *options.TerragruntOptions) map[string]string {
if role.RoleARN == "" {
opts.Logger.Warnf("The command %s completed successfully, but AWS role assumption contains empty required value: roleARN, nothing is being done.", opts.AuthProviderCmd)
return nil
}

sessionName := role.RoleSessionName
if sessionName == "" {
sessionName = options.GetDefaultIAMAssumeRoleSessionName()
}

duration := role.Duration
if duration == 0 {
duration = options.DefaultIAMAssumeRoleDuration
}

// Construct minimal TerragruntOptions for role assumption.
providerOpts := options.TerragruntOptions{
IAMRoleOptions: options.IAMRoleOptions{
RoleARN: role.RoleARN,
AssumeRoleDuration: duration,
AssumeRoleSessionName: sessionName,
},
Logger: opts.Logger,
}

if role.WebIdentityToken != "" {
providerOpts.IAMRoleOptions.WebIdentityToken = role.WebIdentityToken
}

provider := amazonsts.NewProvider(&providerOpts)

creds, err := provider.GetCredentials(ctx)
if err != nil {
opts.Logger.Warnf("Failed to assume role %s: %v", role.RoleARN, err)
return nil
}

if creds == nil {
opts.Logger.Warnf("The command %s completed successfully, but failed to assume role %s, nothing is being done.", opts.AuthProviderCmd, role.RoleARN)
return nil
}

envs := map[string]string{
"AWS_ACCESS_KEY_ID": creds.Envs["AWS_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY": creds.Envs["AWS_SECRET_ACCESS_KEY"],
"AWS_SESSION_TOKEN": creds.Envs["AWS_SESSION_TOKEN"],
"AWS_SECURITY_TOKEN": creds.Envs["AWS_SESSION_TOKEN"],
}

return envs
}

func (creds *AWSCredentials) Envs(_ context.Context, opts *options.TerragruntOptions) map[string]string {
var emptyFields []string

if creds.AccessKeyID == "" {
Expand Down
54 changes: 53 additions & 1 deletion config/config_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const (
FuncNameEndsWith = "endswith"
FuncNameStrContains = "strcontains"
FuncNameTimeCmp = "timecmp"
FuncNameMarkAsRead = "mark_as_read"

sopsCacheName = "sopsCache"
)
Expand Down Expand Up @@ -167,6 +168,7 @@ func createTerragruntEvalContext(ctx *ParsingContext, configPath string) (*hcl.E
FuncNameGetDefaultRetryableErrors: wrapVoidToStringSliceAsFuncImpl(ctx, getDefaultRetryableErrors),
FuncNameReadTfvarsFile: wrapStringSliceToStringAsFuncImpl(ctx, readTFVarsFile),
FuncNameGetWorkingDir: wrapVoidToStringAsFuncImpl(ctx, getWorkingDir),
FuncNameMarkAsRead: wrapStringSliceToStringAsFuncImpl(ctx, markAsRead),

// Map with HCL functions introduced in Terraform after v0.15.3, since upgrade to a later version is not supported
// https://github.com/gruntwork-io/terragrunt/blob/master/go.mod#L22
Expand Down Expand Up @@ -627,12 +629,25 @@ func ParseTerragruntConfig(ctx *ParsingContext, configPath string, defaultVal *c
targetConfig := getCleanedTargetConfigPath(configPath, ctx.TerragruntOptions.TerragruntConfigPath)

targetConfigFileExists := util.FileExists(targetConfig)

if !targetConfigFileExists && defaultVal == nil {
return cty.NilVal, errors.New(TerragruntConfigNotFoundError{Path: targetConfig})
} else if !targetConfigFileExists {
}

if !targetConfigFileExists {
return *defaultVal, nil
}

path, err := util.CanonicalPath(targetConfig, ctx.TerragruntOptions.WorkingDir)
if err != nil {
return cty.NilVal, errors.New(err)
}

ctx.TerragruntOptions.AppendReadFile(
path,
ctx.TerragruntOptions.WorkingDir,
)

// We update the ctx of terragruntOptions to the config being read in.
opts, err := ctx.TerragruntOptions.Clone(targetConfig)
if err != nil {
Expand Down Expand Up @@ -812,6 +827,11 @@ func sopsDecryptFile(ctx *ParsingContext, params []string) (string, error) {
return "", errors.New(err)
}

ctx.TerragruntOptions.AppendReadFile(
canonicalSourceFile,
ctx.TerragruntOptions.WorkingDir,
)

// Set environment variables from the TerragruntOptions.Env map.
// This is especially useful for integrations with things like the `terragrunt-auth-provider` flag,
// which can set environment variables that are used for decryption.
Expand Down Expand Up @@ -1008,6 +1028,11 @@ func readTFVarsFile(ctx *ParsingContext, args []string) (string, error) {
return "", errors.New(TFVarFileNotFoundError{File: varFile})
}

ctx.TerragruntOptions.AppendReadFile(
varFile,
ctx.TerragruntOptions.WorkingDir,
)

fileContents, err := os.ReadFile(varFile)
if err != nil {
return "", errors.New(fmt.Errorf("could not read file %q: %w", varFile, err))
Expand Down Expand Up @@ -1036,6 +1061,33 @@ func readTFVarsFile(ctx *ParsingContext, args []string) (string, error) {
return string(data), nil
}

// markAsRead marks a file as explicitly read. This is useful for detection via TerragruntUnitsReading flag.
func markAsRead(ctx *ParsingContext, args []string) (string, error) {
if len(args) != 1 {
return "", errors.New(WrongNumberOfParamsError{Func: "mark_as_read", Expected: "1", Actual: len(args)})
}

file := args[0]

path, err := util.CanonicalPath(file, ctx.TerragruntOptions.WorkingDir)
if err != nil {
return "", errors.New(err)
}

ctx.TerragruntOptions.AppendReadFile(
path,
ctx.TerragruntOptions.WorkingDir,
)

return file, nil
}

// warnWhenFileNotMarkedAsRead warns when a file is not being marked as read, even though a user might expect it to be.
// Situations where this is the case include:
// - A user specifies a file in the UnitsReading flag and that file is being read while parsing the inputs attribute.
//
// When the file is not marked as read, the function will return true, otherwise false.

// ParseAndDecodeVarFile uses the HCL2 file to parse the given varfile string into an HCL file body, and then decode it
// into the provided output.
func ParseAndDecodeVarFile(opts *options.TerragruntOptions, varFile string, fileContents []byte, out interface{}) error {
Expand Down
Loading

0 comments on commit f42ce1f

Please sign in to comment.