5
5
"encoding/json"
6
6
"fmt"
7
7
"io"
8
- "os"
9
8
"strings"
10
9
11
10
"github.com/rs/zerolog"
@@ -19,11 +18,20 @@ import (
19
18
"github.com/snyk/go-application-framework/pkg/workflow"
20
19
)
21
20
21
+ // WriterEntry is an internal structure to handle all template based writers
22
22
type WriterEntry struct {
23
- writer io.Writer
24
- mimeType string
25
- templates []string
26
- closer func () error
23
+ writer io.WriteCloser
24
+ mimeType string
25
+ templates []string
26
+ renderEmptyData bool
27
+ }
28
+
29
+ // FileWriter is a public structure used to configure file based rendering
30
+ type FileWriter struct {
31
+ NameConfigKey string // defines the configuration key to look up the filename under
32
+ MimeType string // defines the final mime type of the rendering
33
+ TemplateFiles []string // defines the set of template files to use for rendering
34
+ WriteEmptyContent bool // specifies if anything should be written at all if the input data is empty
27
35
}
28
36
29
37
func getListOfFindings (input []workflow.Data , debugLogger * zerolog.Logger ) (findings []* local_models.LocalFinding , remainingData []workflow.Data ) {
@@ -65,83 +73,89 @@ func getTotalNumberOfFindings(findings []*local_models.LocalFinding) uint32 {
65
73
return count
66
74
}
67
75
68
- func getWritersToUse (config configuration.Configuration , outputDestination iUtils.OutputDestination , findings []* local_models.LocalFinding ) (map [string ]* WriterEntry , error ) {
76
+ func getWritersToUse (config configuration.Configuration , outputDestination iUtils.OutputDestination ) map [string ]* WriterEntry {
77
+ // resulting map of writers and their templates
69
78
writerMap := map [string ]* WriterEntry {
70
- DEFAULT_WRITER : {
71
- writer : outputDestination .GetWriter (),
72
- mimeType : presenters .DefaultMimeType ,
73
- templates : presenters .DefaultTemplateFiles ,
74
- closer : func () error {
75
- _ , err := fmt .Fprintln (outputDestination .GetWriter (), "" )
76
- return err
77
- },
78
- },
79
+ DEFAULT_WRITER : getDefaultWriter (config , outputDestination ),
79
80
}
80
81
81
- sarifWriter , err := getSarifFileRenderer (config , findings )
82
- if err != nil {
83
- return writerMap , err
82
+ // default file writers
83
+ fileWriters := []FileWriter {
84
+ {
85
+ OUTPUT_CONFIG_KEY_SARIF_FILE ,
86
+ SARIF_MIME_TYPE ,
87
+ ApplicationSarifTemplates ,
88
+ true ,
89
+ },
90
+ /*
91
+ skipping support for json file output by default, since there is no supporting rendering yet.
92
+ {
93
+ OUTPUT_CONFIG_KEY_JSON_FILE,
94
+ SARIF_MIME_TYPE,
95
+ ApplicationSarifTemplates,
96
+ true,
97
+ },*/
98
+ }
99
+
100
+ // use configured file writers if available
101
+ if tmp , ok := config .Get (OUTPUT_CONFIG_KEY_FILE_WRITERS ).([]FileWriter ); ok {
102
+ fileWriters = tmp
103
+ }
104
+
105
+ for _ , fileWriter := range fileWriters {
106
+ if config .IsSet (fileWriter .NameConfigKey ) {
107
+ writerMap [fileWriter .NameConfigKey ] = & WriterEntry {
108
+ writer : & delayedFileOpenWriteCloser {Filename : config .GetString (fileWriter .NameConfigKey )},
109
+ mimeType : fileWriter .MimeType ,
110
+ templates : fileWriter .TemplateFiles ,
111
+ renderEmptyData : fileWriter .WriteEmptyContent ,
112
+ }
113
+ }
84
114
}
85
115
86
- if sarifWriter != nil {
87
- writerMap [OUTPUT_CONFIG_KEY_SARIF_FILE ] = sarifWriter
116
+ return writerMap
117
+ }
118
+
119
+ func getDefaultWriter (config configuration.Configuration , outputDestination iUtils.OutputDestination ) * WriterEntry {
120
+ writer := & WriterEntry {
121
+ writer : & newLineCloser {
122
+ writer : outputDestination .GetWriter (),
123
+ },
124
+ mimeType : DEFAULT_MIME_TYPE ,
125
+ templates : DefaultTemplateFiles ,
126
+ renderEmptyData : true ,
88
127
}
89
128
90
129
if config .GetBool (OUTPUT_CONFIG_KEY_SARIF ) {
91
- writerMap [ DEFAULT_WRITER ] .mimeType = presenters . ApplicationSarifMimeType
92
- writerMap [ DEFAULT_WRITER ] .templates = presenters . ApplicationSarifTemplates
130
+ writer .mimeType = SARIF_MIME_TYPE
131
+ writer .templates = ApplicationSarifTemplates
93
132
}
94
133
95
134
if config .IsSet (OUTPUT_CONFIG_TEMPLATE_FILE ) {
96
- writerMap [ DEFAULT_WRITER ] .templates = []string {config .GetString (OUTPUT_CONFIG_TEMPLATE_FILE )}
135
+ writer .templates = []string {config .GetString (OUTPUT_CONFIG_TEMPLATE_FILE )}
97
136
}
98
137
99
- return writerMap , nil
138
+ return writer
100
139
}
101
140
102
- func getSarifFileRenderer (config configuration.Configuration , findings []* local_models.LocalFinding ) (* WriterEntry , error ) {
103
- outputFileName := config .GetString (OUTPUT_CONFIG_KEY_SARIF_FILE )
104
- if len (outputFileName ) == 0 {
105
- //nolint:nilnil // returning a nil writer is a valid case based on the configuration and is not an error case
106
- return nil , nil
107
- }
108
-
109
- if ! config .GetBool (OUTPUT_CONFIG_WRITE_EMPTY_FILE ) && getTotalNumberOfFindings (findings ) == 0 {
110
- //nolint:nilnil // returning a nil writer is a valid case based on the configuration and is not an error case
111
- return nil , nil
112
- }
113
-
114
- pathError := iUtils .CreateFilePath (outputFileName )
115
- if pathError != nil {
116
- return nil , pathError
117
- }
118
-
119
- file , fileErr := os .OpenFile (outputFileName , os .O_WRONLY | os .O_CREATE , 0644 )
120
- if fileErr != nil {
121
- return nil , fileErr
122
- }
141
+ func useRendererWith (name string , wEntry * WriterEntry , findings []* local_models.LocalFinding , invocation workflow.InvocationContext ) {
142
+ debugLogger := invocation .GetEnhancedLogger ()
123
143
124
- writer := & WriterEntry {
125
- writer : file ,
126
- mimeType : presenters .ApplicationSarifMimeType ,
127
- templates : presenters .ApplicationSarifTemplates ,
128
- closer : func () error { return file .Close () },
144
+ if ! wEntry .renderEmptyData && getTotalNumberOfFindings (findings ) == 0 {
145
+ debugLogger .Info ().Msgf ("[%s] The input is empty, skipping rendering!" , name )
146
+ return
129
147
}
130
- return writer , nil
131
- }
132
148
133
- func useRendererWith (name string , wEntry * WriterEntry , debugLogger * zerolog.Logger , findings []* local_models.LocalFinding , config configuration.Configuration , invocation workflow.InvocationContext ) {
134
149
debugLogger .Info ().Msgf ("[%s] Creating findings model renderer" , name )
135
150
136
- if wEntry .closer != nil {
137
- defer func () {
138
- closeErr := wEntry .closer ()
139
- if closeErr != nil {
140
- debugLogger .Err (closeErr ).Msgf ("[%s] Error while closing writer." , name )
141
- }
142
- }()
143
- }
151
+ defer func () {
152
+ closeErr := wEntry .writer .Close ()
153
+ if closeErr != nil {
154
+ debugLogger .Err (closeErr ).Msgf ("[%s] Error while closing writer." , name )
155
+ }
156
+ }()
144
157
158
+ config := invocation .GetConfiguration ()
145
159
renderer := presenters .NewLocalFindingsRenderer (
146
160
findings ,
147
161
config ,
@@ -160,6 +174,7 @@ func useRendererWith(name string, wEntry *WriterEntry, debugLogger *zerolog.Logg
160
174
}
161
175
162
176
func HandleContentTypeFindingsModel (input []workflow.Data , invocation workflow.InvocationContext , outputDestination iUtils.OutputDestination ) ([]workflow.Data , error ) {
177
+ var err error
163
178
debugLogger := invocation .GetEnhancedLogger ()
164
179
config := invocation .GetConfiguration ()
165
180
@@ -172,24 +187,22 @@ func HandleContentTypeFindingsModel(input []workflow.Data, invocation workflow.I
172
187
threadCount := max (int64 (config .GetInt (configuration .MAX_THREADS )), 1 )
173
188
debugLogger .Info ().Msgf ("Thread count: %d" , threadCount )
174
189
175
- writerMap , err := getWritersToUse (config , outputDestination , findings )
176
- if err != nil {
177
- debugLogger .Err (err ).Msg ("Failed to initialize all required writers" )
178
- }
190
+ writerMap := getWritersToUse (config , outputDestination )
179
191
180
192
// iterate over all writers and render for each of them
181
193
ctx := context .Background ()
182
194
availableThreads := semaphore .NewWeighted (threadCount )
183
195
for k , v := range writerMap {
184
196
err = availableThreads .Acquire (ctx , 1 )
185
197
if err != nil {
186
- debugLogger .Err (err ).Msgf ("[%s] Failed to acquire threading lock." , k )
198
+ debugLogger .Err (err ).Msgf ("[%s] Failed to acquire threading lock. Cancel rendering." , k )
199
+ break
187
200
}
188
201
189
- go func () {
202
+ go func (name string , writer * WriterEntry ) {
190
203
defer availableThreads .Release (1 )
191
- useRendererWith (k , v , debugLogger , findings , config , invocation )
192
- }()
204
+ useRendererWith (name , writer , findings , invocation )
205
+ }(k , v )
193
206
}
194
207
195
208
err = availableThreads .Acquire (ctx , threadCount )
0 commit comments