Windows Support: Fix Build Regressions, Tests Pass

This commit is contained in:
Jeremy Edwards
2021-03-14 10:24:45 -07:00
parent cb8534b79b
commit 4181ece888
20 changed files with 322 additions and 55 deletions
+19 -10
View File
@@ -29,6 +29,7 @@ import (
"github.com/golang/glog"
cpmtypes "k8s.io/node-problem-detector/pkg/custompluginmonitor/types"
"k8s.io/node-problem-detector/pkg/util"
"k8s.io/node-problem-detector/pkg/util/tomb"
)
@@ -147,12 +148,7 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
}
defer cancel()
// create a process group
sysProcAttr := &syscall.SysProcAttr{
Setpgid: true,
}
cmd := exec.Command(rule.Path, rule.Args...)
cmd.SysProcAttr = sysProcAttr
cmd := util.Exec(rule.Path, rule.Args...)
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
@@ -172,6 +168,9 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
waitChan := make(chan struct{})
defer close(waitChan)
var m sync.Mutex
timeout := false
go func() {
select {
case <-ctx.Done():
@@ -183,7 +182,12 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
glog.Errorf("Error in cmd.Process check %q", rule.Path)
break
}
err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
m.Lock()
timeout = true
m.Unlock()
err := util.Kill(cmd)
if err != nil {
glog.Errorf("Error in kill process %d, %v", cmd.Process.Pid, err)
}
@@ -202,12 +206,12 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
wg.Add(2)
go func() {
defer wg.Done()
stdout, stdoutErr = readFromReader(stdoutPipe, maxCustomPluginBufferBytes)
wg.Done()
}()
go func() {
defer wg.Done()
stderr, stderrErr = readFromReader(stderrPipe, maxCustomPluginBufferBytes)
wg.Done()
}()
// This will wait for the reads to complete. If the execution times out, the pipes
// will be closed and the wait group unblocks.
@@ -234,7 +238,11 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
output = string(stdout)
output = strings.TrimSpace(output)
if cmd.ProcessState.Sys().(syscall.WaitStatus).Signaled() {
m.Lock()
cmdKilled := timeout
m.Unlock()
if cmdKilled {
output = fmt.Sprintf("Timeout when running plugin %q: state - %s. output - %q", rule.Path, cmd.ProcessState.String(), output)
}
@@ -257,6 +265,7 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
}
}
// Stop the plugin.
func (p *Plugin) Stop() {
p.tomb.Stop()
glog.Info("Stop plugin execution")
+36 -24
View File
@@ -17,6 +17,7 @@ limitations under the License.
package plugin
import (
"runtime"
"testing"
"time"
@@ -25,6 +26,13 @@ import (
func TestNewPluginRun(t *testing.T) {
ruleTimeout := 1 * time.Second
timeoutExitStatus := cpmtypes.Unknown
ext := "sh"
if runtime.GOOS == "windows" {
ext = "cmd"
timeoutExitStatus = cpmtypes.NonOK
}
utMetas := map[string]struct {
Rule cpmtypes.CustomRule
@@ -33,7 +41,7 @@ func TestNewPluginRun(t *testing.T) {
}{
"ok": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/ok.sh",
Path: "./test-data/ok." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.OK,
@@ -41,7 +49,7 @@ func TestNewPluginRun(t *testing.T) {
},
"non-ok": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/non-ok.sh",
Path: "./test-data/non-ok." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.NonOK,
@@ -49,7 +57,7 @@ func TestNewPluginRun(t *testing.T) {
},
"unknown": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/unknown.sh",
Path: "./test-data/unknown." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.Unknown,
@@ -57,6 +65,7 @@ func TestNewPluginRun(t *testing.T) {
},
"non executable": {
Rule: cpmtypes.CustomRule{
// Intentionally run .sh for Windows, this is meant to be not executable.
Path: "./test-data/non-executable.sh",
Timeout: &ruleTimeout,
},
@@ -65,7 +74,7 @@ func TestNewPluginRun(t *testing.T) {
},
"longer than 80 stdout with ok exit status": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/longer-than-80-stdout-with-ok-exit-status.sh",
Path: "./test-data/longer-than-80-stdout-with-ok-exit-status." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.OK,
@@ -73,7 +82,7 @@ func TestNewPluginRun(t *testing.T) {
},
"non defined exit status": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/non-defined-exit-status.sh",
Path: "./test-data/non-defined-exit-status." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.Unknown,
@@ -81,29 +90,32 @@ func TestNewPluginRun(t *testing.T) {
},
"sleep 3 second with ok exit status": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/sleep-3-second-with-ok-exit-status.sh",
Path: "./test-data/sleep-3-second-with-ok-exit-status." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.Unknown,
Output: `Timeout when running plugin "./test-data/sleep-3-second-with-ok-exit-status.sh": state - signal: killed. output - ""`,
ExitStatus: timeoutExitStatus,
Output: `Timeout when running plugin "./test-data/sleep-3-second-with-ok-exit-status.` + ext + `": state - signal: killed. output - ""`,
},
}
conf := cpmtypes.CustomPluginConfig{}
(&conf).ApplyConfiguration()
p := Plugin{config: conf}
for desp, utMeta := range utMetas {
gotExitStatus, gotOutput := p.run(utMeta.Rule)
// cut at position max_output_length if expected output is longer than max_output_length bytes
if len(utMeta.Output) > *p.config.PluginGlobalConfig.MaxOutputLength {
utMeta.Output = utMeta.Output[:*p.config.PluginGlobalConfig.MaxOutputLength]
}
if gotExitStatus != utMeta.ExitStatus || gotOutput != utMeta.Output {
t.Errorf("%s", desp)
t.Errorf("Error in run plugin and get exit status and output for %q. "+
"Got exit status: %v, Expected exit status: %v. "+
"Got output: %q, Expected output: %q",
utMeta.Rule.Path, gotExitStatus, utMeta.ExitStatus, gotOutput, utMeta.Output)
}
for k, v := range utMetas {
desp := k
utMeta := v
t.Run(desp, func(t *testing.T) {
conf := cpmtypes.CustomPluginConfig{}
(&conf).ApplyConfiguration()
p := Plugin{config: conf}
gotExitStatus, gotOutput := p.run(utMeta.Rule)
// cut at position max_output_length if expected output is longer than max_output_length bytes
if len(utMeta.Output) > *p.config.PluginGlobalConfig.MaxOutputLength {
utMeta.Output = utMeta.Output[:*p.config.PluginGlobalConfig.MaxOutputLength]
}
if gotExitStatus != utMeta.ExitStatus || gotOutput != utMeta.Output {
t.Errorf("Error in run plugin and get exit status and output for %q. "+
"Got exit status: %v, Expected exit status: %v. "+
"Got output: %q, Expected output: %q",
utMeta.Rule.Path, gotExitStatus, utMeta.ExitStatus, gotOutput, utMeta.Output)
}
})
}
}
@@ -0,0 +1,4 @@
@echo off
echo 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
exit 0
@@ -0,0 +1,4 @@
@echo off
echo NON-DEFINED-EXIT-STATUS
exit 100
@@ -0,0 +1,4 @@
@echo off
echo NonOK
exit 1
@@ -0,0 +1,4 @@
@echo off
echo OK
exit 0
@@ -0,0 +1,5 @@
@echo off
ping 127.0.0.1 -n 3 > nul
echo SLEEP 3S SECOND
exit 0
@@ -0,0 +1,4 @@
@echo off
echo UNKNOWN
exit 3