@@ -22,26 +22,65 @@ var (
2222 allowedTerraformChars = regexp .MustCompile (`[^a-zA-Z0-9_-]` )
2323)
2424
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 {
2665 var err error
2766 if ! filepath .IsAbs (cfg .OutputDir ) {
2867 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 )
3069 }
3170 }
3271
3372 if _ , err := os .Stat (cfg .OutputDir ); err == nil && cfg .Clobber {
3473 log .Printf ("Deleting all files in %s" , cfg .OutputDir )
3574 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 )
3776 }
3877 } 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 )
4079 }
4180
4281 log .Printf ("Generating resources to %s" , cfg .OutputDir )
4382 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 )
4584 }
4685
4786 // Generate provider installation block
@@ -59,22 +98,21 @@ func Generate(ctx context.Context, cfg *Config) error {
5998 tf , err := setupTerraform (cfg )
6099 // Terraform init to download the provider
61100 if err != nil {
62- return fmt . Errorf ("failed to run terraform init: %w" , err )
101+ return failuref ("failed to run terraform init: %w" , err )
63102 }
64103 cfg .Terraform = tf
65104
105+ var returnResult GenerationResult
66106 if cfg .Cloud != nil {
67107 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 )
72110
73111 for _ , stack := range stacks {
74112 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 ... )
78116 }
79117 }
80118
@@ -89,29 +127,42 @@ func Generate(ctx context.Context, cfg *Config) error {
89127 onCallURL : cfg .Grafana .OnCallURL ,
90128 }
91129 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 )
94136 }
95137 }
96138
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
99147 }
100148
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 )
104152 }
153+ return returnResult
105154 }
106155
107156 if cfg .Format == OutputFormatJSON {
108- return convertToTFJSON (cfg .OutputDir )
157+ if err := convertToTFJSON (cfg .OutputDir ); err != nil {
158+ return failure (err )
159+ }
109160 }
110161
111- return nil
162+ return returnResult
112163}
113164
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 {
115166 generatedFilename := func (suffix string ) string {
116167 if provider == "" {
117168 return filepath .Join (cfg .OutputDir , suffix )
@@ -122,7 +173,7 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
122173
123174 resources , err := filterResources (resources , cfg .IncludeResources )
124175 if err != nil {
125- return err
176+ return failure ( err )
126177 }
127178
128179 // Generate HCL blocks in parallel with a wait group
@@ -207,12 +258,21 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
207258 wg .Wait ()
208259 close (results )
209260
261+ returnResult := GenerationResult {}
210262 resultsSlice := []result {}
211263 for r := range results {
212264 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+ })
214275 }
215- resultsSlice = append (resultsSlice , r )
216276 }
217277 sort .Slice (resultsSlice , func (i , j int ) bool {
218278 return resultsSlice [i ].resource .Name < resultsSlice [j ].resource .Name
@@ -225,23 +285,21 @@ func generateImportBlocks(ctx context.Context, client *common.Client, listerData
225285 }
226286
227287 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
235289 }
236290
237291 if err := writeBlocks (generatedFilename ("imports.tf" ), allBlocks ... ); err != nil {
238- return err
292+ return failure ( err )
239293 }
240294 _ , err = cfg .Terraform .Plan (ctx , tfexec .GenerateConfigOut (generatedFilename ("resources.tf" )))
241295 if err != nil {
242- return fmt . Errorf ("failed to generate resources: %w" , err )
296+ return failuref ("failed to generate resources: %w" , err )
243297 }
244- return sortResourcesFile (generatedFilename ("resources.tf" ))
298+ if err := sortResourcesFile (generatedFilename ("resources.tf" )); err != nil {
299+ return failure (err )
300+ }
301+
302+ return returnResult
245303}
246304
247305func filterResources (resources []* common.Resource , includedResources []string ) ([]* common.Resource , error ) {
0 commit comments