Skip to content

Commit 6acf5b1

Browse files
committed
Capture the logs from stderr of custom plugins.
1 parent 061e977 commit 6acf5b1

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

pkg/custompluginmonitor/plugin/plugin.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package plugin
1919
import (
2020
"context"
2121
"fmt"
22+
"io"
23+
"io/ioutil"
2224
"os/exec"
2325
"strings"
2426
"sync"
@@ -30,6 +32,10 @@ import (
3032
"k8s.io/node-problem-detector/pkg/util/tomb"
3133
)
3234

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+
3339
type Plugin struct {
3440
config cpmtypes.CustomPluginConfig
3541
syncChan chan struct{}
@@ -115,6 +121,20 @@ func (p *Plugin) runRules() {
115121
glog.Info("Finish running custom plugins")
116122
}
117123

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+
118138
func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, output string) {
119139
var ctx context.Context
120140
var cancel context.CancelFunc
@@ -127,14 +147,66 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
127147
defer cancel()
128148

129149
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()
131157
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 {
132198
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"
135201
}
136202
}
137203

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+
138210
// trim suffix useless bytes
139211
output = string(stdout)
140212
output = strings.TrimSpace(output)

pkg/custompluginmonitor/plugin/plugin_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func TestNewPluginRun(t *testing.T) {
6161
Timeout: &ruleTimeout,
6262
},
6363
ExitStatus: cpmtypes.Unknown,
64-
Output: "Error in running plugin. Please check the error log",
64+
Output: "Error in starting plugin. Please check the error log",
6565
},
6666
"longer than 80 stdout with ok exit status": {
6767
Rule: cpmtypes.CustomRule{

0 commit comments

Comments
 (0)