@@ -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 {}
@@ -117,6 +123,20 @@ func (p *Plugin) runRules() {
117
123
glog .Info ("Finish running custom plugins" )
118
124
}
119
125
126
+ // readFromReader reads the maxBytes from the reader and drains the rest.
127
+ func readFromReader (reader io.ReadCloser , maxBytes int64 ) ([]byte , error ) {
128
+ limitReader := io .LimitReader (reader , maxBytes )
129
+ data , err := ioutil .ReadAll (limitReader )
130
+ if err != nil {
131
+ return []byte {}, err
132
+ }
133
+ // Drain the reader
134
+ if _ , err := io .Copy (ioutil .Discard , reader ); err != nil {
135
+ return []byte {}, err
136
+ }
137
+ return data , nil
138
+ }
139
+
120
140
func (p * Plugin ) run (rule cpmtypes.CustomRule ) (exitStatus cpmtypes.Status , output string ) {
121
141
var ctx context.Context
122
142
var cancel context.CancelFunc
@@ -129,14 +149,66 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
129
149
defer cancel ()
130
150
131
151
cmd := exec .CommandContext (ctx , rule .Path , rule .Args ... )
132
- stdout , err := cmd .Output ()
152
+
153
+ stdoutPipe , err := cmd .StdoutPipe ()
154
+ if err != nil {
155
+ glog .Errorf ("Error creating stdout pipe for plugin %q: error - %v" , rule .Path , err )
156
+ return cpmtypes .Unknown , "Error creating stdout pipe for plugin. Please check the error log"
157
+ }
158
+ stderrPipe , err := cmd .StderrPipe ()
133
159
if err != nil {
160
+ glog .Errorf ("Error creating stderr pipe for plugin %q: error - %v" , rule .Path , err )
161
+ return cpmtypes .Unknown , "Error creating stderr pipe for plugin. Please check the error log"
162
+ }
163
+ if err := cmd .Start (); err != nil {
164
+ glog .Errorf ("Error in starting plugin %q: error - %v" , rule .Path , err )
165
+ return cpmtypes .Unknown , "Error in starting plugin. Please check the error log"
166
+ }
167
+
168
+ var (
169
+ wg sync.WaitGroup
170
+ stdout []byte
171
+ stderr []byte
172
+ stdoutErr error
173
+ stderrErr error
174
+ )
175
+
176
+ wg .Add (2 )
177
+ go func () {
178
+ stdout , stdoutErr = readFromReader (stdoutPipe , maxCustomPluginBufferBytes )
179
+ wg .Done ()
180
+ }()
181
+ go func () {
182
+ stderr , stderrErr = readFromReader (stderrPipe , maxCustomPluginBufferBytes )
183
+ wg .Done ()
184
+ }()
185
+ // This will wait for the reads to complete. If the execution times out, the pipes
186
+ // will be closed and the wait group unblocks.
187
+ wg .Wait ()
188
+
189
+ if stdoutErr != nil {
190
+ glog .Errorf ("Error reading stdout for plugin %q: error - %v" , rule .Path , err )
191
+ return cpmtypes .Unknown , "Error reading stdout for plugin. Please check the error log"
192
+ }
193
+
194
+ if stderrErr != nil {
195
+ glog .Errorf ("Error reading stderr for plugin %q: error - %v" , rule .Path , err )
196
+ return cpmtypes .Unknown , "Error reading stderr for plugin. Please check the error log"
197
+ }
198
+
199
+ if err := cmd .Wait (); err != nil {
134
200
if _ , ok := err .(* exec.ExitError ); ! ok {
135
- glog .Errorf ("Error in running plugin %q: error - %v. output - %q" , rule .Path , err , string (stdout ))
136
- return cpmtypes .Unknown , "Error in running plugin. Please check the error log"
201
+ glog .Errorf ("Error in waiting for plugin %q: error - %v. output - %q" , rule .Path , err , string (stdout ))
202
+ return cpmtypes .Unknown , "Error in waiting for plugin. Please check the error log"
137
203
}
138
204
}
139
205
206
+ // log the stderr from the plugin
207
+ if len (stderr ) != 0 {
208
+ glog .Infof ("Start logs from plugin %q \n %s" , rule .Path , string (stderr ))
209
+ glog .Infof ("End logs from plugin %q" , rule .Path )
210
+ }
211
+
140
212
// trim suffix useless bytes
141
213
output = string (stdout )
142
214
output = strings .TrimSpace (output )
0 commit comments