44 "context"
55 "fmt"
66 "os"
7+ "path/filepath"
78 "slices"
89
910 "atomicgo.dev/keyboard"
@@ -56,10 +57,6 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
5657 defer func () {
5758 _ , _ = multi .Stop ()
5859 }()
59- stdlibSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting stdlib source engine" )
60- awsSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting AWS source engine" )
61- gcpSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting GCP source engine" )
62- statusArea := pterm .DefaultParagraph .WithWriter (multi .NewWriter ())
6360
6461 natsOpts := natsOptions (ctx , oi , token )
6562
@@ -70,6 +67,31 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
7067
7168 p := pool .NewWithResults [[]* discovery.Engine ]().WithErrors ()
7269
70+ tfFiles , err := filepath .Glob (filepath .Join ("." , "*.tf" ))
71+ if err != nil {
72+ return nil , err
73+ }
74+
75+ if len (tfFiles ) == 0 && ! failOverToDefaultLoginCfg {
76+ 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 )
86+ }
87+
88+ stdlibSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting stdlib source engine" )
89+ awsSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting AWS source engine" )
90+ gcpSpinner , _ := pterm .DefaultSpinner .WithWriter (multi .NewWriter ()).Start ("Starting GCP source engine" )
91+ statusArea := pterm .DefaultParagraph .WithWriter (multi .NewWriter ())
92+
93+ foundCloudProvider := false
94+
7395 p .Go (func () ([]* discovery.Engine , error ) { //nolint:contextcheck // todo: pass in context with timeout to abort timely and allow Ctrl-C to work
7496 ec := discovery.EngineConfig {
7597 Version : fmt .Sprintf ("cli-%v" , tracing .Version ()),
@@ -107,13 +129,19 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
107129 return nil , fmt .Errorf ("failed to load variables from the environment: %w" , err )
108130 }
109131
110- providers , err := tfutils .ParseAWSProviders ("." , tfEval )
132+ awsProviders , err := tfutils .ParseAWSProviders ("." , tfEval )
111133 if err != nil {
112- awsSpinner .Fail ("Failed to parse providers" )
113- return nil , fmt .Errorf ("failed to parse providers: %w" , err )
134+ awsSpinner .Fail ("Failed to parse AWS providers" )
135+ return nil , fmt .Errorf ("failed to parse AWS providers: %w" , err )
136+ }
137+
138+ if len (awsProviders ) == 0 && ! failOverToDefaultLoginCfg {
139+ awsSpinner .Warning ("No AWS terraform providers found, skipping AWS source initialization." )
140+ return nil , nil // skip AWS if there are no awsProviders
114141 }
142+
115143 configs := []aws.Config {}
116- for _ , p := range providers {
144+ for _ , p := range awsProviders {
117145 if p .Error != nil {
118146 // skip providers that had errors. This allows us to use
119147 // providers we _could_ detect, while still failing if there is
@@ -123,20 +151,24 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
123151 }
124152 c , err := tfutils .ConfigFromProvider (ctx , * p .Provider )
125153 if err != nil {
126- awsSpinner .Fail ("Error when converting provider to config" )
127- return nil , fmt .Errorf ("error when converting provider to config: %w" , err )
154+ awsSpinner .Fail ("Error when converting AWS Terraform provider to config: " , err )
155+ return nil , fmt .Errorf ("error when converting AWS Terraform provider to config: %w" , err )
128156 }
129157 credentials , _ := c .Credentials .Retrieve (ctx )
130- statusArea .Println (fmt .Sprintf ("Using AWS provider in %s with %s." , p .FilePath , credentials .Source ))
158+ aliasInfo := ""
159+ if p .Provider .Alias != "" {
160+ aliasInfo = fmt .Sprintf (" (alias: %s)" , p .Provider .Alias )
161+ }
162+ statusArea .Println (fmt .Sprintf ("Using AWS provider %s%s in %s with %s." , p .Provider .Name , aliasInfo , p .FilePath , credentials .Source ))
131163 configs = append (configs , c )
132164 }
133165 if len (configs ) == 0 && failOverToDefaultLoginCfg {
166+ statusArea .Println ("No AWS terraform providers found. Attempting to use AWS default credentials for configuration." )
134167 userConfig , err := config .LoadDefaultConfig (ctx )
135168 if err != nil {
136- awsSpinner .Fail ("Failed to load default AWS config" )
169+ awsSpinner .Fail ("Failed to load default AWS config: " , err )
137170 return nil , fmt .Errorf ("failed to load default AWS config: %w" , err )
138171 }
139- statusArea .Println ("Using default AWS CLI config. No AWS terraform providers found." )
140172 configs = append (configs , userConfig )
141173 }
142174 ec := discovery.EngineConfig {
@@ -173,6 +205,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
173205 }
174206
175207 awsSpinner .Success ("AWS source engine started" )
208+ foundCloudProvider = true
176209 return []* discovery.Engine {awsEngine }, nil
177210 })
178211
@@ -190,6 +223,11 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
190223 return nil , fmt .Errorf ("failed to parse GCP providers: %w" , err )
191224 }
192225
226+ if len (gcpProviders ) == 0 && ! failOverToDefaultLoginCfg {
227+ gcpSpinner .Warning ("No GCP terraform providers found, skipping GCP source initialization." )
228+ return nil , nil // skip GCP if there are no providers
229+ }
230+
193231 // Process GCP providers and extract configurations
194232 gcpConfigs := []* gcpproc.GCPConfig {}
195233
@@ -201,7 +239,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
201239
202240 config , err := tfutils .ConfigFromGCPProvider (* p .Provider )
203241 if err != nil {
204- statusArea .Println (fmt .Sprintf ("Error configuring GCP provider in %s: %s" , p .FilePath , err .Error ()))
242+ statusArea .Println (fmt .Sprintf ("Error configuring GCP provider %s in %s: %s" , p . Provider . Name , p .FilePath , err .Error ()))
205243 continue
206244 }
207245
@@ -215,12 +253,12 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
215253 if config .Alias != "" {
216254 aliasInfo = fmt .Sprintf (" (alias: %s)" , config .Alias )
217255 }
218- statusArea .Println (fmt .Sprintf ("Using GCP provider in %s with project %s%s" , p .FilePath , config .ProjectID , aliasInfo ))
256+ statusArea .Println (fmt .Sprintf ("Using GCP provider in %s with project %s%s. " , p .FilePath , config .ProjectID , aliasInfo ))
219257 }
220258
221259 // Fallback to default GCP config if no terraform providers found
222260 if len (gcpConfigs ) == 0 && failOverToDefaultLoginCfg {
223- statusArea .Println ("No GCP terraform providers found. Attempting to use default GCP credentials ." )
261+ statusArea .Println ("No GCP terraform providers found. Attempting to use GCP Application Default Credentials for configuration ." )
224262 // Try to use Application Default Credentials by passing nil config
225263 gcpConfigs = append (gcpConfigs , nil )
226264 }
@@ -280,6 +318,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
280318 gcpSpinner .Success (fmt .Sprintf ("%d GCP source engines started" , len (gcpEngines )))
281319 }
282320
321+ foundCloudProvider = true
283322 return gcpEngines , nil
284323 })
285324
@@ -288,6 +327,19 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
288327 return func () {}, fmt .Errorf ("error starting sources: %w" , err )
289328 }
290329
330+ if ! foundCloudProvider {
331+ statusArea .Println (`No cloud providers found in Terraform configuration.
332+
333+ The Overmind CLI requires access to cloud provider configurations to interrogate resources. Without configured providers, the CLI cannot determine which cloud resources to query and as a result calculate a successful blast radius.
334+
335+ To resolve this issue ensure your Terraform configuration files define at least one supported cloud provider (e.g., AWS, GCP)
336+
337+ For more information about configuring cloud providers in Terraform, visit:
338+ - AWS: https://registry.terraform.io/providers/hashicorp/aws/latest/docs
339+ - GCP: https://registry.terraform.io/providers/hashicorp/google/latest/docs` )
340+ }
341+
342+ // Return a cleanup function to stop all engines
291343 return func () {
292344 for _ , e := range slices .Concat (engines ... ) {
293345 err := e .Stop ()
0 commit comments