@@ -22,26 +22,65 @@ var (
22
22
allowedTerraformChars = regexp .MustCompile (`[^a-zA-Z0-9_-]` )
23
23
)
24
24
25
- func Generate (ctx context.Context , cfg * Config ) error {
25
+ // ResourceError is an error that occurred while generating a resource.
26
+ // It can be filtered out by the caller if it is not critical that a single resource failed.
27
+ type ResourceError struct {
28
+ Resource * common.Resource
29
+ Err error
30
+ }
31
+
32
+ func (e ResourceError ) Error () string {
33
+ return fmt .Sprintf ("resource %s: %v" , e .Resource .Name , e .Err )
34
+ }
35
+
36
+ type GenerationSuccess struct {
37
+ Resource * common.Resource
38
+ Blocks int
39
+ }
40
+
41
+ type GenerationResult struct {
42
+ Success []GenerationSuccess
43
+ Errors []error
44
+ }
45
+
46
+ func (r GenerationResult ) Blocks () int {
47
+ blocks := 0
48
+ for _ , s := range r .Success {
49
+ blocks += s .Blocks
50
+ }
51
+ return blocks
52
+ }
53
+
54
+ func failure (err error ) GenerationResult {
55
+ return GenerationResult {
56
+ Errors : []error {err },
57
+ }
58
+ }
59
+
60
+ func failuref (format string , args ... any ) GenerationResult {
61
+ return failure (fmt .Errorf (format , args ... ))
62
+ }
63
+
64
+ func Generate (ctx context.Context , cfg * Config ) GenerationResult {
26
65
var err error
27
66
if ! filepath .IsAbs (cfg .OutputDir ) {
28
67
if cfg .OutputDir , err = filepath .Abs (cfg .OutputDir ); err != nil {
29
- return fmt . Errorf ("failed to get absolute path for %s: %w" , cfg .OutputDir , err )
68
+ return failuref ("failed to get absolute path for %s: %w" , cfg .OutputDir , err )
30
69
}
31
70
}
32
71
33
72
if _ , err := os .Stat (cfg .OutputDir ); err == nil && cfg .Clobber {
34
73
log .Printf ("Deleting all files in %s" , cfg .OutputDir )
35
74
if err := os .RemoveAll (cfg .OutputDir ); err != nil {
36
- return fmt . Errorf ("failed to delete %s: %s" , cfg .OutputDir , err )
75
+ return failuref ("failed to delete %s: %s" , cfg .OutputDir , err )
37
76
}
38
77
} else if err == nil && ! cfg .Clobber {
39
- return fmt . Errorf ("output dir %q already exists. Use the clobber option to delete it" , cfg .OutputDir )
78
+ return failuref ("output dir %q already exists. Use the clobber option to delete it" , cfg .OutputDir )
40
79
}
41
80
42
81
log .Printf ("Generating resources to %s" , cfg .OutputDir )
43
82
if err := os .MkdirAll (cfg .OutputDir , 0755 ); err != nil {
44
- return fmt . Errorf ("failed to create output directory %s: %s" , cfg .OutputDir , err )
83
+ return failuref ("failed to create output directory %s: %s" , cfg .OutputDir , err )
45
84
}
46
85
47
86
// Generate provider installation block
@@ -59,22 +98,21 @@ func Generate(ctx context.Context, cfg *Config) error {
59
98
tf , err := setupTerraform (cfg )
60
99
// Terraform init to download the provider
61
100
if err != nil {
62
- return fmt . Errorf ("failed to run terraform init: %w" , err )
101
+ return failuref ("failed to run terraform init: %w" , err )
63
102
}
64
103
cfg .Terraform = tf
65
104
105
+ var returnResult GenerationResult
66
106
if cfg .Cloud != nil {
67
107
log .Printf ("Generating cloud resources" )
68
- stacks , err := generateCloudResources (ctx , cfg )
69
- if err != nil {
70
- return err
71
- }
108
+ var stacks []stack
109
+ stacks , returnResult = generateCloudResources (ctx , cfg )
72
110
73
111
for _ , stack := range stacks {
74
112
stack .name = "stack-" + stack .slug
75
- if err := generateGrafanaResources (ctx , cfg , stack , false ); err != nil {
76
- return err
77
- }
113
+ stackResult := generateGrafanaResources (ctx , cfg , stack , false )
114
+ returnResult . Success = append ( returnResult . Success , stackResult . Success ... )
115
+ returnResult . Errors = append ( returnResult . Errors , stackResult . Errors ... )
78
116
}
79
117
}
80
118
@@ -89,29 +127,42 @@ func Generate(ctx context.Context, cfg *Config) error {
89
127
onCallURL : cfg .Grafana .OnCallURL ,
90
128
}
91
129
log .Printf ("Generating Grafana resources" )
92
- if err := generateGrafanaResources (ctx , cfg , stack , true ); err != nil {
93
- return err
130
+ returnResult = generateGrafanaResources (ctx , cfg , stack , true )
131
+ }
132
+
133
+ if ! cfg .OutputCredentials && cfg .Format != OutputFormatCrossplane {
134
+ if err := postprocessing .RedactCredentials (cfg .OutputDir ); err != nil {
135
+ return failuref ("failed to redact credentials: %w" , err )
94
136
}
95
137
}
96
138
97
- if cfg .Format == OutputFormatCrossplane {
98
- return convertToCrossplane (cfg )
139
+ if returnResult .Blocks () == 0 {
140
+ if err := os .WriteFile (filepath .Join (cfg .OutputDir , "resources.tf" ), []byte ("# No resources were found\n " ), 0600 ); err != nil {
141
+ return failure (err )
142
+ }
143
+ if err := os .WriteFile (filepath .Join (cfg .OutputDir , "imports.tf" ), []byte ("# No resources were found\n " ), 0600 ); err != nil {
144
+ return failure (err )
145
+ }
146
+ return returnResult
99
147
}
100
148
101
- if ! cfg .OutputCredentials {
102
- if err := postprocessing . RedactCredentials (cfg . OutputDir ); err != nil {
103
- return fmt . Errorf ( "failed to redact credentials: %w" , err )
149
+ if cfg .Format == OutputFormatCrossplane {
150
+ if err := convertToCrossplane (cfg ); err != nil {
151
+ return failure ( err )
104
152
}
153
+ return returnResult
105
154
}
106
155
107
156
if cfg .Format == OutputFormatJSON {
108
- return convertToTFJSON (cfg .OutputDir )
157
+ if err := convertToTFJSON (cfg .OutputDir ); err != nil {
158
+ return failure (err )
159
+ }
109
160
}
110
161
111
- return nil
162
+ return returnResult
112
163
}
113
164
114
- func generateImportBlocks (ctx context.Context , client * common.Client , listerData any , resources []* common.Resource , cfg * Config , provider string ) error {
165
+ func generateImportBlocks (ctx context.Context , client * common.Client , listerData any , resources []* common.Resource , cfg * Config , provider string ) GenerationResult {
115
166
generatedFilename := func (suffix string ) string {
116
167
if provider == "" {
117
168
return filepath .Join (cfg .OutputDir , suffix )
@@ -122,7 +173,7 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
122
173
123
174
resources , err := filterResources (resources , cfg .IncludeResources )
124
175
if err != nil {
125
- return err
176
+ return failure ( err )
126
177
}
127
178
128
179
// Generate HCL blocks in parallel with a wait group
@@ -207,12 +258,21 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
207
258
wg .Wait ()
208
259
close (results )
209
260
261
+ returnResult := GenerationResult {}
210
262
resultsSlice := []result {}
211
263
for r := range results {
212
264
if r .err != nil {
213
- return fmt .Errorf ("failed to generate %s resources: %w" , r .resource .Name , r .err )
265
+ returnResult .Errors = append (returnResult .Errors , ResourceError {
266
+ Resource : r .resource ,
267
+ Err : r .err ,
268
+ })
269
+ } else {
270
+ resultsSlice = append (resultsSlice , r )
271
+ returnResult .Success = append (returnResult .Success , GenerationSuccess {
272
+ Resource : r .resource ,
273
+ Blocks : len (r .blocks ),
274
+ })
214
275
}
215
- resultsSlice = append (resultsSlice , r )
216
276
}
217
277
sort .Slice (resultsSlice , func (i , j int ) bool {
218
278
return resultsSlice [i ].resource .Name < resultsSlice [j ].resource .Name
@@ -225,23 +285,21 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
225
285
}
226
286
227
287
if len (allBlocks ) == 0 {
228
- if err := os .WriteFile (generatedFilename ("resources.tf" ), []byte ("# No resources were found\n " ), 0600 ); err != nil {
229
- return err
230
- }
231
- if err := os .WriteFile (generatedFilename ("imports.tf" ), []byte ("# No resources were found\n " ), 0600 ); err != nil {
232
- return err
233
- }
234
- return nil
288
+ return returnResult
235
289
}
236
290
237
291
if err := writeBlocks (generatedFilename ("imports.tf" ), allBlocks ... ); err != nil {
238
- return err
292
+ return failure ( err )
239
293
}
240
294
_ , err = cfg .Terraform .Plan (ctx , tfexec .GenerateConfigOut (generatedFilename ("resources.tf" )))
241
295
if err != nil {
242
- return fmt . Errorf ("failed to generate resources: %w" , err )
296
+ return failuref ("failed to generate resources: %w" , err )
243
297
}
244
- return sortResourcesFile (generatedFilename ("resources.tf" ))
298
+ if err := sortResourcesFile (generatedFilename ("resources.tf" )); err != nil {
299
+ return failure (err )
300
+ }
301
+
302
+ return returnResult
245
303
}
246
304
247
305
func filterResources (resources []* common.Resource , includedResources []string ) ([]* common.Resource , error ) {
0 commit comments