Skip to content

Commit ed35129

Browse files
tphoneycursoragent
authored andcommitted
Update tf provider parsing for submodules (#2691)
Enable recursive parsing of AWS provider configurations to support providers defined within Terraform submodules. --- Linear Issue: [ENG-1393](https://linear.app/overmind/issue/ENG-1393/support-submodule-providers) <a href="https://cursor.com/background-agent?bcId=bc-70333602-3079-47bf-9c58-7751a694c64b"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-cursor-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-cursor-light.svg"><img alt="Open in Cursor" src="https://cursor.com/open-in-cursor.svg"></picture></a>&nbsp;<a href="https://cursor.com/agents?id=bc-70333602-3079-47bf-9c58-7751a694c64b"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/open-in-web-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/open-in-web-light.svg"><img alt="Open in Web" src="https://cursor.com/open-in-web.svg"></picture></a> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> GitOrigin-RevId: 3a1d807ce26bd05af7646948a0407d3a82820605
1 parent f86374a commit ed35129

File tree

5 files changed

+362
-53
lines changed

5 files changed

+362
-53
lines changed

cmd/explore.go

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package cmd
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
7-
"path/filepath"
88
"slices"
9+
"strings"
910

1011
"atomicgo.dev/keyboard"
1112
"atomicgo.dev/keyboard/keys"
@@ -24,6 +25,7 @@ import (
2425
log "github.com/sirupsen/logrus"
2526
"github.com/sourcegraph/conc/pool"
2627
"github.com/spf13/cobra"
28+
"github.com/spf13/viper"
2729
"golang.org/x/oauth2"
2830
)
2931

@@ -52,6 +54,9 @@ For GCP, ensure you have appropriate permissions (roles/browser or equivalent) t
5254
func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oauth2.Token, tfArgs []string, failOverToDefaultLoginCfg bool) (func(), error) {
5355
var err error
5456

57+
// Default to recursive search unless --no-recursion is set
58+
tfRecursive := !viper.GetBool("no-recursion")
59+
5560
multi := pterm.DefaultMultiPrinter
5661
_, _ = multi.Start()
5762
defer func() {
@@ -67,22 +72,31 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
6772

6873
p := pool.NewWithResults[[]*discovery.Engine]().WithErrors()
6974

70-
tfFiles, err := filepath.Glob(filepath.Join(".", "*.tf"))
75+
// find all the terraform files
76+
tfFiles, err := tfutils.FindTerraformFiles(".", tfRecursive)
7177
if err != nil {
78+
// we only error if there is a filesystem error, 0 files is handled below
7279
return nil, err
7380
}
7481

82+
// if no terraform files are found, return an error
7583
if len(tfFiles) == 0 && !failOverToDefaultLoginCfg {
7684
currentDir, _ := os.Getwd()
77-
return nil, fmt.Errorf(`No Terraform configuration files found in %s
78-
79-
The Overmind CLI requires access to Terraform configuration files (.tf files) to discover and authenticate with cloud providers. Without Terraform configuration, the CLI cannot determine which cloud resources to interrogate.
80-
81-
To resolve this issue:
82-
- Ensure you're running the command from a directory containing Terraform files (.tf files)
83-
- Or create Terraform configuration files that define your cloud providers
84-
85-
For more information about Terraform configuration, visit: https://developer.hashicorp.com/terraform/language`, currentDir)
85+
msgLines := []string{
86+
fmt.Sprintf("No Terraform configuration files found in %s", currentDir),
87+
"",
88+
"The Overmind CLI requires access to Terraform configuration files (.tf files) to discover and authenticate with cloud providers. Without Terraform configuration, the CLI cannot determine which cloud resources to interrogate.",
89+
"",
90+
"To resolve this issue:",
91+
"- Ensure you're running the command from a directory containing Terraform files (.tf files)",
92+
"- Or create Terraform configuration files that define your cloud providers",
93+
"",
94+
}
95+
if !tfRecursive {
96+
msgLines = append(msgLines, "- Or remove --no-recursion to scan subdirectories for Terraform stacks")
97+
}
98+
msgLines = append(msgLines, "For more information about Terraform configuration, visit: https://developer.hashicorp.com/terraform/language")
99+
return nil, errors.New(strings.Join(msgLines, "\n"))
86100
}
87101

88102
stdlibSpinner, _ := pterm.DefaultSpinner.WithWriter(multi.NewWriter()).Start("Starting stdlib source engine")
@@ -129,7 +143,7 @@ For more information about Terraform configuration, visit: https://developer.has
129143
return nil, fmt.Errorf("failed to load variables from the environment: %w", err)
130144
}
131145

132-
awsProviders, err := tfutils.ParseAWSProviders(".", tfEval)
146+
awsProviders, err := tfutils.ParseAWSProviders(".", tfEval, tfRecursive)
133147
if err != nil {
134148
awsSpinner.Fail("Failed to parse AWS providers")
135149
return nil, fmt.Errorf("failed to parse AWS providers: %w", err)
@@ -217,7 +231,7 @@ For more information about Terraform configuration, visit: https://developer.has
217231
return nil, fmt.Errorf("failed to load variables from the environment for GCP: %w", err)
218232
}
219233

220-
gcpProviders, err := tfutils.ParseGCPProviders(".", tfEval)
234+
gcpProviders, err := tfutils.ParseGCPProviders(".", tfEval, tfRecursive)
221235
if err != nil {
222236
gcpSpinner.Fail("Failed to parse GCP providers")
223237
return nil, fmt.Errorf("failed to parse GCP providers: %w", err)
@@ -404,6 +418,8 @@ func init() {
404418
rootCmd.AddCommand(exploreCmd)
405419

406420
addAPIFlags(exploreCmd)
421+
// flag to opt-out of recursion and only scan the current folder for *.tf files
422+
exploreCmd.PersistentFlags().Bool("no-recursion", false, "Only scan the current directory for Terraform files (non-recursive).")
407423
}
408424

409425
// unifiedGCPConfigs collates the given GCP configs by project ID.

tfutils/aws_config.go

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,17 +472,12 @@ type ProviderResult struct {
472472
FilePath string
473473
}
474474

475-
// Parses AWS provider config from all terraform files in the given directory,
476-
// without recursion as we don't yet support providers in submodules. Returns a
477-
// list of AWS providers and a list of files that were parsed. This will return
478-
// an error only if there was an error loading the files. ProviderResults will
479-
// be returned for:
480-
//
481-
// * Files that could not be parsed at all (just an error)
482-
// * Files that contained an AWS provider that we couldn't fully evaluate (with an error)
483-
// * Files that contained an AWS provider that we could fully evaluate (with no error)
484-
func ParseAWSProviders(terraformDir string, evalContext *hcl.EvalContext) ([]ProviderResult, error) {
485-
files, err := filepath.Glob(filepath.Join(terraformDir, "*.tf"))
475+
// ParseAWSProviders scans for .tf files and extracts AWS provider configurations.
476+
// The search behavior is controlled by the recursive flag: when false, only the
477+
// provided directory is scanned via a simple glob; when true, the directory is
478+
// walked recursively while skipping dot-directories (e.g., .terraform).
479+
func ParseAWSProviders(terraformDir string, evalContext *hcl.EvalContext, recursive bool) ([]ProviderResult, error) {
480+
files, err := FindTerraformFiles(terraformDir, recursive)
486481
if err != nil {
487482
return nil, err
488483
}
@@ -549,6 +544,38 @@ func ParseAWSProviders(terraformDir string, evalContext *hcl.EvalContext) ([]Pro
549544
return results, nil
550545
}
551546

547+
// FindTerraformFiles returns a list of Terraform files under terraformDir.
548+
// When recursive is false, it uses a simple glob for "*.tf" in the directory.
549+
// When recursive is true, it walks the directory tree and collects .tf files,
550+
// skipping any dot-prefixed subdirectories (e.g., .terraform).
551+
func FindTerraformFiles(terraformDir string, recursive bool) ([]string, error) {
552+
if !recursive {
553+
return filepath.Glob(filepath.Join(terraformDir, "*.tf"))
554+
}
555+
files := []string{}
556+
err := filepath.Walk(terraformDir, func(path string, info os.FileInfo, err error) error {
557+
if err != nil {
558+
return err
559+
}
560+
// If this is a subdirectory starting with a dot, skip it entirely
561+
if info.IsDir() && path != terraformDir && strings.HasPrefix(filepath.Base(path), ".") {
562+
return filepath.SkipDir
563+
}
564+
if info.IsDir() {
565+
return nil
566+
}
567+
// Only include .tf files
568+
if strings.HasSuffix(path, ".tf") {
569+
files = append(files, path)
570+
}
571+
return nil
572+
})
573+
if err != nil {
574+
return nil, fmt.Errorf("error walking directory %s: %w", terraformDir, err)
575+
}
576+
return files, nil
577+
}
578+
552579
// ConfigFromProvider creates an aws.Config from an AWSProvider that uses the
553580
// provided HTTP client. This client will be modified with proxy settings if
554581
// they are present in the provider.

0 commit comments

Comments
 (0)