Skip to content

Commit

Permalink
fix: use GetOutputs option to fetch nested depdendencies
Browse files Browse the repository at this point in the history
Requires: infracost/terragrunt#13

Refactors the `TerragruntHCLProvider` to include a `terragruntPathToValue`
which is passed to the `TerragruntOptions`. This is used by the Terragrunt
library to fetch certain nested deps. See infracost/terragrunt#13
PR description for more details.
  • Loading branch information
hugorut committed Feb 15, 2024
1 parent 5ffa663 commit e360d86
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 37 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ require (
github.com/alecthomas/jsonschema v0.0.0-20211209230136-e2b41affa5c1
github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61
github.com/fatih/camelcase v1.0.0
github.com/go-errors/errors v1.4.2
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.11.0
github.com/google/go-github/v41 v41.0.0
Expand Down Expand Up @@ -158,7 +159,6 @@ require (
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/dlclark/regexp2 v1.8.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
Expand Down Expand Up @@ -271,7 +271,7 @@ replace github.com/jedib0t/go-pretty/v6 => github.com/aliscott/go-pretty/v6 v6.1

replace github.com/spf13/cobra => github.com/spf13/cobra v1.4.0

//replace github.com/gruntwork-io/terragrunt => github.com/infracost/terragrunt v0.47.1-0.20231103101711-77c5e7d4d795
replace github.com/gruntwork-io/terragrunt => github.com/infracost/terragrunt v0.47.1-0.20231211141424-6a52de9a284a
//replace github.com/gruntwork-io/terragrunt => github.com/infracost/terragrunt v0.47.1-0.20231211141424-6a52de9a284a
replace github.com/gruntwork-io/terragrunt => github.com/infracost/terragrunt v0.47.1-0.20240215101241-26c448a9863d

replace github.com/heimdalr/dag => github.com/aliscott/dag v1.3.2-0.20231115114512-4ce18c825f94
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,8 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/infracost/terragrunt v0.47.1-0.20231211141424-6a52de9a284a h1:+3N8wKw04orEc9Gi0XoAPUf9pwRKBHfs/LAiqFGwEm0=
github.com/infracost/terragrunt v0.47.1-0.20231211141424-6a52de9a284a/go.mod h1:xmRpWI+M4KlZbMQp1pj20CdXlZf5eIkRND5ybNkdnus=
github.com/infracost/terragrunt v0.47.1-0.20240215101241-26c448a9863d h1:rVBc/NwXzZASv73k94EWOm3cdzuUZWHq2gms6n483z8=
github.com/infracost/terragrunt v0.47.1-0.20240215101241-26c448a9863d/go.mod h1:xmRpWI+M4KlZbMQp1pj20CdXlZf5eIkRND5ybNkdnus=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
Expand Down
96 changes: 64 additions & 32 deletions internal/providers/terraform/terragrunt_hcl_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package terraform
import (
"bufio"
"context"
"errors"
"fmt"
"net/url"
"os"
Expand All @@ -15,6 +16,7 @@ import (
"sync"
"time"

goerrors "github.com/go-errors/errors"
tgerrors "github.com/gruntwork-io/go-commons/errors"
tgcliterraform "github.com/gruntwork-io/terragrunt/cli/commands/terraform"
tgcliinfo "github.com/gruntwork-io/terragrunt/cli/commands/terragrunt-info"
Expand All @@ -29,7 +31,6 @@ import (
hcl2 "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/sirupsen/logrus"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -456,6 +457,7 @@ func (p *TerragruntHCLProvider) prepWorkingDirs() ([]*terragruntWorkingDirInfo,
return funcs
},
Parallelism: 1,
GetOutputs: p.terragruntPathToValue,
}

howThesePathsWereFound := fmt.Sprintf("Terragrunt config file found in a subdirectory of %s", terragruntOptions.WorkingDir)
Expand All @@ -470,7 +472,7 @@ func (p *TerragruntHCLProvider) prepWorkingDirs() ([]*terragruntWorkingDirInfo,
err = s.Run(terragruntOptions)
if err != nil {
return nil, clierror.NewCLIError(
errors.Errorf(
fmt.Errorf(
"%s\n%v%s",
"Failed to parse the Terragrunt code using the Terragrunt library:",
err.Error(),
Expand Down Expand Up @@ -907,7 +909,7 @@ func mergeListWithDependencyMap(valueMap map[string]cty.Value, pieces []string,

// fetchModuleOutputs returns the Terraform outputs from the dependencies of Terragrunt file provided in the opts input.
func (p *TerragruntHCLProvider) fetchModuleOutputs(opts *tgoptions.TerragruntOptions) (cty.Value, error) {
outputs := cty.MapVal(map[string]cty.Value{
fallbackOutputs := cty.MapVal(map[string]cty.Value{
"outputs": cty.ObjectVal(map[string]cty.Value{
"mock": cty.StringVal("val"),
}),
Expand All @@ -923,46 +925,71 @@ func (p *TerragruntHCLProvider) fetchModuleOutputs(opts *tgoptions.TerragruntOpt
}

if mod != nil && len(mod.Dependencies) > 0 {
blocks, err := decodeDependencyBlocks(mod.TerragruntOptions.TerragruntConfigPath, opts, nil, nil)
value, err := p.decodeTerragruntDepsToValue(mod.TerragruntOptions.TerragruntConfigPath, opts)
if err != nil {
return cty.Value{}, fmt.Errorf("could not parse dependency blocks for Terragrunt file %s %w", mod.TerragruntOptions.TerragruntConfigPath, err)
return fallbackOutputs, err
}

out := map[string]cty.Value{}
for dir, dep := range blocks {
value, depErr := terragruntOutputCache.Set(dir, func() (cty.Value, error) {
info := p.runTerragrunt(opts.Clone(dir))
if info != nil && info.error != nil {
return cty.NilVal, fmt.Errorf("could not evaluate dependency %s at dir %s err: %w", dep.Name, dir, err)
}
return value, nil
}
}

if info == nil {
return cty.EmptyObjectVal, nil
}
return fallbackOutputs, nil
}

return info.evaluatedOutputs, nil
})
if depErr != nil {
return outputs, depErr
}
func (p *TerragruntHCLProvider) terragruntPathToValue(targetConfig string, opts *tgoptions.TerragruntOptions) (*cty.Value, bool, error) {
value, err := terragruntOutputCache.Set(targetConfig, func() (cty.Value, error) {
info := p.runTerragrunt(opts.Clone(targetConfig))
if info != nil && info.error != nil {
return cty.EmptyObjectVal, fmt.Errorf("could not run teragrunt path %s err: %w", targetConfig, info.error)
}

out[dep.Name] = cty.MapVal(map[string]cty.Value{
"outputs": value,
})
}
if info == nil {
return cty.EmptyObjectVal, nil
}

if len(out) > 0 {
encoded, err := toCtyValue(out, generateTypeFromValuesMap(out))
if err == nil {
return encoded, nil
}
return info.evaluatedOutputs, nil
})

p.logger.Warn().Err(err).Msg("could not transform output blocks to cty type, using dummy output type")
}
if err != nil {
return &cty.EmptyObjectVal, true, err
}

if value.RawEquals(cty.EmptyObjectVal) {
return &cty.EmptyObjectVal, true, nil
}

return &value, false, nil
}

func (p *TerragruntHCLProvider) decodeTerragruntDepsToValue(targetConfig string, opts *tgoptions.TerragruntOptions) (cty.Value, error) {
blocks, err := decodeDependencyBlocks(targetConfig, opts, nil, nil)
if err != nil {
return cty.EmptyObjectVal, fmt.Errorf("could not parse dependency blocks for Terragrunt file %s %w", targetConfig, err)
}

out := map[string]cty.Value{}
for dir, dep := range blocks {
value, _, depErr := p.terragruntPathToValue(dir, opts)
if depErr != nil {
return cty.EmptyObjectVal, depErr
}

out[dep.Name] = cty.MapVal(map[string]cty.Value{
"outputs": *value,
})
}

return outputs, nil
if len(out) > 0 {
encoded, err := toCtyValue(out, generateTypeFromValuesMap(out))
if err == nil {
return encoded, nil
}

p.logger.Warn().Err(err).Msg("could not transform output blocks to cty type, using dummy output type")
}

return cty.EmptyObjectVal, nil
}

func toCtyValue(val map[string]cty.Value, ty cty.Type) (v cty.Value, err error) {
Expand Down Expand Up @@ -1030,6 +1057,11 @@ func decodeDependencyBlocks(filename string, terragruntOptions *tgoptions.Terrag
}

localsAsCty, trackInclude, err := tgconfig.DecodeBaseBlocks(terragruntOptions, parser, file, filename, include, nil)
var withStack *goerrors.Error
if errors.As(err, &withStack) {
stack := withStack.ErrorStack()
return nil, fmt.Errorf("could not parse base hcl blocks %w", errors.New(stack))
}
if err != nil {
return nil, fmt.Errorf("could not parse base hcl blocks %w", err)
}
Expand Down

0 comments on commit e360d86

Please sign in to comment.