Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cli/commands/run/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ const (
)

// WithRunVersionCache initializes the version cache in the context for the run package.
// If a cache already exists in the context, it reuses it. Otherwise, creates a new one.
func WithRunVersionCache(ctx context.Context) context.Context {
// Check if cache already exists in context
if existingCache, ok := ctx.Value(versionCacheContextKey).(*cache.Cache[string]); ok && existingCache != nil {
return ctx // Reuse existing cache
}

// Create new cache if none exists
ctx = context.WithValue(ctx, versionCacheContextKey, cache.NewCache[string](versionCacheName))
return ctx
}
Expand All @@ -23,3 +30,8 @@ func WithRunVersionCache(ctx context.Context) context.Context {
func GetRunVersionCache(ctx context.Context) *cache.Cache[string] {
return cache.ContextCache[string](ctx, versionCacheContextKey)
}

// ClearVersionCache clears the version cache from the context. Useful during testing.
func ClearVersionCache(ctx context.Context) {
cache.ContextCache[string](ctx, versionCacheContextKey).Clear()
}
28 changes: 11 additions & 17 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/gruntwork-io/terragrunt/pkg/log/writer"
"github.com/gruntwork-io/terragrunt/tf"

"github.com/gruntwork-io/terragrunt/internal/cache"
"github.com/gruntwork-io/terragrunt/internal/ctyhelper"
"github.com/gruntwork-io/terragrunt/internal/experiment"
"github.com/gruntwork-io/terragrunt/internal/remotestate"
Expand Down Expand Up @@ -51,8 +50,6 @@ const (

FoundInFile = "found_in_file"

iamRoleCacheName = "iamRoleCache"

logMsgSeparator = "\n"

DefaultEngineType = "rpc"
Expand Down Expand Up @@ -1170,7 +1167,7 @@ func ReadTerragruntConfig(ctx context.Context, l log.Logger, terragruntOptions *
func ParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string, includeFromChild *IncludeConfig) (*TerragruntConfig, error) {
var config *TerragruntConfig

hclCache := cache.ContextCache[*hclparse.File](ctx, HclCacheContextKey)
hclCache := GetHclCache(ctx)

err := telemetry.TelemeterFromContext(ctx).Collect(ctx, "parse_config_file", map[string]any{
"config_path": configPath,
Expand All @@ -1186,11 +1183,6 @@ func ParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string, inclu
decodeListKey = fmt.Sprintf("%v", ctx.PartialParseDecodeList)
}

dir, err := os.Getwd()
if err != nil {
return err
}

fileInfo, err := os.Stat(configPath)
if err != nil {
if os.IsNotExist(err) {
Expand All @@ -1200,11 +1192,16 @@ func ParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string, inclu
return errors.Errorf("failed to get file info: %w", err)
}

var (
file *hclparse.File
cacheKey = fmt.Sprintf("parse-config-%v-%v-%v-%v-%v-%v", configPath, childKey, decodeListKey, ctx.TerragruntOptions.WorkingDir, dir, fileInfo.ModTime().UnixMicro())
cacheKey := fmt.Sprintf("%v-%v-%v-%v-%v",
configPath,
ctx.TerragruntOptions.WorkingDir,
childKey,
decodeListKey,
fileInfo.ModTime().UnixMicro(),
)

var file *hclparse.File

// TODO: Remove lint ignore
if cacheConfig, found := hclCache.Get(ctx, cacheKey); found { //nolint:contextcheck
file = cacheConfig
Expand Down Expand Up @@ -1489,9 +1486,6 @@ func detectBareIncludeUsage(file *hclparse.File) bool {
}
}

// iamRoleCache - store for cached values of IAM roles
var iamRoleCache = cache.NewCache[options.IAMRoleOptions](iamRoleCacheName)

// setIAMRole - extract IAM role details from Terragrunt flags block
func setIAMRole(ctx *ParsingContext, l log.Logger, file *hclparse.File, includeFromChild *IncludeConfig) error {
// Prefer the IAM Role CLI args if they were passed otherwise lazily evaluate the IamRoleOptions using the config.
Expand All @@ -1501,7 +1495,7 @@ func setIAMRole(ctx *ParsingContext, l log.Logger, file *hclparse.File, includeF
// as key is considered HCL code and include configuration
var (
key = fmt.Sprintf("%v-%v", file.Content(), includeFromChild)
config, found = iamRoleCache.Get(ctx, key)
config, found = GetIAMRoleCache(ctx).Get(ctx, key)
)

if !found {
Expand All @@ -1511,7 +1505,7 @@ func setIAMRole(ctx *ParsingContext, l log.Logger, file *hclparse.File, includeF
}

config = iamConfig.GetIAMRoleOptions()
iamRoleCache.Put(ctx, key, config)
GetIAMRoleCache(ctx).Put(ctx, key, config)
}
// We merge the OriginalIAMRoleOptions into the one from the config, because the CLI passed IAMRoleOptions has
// precedence.
Expand Down
17 changes: 3 additions & 14 deletions config/config_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (

"github.com/gruntwork-io/terragrunt/config/hclparse"
"github.com/gruntwork-io/terragrunt/internal/awshelper"
"github.com/gruntwork-io/terragrunt/internal/cache"
"github.com/gruntwork-io/terragrunt/internal/cli"
"github.com/gruntwork-io/terragrunt/internal/ctyhelper"
"github.com/gruntwork-io/terragrunt/internal/errors"
Expand Down Expand Up @@ -79,8 +78,6 @@ const (
FuncNameTimeCmp = "timecmp"
FuncNameMarkAsRead = "mark_as_read"
FuncNameConstraintCheck = "constraint_check"

sopsCacheName = "sopsCache"
)

// TerraformCommandsNeedLocking is a list of terraform commands that accept -lock-timeout
Expand Down Expand Up @@ -338,7 +335,7 @@ func parseGetEnvParameters(parameters []string) (EnvVar, error) {
func RunCommand(ctx *ParsingContext, l log.Logger, args []string) (string, error) {
// runCommandCache - cache of evaluated `run_cmd` invocations
// see: https://github.com/gruntwork-io/terragrunt/issues/1427
runCommandCache := cache.ContextCache[string](ctx, RunCmdCacheContextKey)
runCommandCache := GetRunCmdCache(ctx)

if len(args) == 0 {
return "", errors.New(EmptyStringNotAllowedError("parameter to the run_cmd function"))
Expand Down Expand Up @@ -879,14 +876,6 @@ func getModulePathFromSourceURL(sourceURL string) (string, error) {
return matches[1], nil
}

// A cache of the results of a decrypt operation via sops. Each decryption
// operation can take several seconds, so this cache speeds up terragrunt executions
// where the same sops files are referenced multiple times.
//
// The cache keys are the canonical paths to the encrypted files, and the values are the
// plain-text result of the decrypt operation.
var sopsCache = cache.NewCache[string](sopsCacheName)

// decrypts and returns sops encrypted utf-8 yaml or json data as a string
func sopsDecryptFile(ctx *ParsingContext, l log.Logger, params []string) (string, error) {
numParams := len(params)
Expand Down Expand Up @@ -939,7 +928,7 @@ func sopsDecryptFile(ctx *ParsingContext, l log.Logger, params []string) (string
}
}

if val, ok := sopsCache.Get(ctx, path); ok {
if val, ok := GetSopsCache(ctx).Get(ctx, path); ok {
return val, nil
}

Expand All @@ -950,7 +939,7 @@ func sopsDecryptFile(ctx *ParsingContext, l log.Logger, params []string) (string

if utf8.Valid(rawData) {
value := string(rawData)
sopsCache.Put(ctx, path, value)
GetSopsCache(ctx).Put(ctx, path, value)

return value, nil
}
Expand Down
5 changes: 2 additions & 3 deletions config/config_partial.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/gruntwork-io/terragrunt/pkg/log"
"github.com/huandu/go-clone"

"github.com/gruntwork-io/terragrunt/internal/cache"
"github.com/gruntwork-io/terragrunt/internal/strict/controls"

"github.com/hashicorp/hcl/v2"
Expand Down Expand Up @@ -277,7 +276,7 @@ func cliFlagsToCty(ctx *ParsingContext, flagByName map[string]*FeatureFlag) (map
}

func PartialParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string, include *IncludeConfig) (*TerragruntConfig, error) {
hclCache := cache.ContextCache[*hclparse.File](ctx, HclCacheContextKey)
hclCache := GetHclCache(ctx)

fileInfo, err := os.Stat(configPath)
if err != nil {
Expand Down Expand Up @@ -313,7 +312,7 @@ func PartialParseConfigFile(ctx *ParsingContext, l log.Logger, configPath string
func TerragruntConfigFromPartialConfig(ctx *ParsingContext, l log.Logger, file *hclparse.File, includeFromChild *IncludeConfig) (*TerragruntConfig, error) {
var cacheKey = fmt.Sprintf("%#v-%#v-%#v-%#v", file.ConfigPath, file.Content(), includeFromChild, ctx.PartialParseDecodeList)

terragruntConfigCache := cache.ContextCache[*TerragruntConfig](ctx, TerragruntConfigCacheContextKey)
terragruntConfigCache := GetTerragruntConfigCache(ctx)
if ctx.TerragruntOptions.UsePartialParseConfigCache {
if config, found := terragruntConfigCache.Get(ctx, cacheKey); found {
l.Debugf("Cache hit for '%s' (partial parsing), decodeList: '%v'.", ctx.TerragruntOptions.TerragruntConfigPath, ctx.PartialParseDecodeList)
Expand Down
98 changes: 85 additions & 13 deletions config/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,103 @@ package config

import (
"context"
"sync"

"github.com/gruntwork-io/terragrunt/config/hclparse"
"github.com/gruntwork-io/terragrunt/internal/cache"
"github.com/gruntwork-io/terragrunt/options"
)

type configKey byte

const (
HclCacheContextKey configKey = iota
TerragruntConfigCacheContextKey configKey = iota
RunCmdCacheContextKey configKey = iota
DependencyOutputCacheContextKey configKey = iota

hclCacheName = "hclCache"
configCacheName = "configCache"
runCmdCacheName = "runCmdCache"
dependencyOutputCacheName = "dependencyOutputCache"
HclCacheContextKey configKey = iota
TerragruntConfigCacheContextKey configKey = iota
RunCmdCacheContextKey configKey = iota
DependencyOutputCacheContextKey configKey = iota
DependencyJSONOutputCacheContextKey configKey = iota
DependencyLocksContextKey configKey = iota
SopsCacheContextKey configKey = iota
IAMRoleCacheContextKey configKey = iota

hclCacheName = "hclCache"
configCacheName = "configCache"
runCmdCacheName = "runCmdCache"
dependencyOutputCacheName = "dependencyOutputCache"
dependencyJSONOutputCacheName = "dependencyJSONOutputCache"
dependencyLocksCacheName = "dependencyLocksCache"
sopsCacheName = "sopsCache"
iamRoleCacheName = "iamRoleCache"
)

// GetSopsCache returns the SOPS cache instance from context
func GetSopsCache(ctx context.Context) *cache.Cache[string] {
return cache.ContextCache[string](ctx, SopsCacheContextKey)
}

// GetIAMRoleCache returns the IAM role cache instance from context
func GetIAMRoleCache(ctx context.Context) *cache.Cache[options.IAMRoleOptions] {
return cache.ContextCache[options.IAMRoleOptions](ctx, IAMRoleCacheContextKey)
}

// GetHclCache returns the HCL file cache instance from context
func GetHclCache(ctx context.Context) *cache.Cache[*hclparse.File] {
return cache.ContextCache[*hclparse.File](ctx, HclCacheContextKey)
}

// GetTerragruntConfigCache returns the Terragrunt config cache instance from context
func GetTerragruntConfigCache(ctx context.Context) *cache.Cache[*TerragruntConfig] {
return cache.ContextCache[*TerragruntConfig](ctx, TerragruntConfigCacheContextKey)
}

// GetRunCmdCache returns the run command cache instance from context
func GetRunCmdCache(ctx context.Context) *cache.Cache[string] {
return cache.ContextCache[string](ctx, RunCmdCacheContextKey)
}

// GetDependencyOutputCache returns the dependency output cache instance from context
func GetDependencyOutputCache(ctx context.Context) *cache.Cache[*dependencyOutputCache] {
return cache.ContextCache[*dependencyOutputCache](ctx, DependencyOutputCacheContextKey)
}

// GetDependencyJSONOutputCache returns the dependency JSON output cache instance from context
func GetDependencyJSONOutputCache(ctx context.Context) *cache.Cache[[]byte] {
return cache.ContextCache[[]byte](ctx, DependencyJSONOutputCacheContextKey)
}

// GetDependencyLocksCache returns the dependency locks cache instance from context
func GetDependencyLocksCache(ctx context.Context) *cache.Cache[*sync.Mutex] {
return cache.ContextCache[*sync.Mutex](ctx, DependencyLocksContextKey)
}

// WithConfigValues add to context default values for configuration.
// If caches already exist in the context, they are reused. Otherwise, new ones are created.
func WithConfigValues(ctx context.Context) context.Context {
ctx = context.WithValue(ctx, HclCacheContextKey, cache.NewCache[*hclparse.File](hclCacheName))
ctx = context.WithValue(ctx, TerragruntConfigCacheContextKey, cache.NewCache[*TerragruntConfig](configCacheName))
ctx = context.WithValue(ctx, RunCmdCacheContextKey, cache.NewCache[string](runCmdCacheName))
ctx = context.WithValue(ctx, DependencyOutputCacheContextKey, cache.NewCache[*dependencyOutputCache](dependencyOutputCacheName))
// Reuse existing caches if they exist, otherwise create new ones
if _, ok := ctx.Value(HclCacheContextKey).(*cache.Cache[*hclparse.File]); !ok {
ctx = context.WithValue(ctx, HclCacheContextKey, cache.NewCache[*hclparse.File](hclCacheName))
}
if _, ok := ctx.Value(TerragruntConfigCacheContextKey).(*cache.Cache[*TerragruntConfig]); !ok {
ctx = context.WithValue(ctx, TerragruntConfigCacheContextKey, cache.NewCache[*TerragruntConfig](configCacheName))
}
if _, ok := ctx.Value(RunCmdCacheContextKey).(*cache.Cache[string]); !ok {
ctx = context.WithValue(ctx, RunCmdCacheContextKey, cache.NewCache[string](runCmdCacheName))
}
if _, ok := ctx.Value(DependencyOutputCacheContextKey).(*cache.Cache[*dependencyOutputCache]); !ok {
ctx = context.WithValue(ctx, DependencyOutputCacheContextKey, cache.NewCache[*dependencyOutputCache](dependencyOutputCacheName))
}
if _, ok := ctx.Value(DependencyJSONOutputCacheContextKey).(*cache.Cache[[]byte]); !ok {
ctx = context.WithValue(ctx, DependencyJSONOutputCacheContextKey, cache.NewCache[[]byte](dependencyJSONOutputCacheName))
}
if _, ok := ctx.Value(DependencyLocksContextKey).(*cache.Cache[*sync.Mutex]); !ok {
ctx = context.WithValue(ctx, DependencyLocksContextKey, cache.NewCache[*sync.Mutex](dependencyLocksCacheName))
}
if _, ok := ctx.Value(SopsCacheContextKey).(*cache.Cache[string]); !ok {
ctx = context.WithValue(ctx, SopsCacheContextKey, cache.NewCache[string](sopsCacheName))
}
if _, ok := ctx.Value(IAMRoleCacheContextKey).(*cache.Cache[options.IAMRoleOptions]); !ok {
ctx = context.WithValue(ctx, IAMRoleCacheContextKey, cache.NewCache[options.IAMRoleOptions](iamRoleCacheName))
}

return ctx
}
Loading
Loading