4
4
"context"
5
5
"fmt"
6
6
"os"
7
+ "path/filepath"
7
8
"slices"
8
9
9
10
"atomicgo.dev/keyboard"
@@ -56,10 +57,6 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
56
57
defer func () {
57
58
_ , _ = multi .Stop ()
58
59
}()
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 ())
63
60
64
61
natsOpts := natsOptions (ctx , oi , token )
65
62
@@ -70,6 +67,31 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
70
67
71
68
p := pool .NewWithResults [[]* discovery.Engine ]().WithErrors ()
72
69
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
+
73
95
p .Go (func () ([]* discovery.Engine , error ) { //nolint:contextcheck // todo: pass in context with timeout to abort timely and allow Ctrl-C to work
74
96
ec := discovery.EngineConfig {
75
97
Version : fmt .Sprintf ("cli-%v" , tracing .Version ()),
@@ -107,13 +129,19 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
107
129
return nil , fmt .Errorf ("failed to load variables from the environment: %w" , err )
108
130
}
109
131
110
- providers , err := tfutils .ParseAWSProviders ("." , tfEval )
132
+ awsProviders , err := tfutils .ParseAWSProviders ("." , tfEval )
111
133
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
114
141
}
142
+
115
143
configs := []aws.Config {}
116
- for _ , p := range providers {
144
+ for _ , p := range awsProviders {
117
145
if p .Error != nil {
118
146
// skip providers that had errors. This allows us to use
119
147
// providers we _could_ detect, while still failing if there is
@@ -123,20 +151,24 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
123
151
}
124
152
c , err := tfutils .ConfigFromProvider (ctx , * p .Provider )
125
153
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 )
128
156
}
129
157
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 ))
131
163
configs = append (configs , c )
132
164
}
133
165
if len (configs ) == 0 && failOverToDefaultLoginCfg {
166
+ statusArea .Println ("No AWS terraform providers found. Attempting to use AWS default credentials for configuration." )
134
167
userConfig , err := config .LoadDefaultConfig (ctx )
135
168
if err != nil {
136
- awsSpinner .Fail ("Failed to load default AWS config" )
169
+ awsSpinner .Fail ("Failed to load default AWS config: " , err )
137
170
return nil , fmt .Errorf ("failed to load default AWS config: %w" , err )
138
171
}
139
- statusArea .Println ("Using default AWS CLI config. No AWS terraform providers found." )
140
172
configs = append (configs , userConfig )
141
173
}
142
174
ec := discovery.EngineConfig {
@@ -173,6 +205,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
173
205
}
174
206
175
207
awsSpinner .Success ("AWS source engine started" )
208
+ foundCloudProvider = true
176
209
return []* discovery.Engine {awsEngine }, nil
177
210
})
178
211
@@ -190,6 +223,11 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
190
223
return nil , fmt .Errorf ("failed to parse GCP providers: %w" , err )
191
224
}
192
225
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
+
193
231
// Process GCP providers and extract configurations
194
232
gcpConfigs := []* gcpproc.GCPConfig {}
195
233
@@ -201,7 +239,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
201
239
202
240
config , err := tfutils .ConfigFromGCPProvider (* p .Provider )
203
241
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 ()))
205
243
continue
206
244
}
207
245
@@ -215,12 +253,12 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
215
253
if config .Alias != "" {
216
254
aliasInfo = fmt .Sprintf (" (alias: %s)" , config .Alias )
217
255
}
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 ))
219
257
}
220
258
221
259
// Fallback to default GCP config if no terraform providers found
222
260
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 ." )
224
262
// Try to use Application Default Credentials by passing nil config
225
263
gcpConfigs = append (gcpConfigs , nil )
226
264
}
@@ -280,6 +318,7 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
280
318
gcpSpinner .Success (fmt .Sprintf ("%d GCP source engines started" , len (gcpEngines )))
281
319
}
282
320
321
+ foundCloudProvider = true
283
322
return gcpEngines , nil
284
323
})
285
324
@@ -288,6 +327,19 @@ func StartLocalSources(ctx context.Context, oi sdp.OvermindInstance, token *oaut
288
327
return func () {}, fmt .Errorf ("error starting sources: %w" , err )
289
328
}
290
329
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
291
343
return func () {
292
344
for _ , e := range slices .Concat (engines ... ) {
293
345
err := e .Stop ()
0 commit comments