@@ -19,6 +19,8 @@ package plugin
19
19
import (
20
20
"context"
21
21
"fmt"
22
+ "io"
23
+ "io/ioutil"
22
24
"os/exec"
23
25
"strings"
24
26
"sync"
@@ -30,6 +32,10 @@ import (
30
32
"k8s.io/node-problem-detector/pkg/util/tomb"
31
33
)
32
34
35
+ // maxCustomPluginBufferBytes is the max bytes that a custom plugin is allowed to
36
+ // send to stdout/stderr. Any bytes exceeding this value will be truncated.
37
+ const maxCustomPluginBufferBytes = 1024 * 4
38
+
33
39
type Plugin struct {
34
40
config cpmtypes.CustomPluginConfig
35
41
syncChan chan struct {}
@@ -115,6 +121,20 @@ func (p *Plugin) runRules() {
115
121
glog .Info ("Finish running custom plugins" )
116
122
}
117
123
124
+ // readFromReader reads the maxBytes from the reader and drains the rest.
125
+ func readFromReader (reader io.ReadCloser , maxBytes int64 ) ([]byte , error ) {
126
+ limitReader := io .LimitReader (reader , maxBytes )
127
+ data , err := ioutil .ReadAll (limitReader )
128
+ if err != nil {
129
+ return []byte {}, err
130
+ }
131
+ // Drain the reader
132
+ if _ , err := io .Copy (ioutil .Discard , reader ); err != nil {
133
+ return []byte {}, err
134
+ }
135
+ return data , nil
136
+ }
137
+
118
138
func (p * Plugin ) run (rule cpmtypes.CustomRule ) (exitStatus cpmtypes.Status , output string ) {
119
139
var ctx context.Context
120
140
var cancel context.CancelFunc
@@ -127,14 +147,66 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
127
147
defer cancel ()
128
148
129
149
cmd := exec .CommandContext (ctx , rule .Path , rule .Args ... )
130
- stdout , err := cmd .Output ()
150
+
151
+ stdoutPipe , err := cmd .StdoutPipe ()
152
+ if err != nil {
153
+ glog .Errorf ("Error creating stdout pipe for plugin %q: error - %v" , rule .Path , err )
154
+ return cpmtypes .Unknown , "Error creating stdout pipe for plugin. Please check the error log"
155
+ }
156
+ stderrPipe , err := cmd .StderrPipe ()
131
157
if err != nil {
158
+ glog .Errorf ("Error creating stderr pipe for plugin %q: error - %v" , rule .Path , err )
159
+ return cpmtypes .Unknown , "Error creating stderr pipe for plugin. Please check the error log"
160
+ }
161
+ if err := cmd .Start (); err != nil {
162
+ glog .Errorf ("Error in starting plugin %q: error - %v" , rule .Path , err )
163
+ return cpmtypes .Unknown , "Error in starting plugin. Please check the error log"
164
+ }
165
+
166
+ var (
167
+ wg sync.WaitGroup
168
+ stdout []byte
169
+ stderr []byte
170
+ stdoutErr error
171
+ stderrErr error
172
+ )
173
+
174
+ wg .Add (2 )
175
+ go func () {
176
+ stdout , stdoutErr = readFromReader (stdoutPipe , maxCustomPluginBufferBytes )
177
+ wg .Done ()
178
+ }()
179
+ go func () {
180
+ stderr , stderrErr = readFromReader (stderrPipe , maxCustomPluginBufferBytes )
181
+ wg .Done ()
182
+ }()
183
+ // This will wait for the reads to complete. If the execution times out, the pipes
184
+ // will be closed and the wait group unblocks.
185
+ wg .Wait ()
186
+
187
+ if stdoutErr != nil {
188
+ glog .Errorf ("Error reading stdout for plugin %q: error - %v" , rule .Path , err )
189
+ return cpmtypes .Unknown , "Error reading stdout for plugin. Please check the error log"
190
+ }
191
+
192
+ if stderrErr != nil {
193
+ glog .Errorf ("Error reading stderr for plugin %q: error - %v" , rule .Path , err )
194
+ return cpmtypes .Unknown , "Error reading stderr for plugin. Please check the error log"
195
+ }
196
+
197
+ if err := cmd .Wait (); err != nil {
132
198
if _ , ok := err .(* exec.ExitError ); ! ok {
133
- glog .Errorf ("Error in running plugin %q: error - %v. output - %q" , rule .Path , err , string (stdout ))
134
- return cpmtypes .Unknown , "Error in running plugin. Please check the error log"
199
+ glog .Errorf ("Error in waiting for plugin %q: error - %v. output - %q" , rule .Path , err , string (stdout ))
200
+ return cpmtypes .Unknown , "Error in waiting for plugin. Please check the error log"
135
201
}
136
202
}
137
203
204
+ // log the stderr from the plugin
205
+ if len (stderr ) != 0 {
206
+ glog .Infof ("Start logs from plugin %q \n %s" , rule .Path , string (stderr ))
207
+ glog .Infof ("End logs from plugin %q" , rule .Path )
208
+ }
209
+
138
210
// trim suffix useless bytes
139
211
output = string (stdout )
140
212
output = strings .TrimSpace (output )
0 commit comments