Skip to content

Commit

Permalink
fix: make terragrunt-fetch-dependency-output-from-state to work wit…
Browse files Browse the repository at this point in the history
…h not applied dependencies (#3350)

* fix: make `terragrunt-fetch-dependency-output-from-state` to work with not applied dependencies

Signed-off-by: Rodrigo Fior Kuntzer <[email protected]>

* fix: remove terraform only checks in the integration tests

Signed-off-by: Rodrigo Fior Kuntzer <[email protected]>

* fix: applying code suggestions

Signed-off-by: Rodrigo Fior Kuntzer <[email protected]>

---------

Signed-off-by: Rodrigo Fior Kuntzer <[email protected]>
  • Loading branch information
rodrigorfk committed Aug 20, 2024
1 parent 9accf24 commit 5867027
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
13 changes: 12 additions & 1 deletion config/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import (
"bufio"
"bytes"
"encoding/json"
goErrors "errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"sync"

"github.com/aws/aws-sdk-go/aws/awserr"

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

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -500,7 +503,7 @@ func getTerragruntOutput(ctx *ParsingContext, dependencyConfig Dependency) (*cty

jsonBytes, err := getOutputJsonWithCaching(ctx, targetConfigPath)
if err != nil {
if !isRenderJsonCommand(ctx) {
if !isRenderJsonCommand(ctx) && !isAwsS3NoSuchKey(err) {
return nil, true, err
}
ctx.TerragruntOptions.Logger.Warnf("Failed to read outputs from %s referenced in %s as %s, fallback to mock outputs. Error: %v", targetConfigPath, ctx.TerragruntOptions.TerragruntConfigPath, dependencyConfig.Name, err)
Expand All @@ -524,6 +527,14 @@ func getTerragruntOutput(ctx *ParsingContext, dependencyConfig Dependency) (*cty
return &convertedOutput, isEmpty, errors.WithStackTrace(err)
}

func isAwsS3NoSuchKey(err error) bool {
var awsErr awserr.Error
if goErrors.As(errors.Unwrap(err), &awsErr) {
return awsErr.Code() == "NoSuchKey"
}
return false
}

// isRenderJsonCommand This function will true if terragrunt was invoked with render-json
func isRenderJsonCommand(ctx *ParsingContext) bool {
return util.ListContainsElement(ctx.TerragruntOptions.TerraformCliArgs, renderJsonCommand)
Expand Down
29 changes: 29 additions & 0 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5337,6 +5337,35 @@ func TestTerragruntOutputFromRemoteState(t *testing.T) { //nolint: paralleltest
assert.True(t, (strings.Index(output, "app3 output") < strings.Index(output, "app1 output")) && (strings.Index(output, "app1 output") < strings.Index(output, "app2 output")))
}

func TestTerragruntMockOutputsFromRemoteState(t *testing.T) { //nolint: paralleltest
// NOTE: We can't run this test in parallel because there are other tests that also call `config.ClearOutputCache()`, but this function uses a global variable and sometimes it throws an unexpected error:
// "fixture-output-from-remote-state/env1/app2/terragrunt.hcl:23,38-48: Unsupported attribute; This object does not have an attribute named "app3_text"."
// t.Parallel()

s3BucketName := "terragrunt-test-bucket-" + strings.ToLower(uniqueId())
defer deleteS3Bucket(t, TERRAFORM_REMOTE_STATE_S3_REGION, s3BucketName)

tmpEnvPath := copyEnvironment(t, TEST_FIXTURE_OUTPUT_FROM_REMOTE_STATE)

rootTerragruntConfigPath := util.JoinPath(tmpEnvPath, TEST_FIXTURE_OUTPUT_FROM_REMOTE_STATE, config.DefaultTerragruntConfigPath)
copyTerragruntConfigAndFillPlaceholders(t, rootTerragruntConfigPath, rootTerragruntConfigPath, s3BucketName, "not-used", "not-used")

environmentPath := filepath.Join(tmpEnvPath, TEST_FIXTURE_OUTPUT_FROM_REMOTE_STATE, "env1")

// applying only the app1 dependency, the app3 dependency was purposely not applied and should be mocked when running the app2 module
runTerragrunt(t, fmt.Sprintf("terragrunt apply --terragrunt-fetch-dependency-output-from-state --auto-approve --terragrunt-non-interactive --terragrunt-working-dir %s/app1", environmentPath))
// Now delete dependencies cached state
config.ClearOutputCache()
require.NoError(t, os.Remove(filepath.Join(environmentPath, "/app1/.terraform/terraform.tfstate")))
require.NoError(t, os.RemoveAll(filepath.Join(environmentPath, "/app1/.terraform")))

_, stderr, err := runTerragruntCommandWithOutput(t, fmt.Sprintf("terragrunt init --terragrunt-fetch-dependency-output-from-state --terragrunt-non-interactive --terragrunt-working-dir %s/app2", environmentPath))
require.NoError(t, err)

assert.True(t, strings.Contains(stderr, "Failed to read outputs"))
assert.True(t, strings.Contains(stderr, "fallback to mock outputs"))
}

func TestShowErrorWhenRunAllInvokedWithoutArguments(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 5867027

Please sign in to comment.