Skip to content

Windows Support: Fix Build Regressions, Tests Pass #517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ script:
- BUILD_TAGS="disable_stackdriver_exporter" make test
- make clean && ENABLE_JOURNALD=0 make
- ENABLE_JOURNALD=0 make test
- ENABLE_JOURNALD=0 make build-binaries
26 changes: 20 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ UPLOAD_PATH:=$(shell echo $(UPLOAD_PATH) | sed '$$s/\/*$$//')
PKG:=k8s.io/node-problem-detector

# PKG_SOURCES are all the go source code.
ifeq ($(OS),Windows_NT)
PKG_SOURCES:=
# TODO: File change detection does not work in Windows.
else
PKG_SOURCES:=$(shell find pkg cmd -name '*.go')
endif

# PARALLEL specifies the number of parallel test nodes to run for e2e tests.
PARALLEL?=3
Expand Down Expand Up @@ -74,6 +79,12 @@ BUILD_TAGS?=
LINUX_BUILD_TAGS = $(BUILD_TAGS)
WINDOWS_BUILD_TAGS = $(BUILD_TAGS)

ifeq ($(OS),Windows_NT)
HOST_PLATFORM_BUILD_TAGS = $(WINDOWS_BUILD_TAGS)
else
HOST_PLATFORM_BUILD_TAGS = $(LINUX_BUILD_TAGS)
endif

ifeq ($(ENABLE_JOURNALD), 1)
# Enable journald build tag.
LINUX_BUILD_TAGS := $(BUILD_TAGS) journald
Expand All @@ -91,9 +102,9 @@ else
endif

vet:
GO111MODULE=on go list -mod vendor -tags "$(LINUX_BUILD_TAGS)" ./... | \
GO111MODULE=on go list -mod vendor -tags "$(HOST_PLATFORM_BUILD_TAGS)" ./... | \
grep -v "./vendor/*" | \
GO111MODULE=on xargs go vet -mod vendor -tags "$(LINUX_BUILD_TAGS)"
GO111MODULE=on xargs go vet -mod vendor -tags "$(HOST_PLATFORM_BUILD_TAGS)"

fmt:
find . -type f -name "*.go" | grep -v "./vendor/*" | xargs gofmt -s -w -l
Expand All @@ -109,7 +120,10 @@ ifeq ($(ENABLE_JOURNALD), 1)
LINUX_AMD64_BINARIES += bin/linux_amd64/log-counter
endif

windows-binaries: $(WINDOWS_AMD64_BINARIES) $(WINDOWS_AMD64_TEST_BINARIES)
WINDOWS_BINARIES = $(WINDOWS_AMD64_BINARIES) $(WINDOWS_AMD64_TEST_BINARIES)
LINUX_BINARIES = $(LINUX_AMD64_BINARIES) $(LINUX_AMD64_TEST_BINARIES)

windows-binaries: $(WINDOWS_BINARIES)

bin/windows_amd64/%.exe: $(PKG_SOURCES)
ifeq ($(ENABLE_JOURNALD), 1)
Expand Down Expand Up @@ -191,10 +205,10 @@ endif
cmd/healthchecker/health_checker.go

test: vet fmt
GO111MODULE=on go test -mod vendor -timeout=1m -v -race -short -tags "$(LINUX_BUILD_TAGS)" ./...
GO111MODULE=on go test -mod vendor -timeout=1m -v -race -short -tags "$(HOST_PLATFORM_BUILD_TAGS)" ./...

e2e-test: vet fmt build-tar
GO111MODULE=on ginkgo -nodes=$(PARALLEL) -mod vendor -timeout=10m -v -tags "$(LINUX_BUILD_TAGS)" -stream \
GO111MODULE=on ginkgo -nodes=$(PARALLEL) -mod vendor -timeout=10m -v -tags "$(HOST_PLATFORM_BUILD_TAGS)" -stream \
./test/e2e/metriconly/... -- \
-project=$(PROJECT) -zone=$(ZONE) \
-image=$(VM_IMAGE) -image-family=$(IMAGE_FAMILY) -image-project=$(IMAGE_PROJECT) \
Expand All @@ -203,7 +217,7 @@ e2e-test: vet fmt build-tar
-boskos-project-type=$(BOSKOS_PROJECT_TYPE) -job-name=$(JOB_NAME) \
-artifacts-dir=$(ARTIFACTS)

build-binaries: ./bin/node-problem-detector ./bin/log-counter ./bin/health-checker
build-binaries: ./bin/node-problem-detector ./bin/log-counter ./bin/health-checker $(WINDOWS_BINARIES) $(LINUX_BINARIES)

build-container: build-binaries Dockerfile
docker build -t $(IMAGE) --build-arg BASEIMAGE=$(BASEIMAGE) --build-arg LOGCOUNTER=$(LOGCOUNTER) .
Expand Down
11 changes: 8 additions & 3 deletions config/windows-containerd-monitor-filelog.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
"logPath": "C:\\Program Files\\containerd\\containerd.log",
"lookback": "5m",
"bufferSize": 10,
"source": "docker-monitor",
"source": "containerd",
"conditions": [],
"rules": [
{
"type": "temporary",
"reason": "BadCNIConfig",
"pattern": "failed to reload cni configuration.*"
"reason": "MissingPigz",
"pattern": "unpigz not found.*"
},
{
"type": "temporary",
"reason": "IncompatibleContainer",
"pattern": ".*CreateComputeSystem.*"
}
]
}
29 changes: 19 additions & 10 deletions pkg/custompluginmonitor/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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 {
Expand All @@ -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():
Expand All @@ -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)
}
Expand All @@ -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.
Expand All @@ -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)
}

Expand All @@ -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")
Expand Down
60 changes: 36 additions & 24 deletions pkg/custompluginmonitor/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package plugin

import (
"runtime"
"testing"
"time"

Expand All @@ -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
Expand All @@ -33,30 +41,31 @@ func TestNewPluginRun(t *testing.T) {
}{
"ok": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/ok.sh",
Path: "./test-data/ok." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.OK,
Output: "OK",
},
"non-ok": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/non-ok.sh",
Path: "./test-data/non-ok." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.NonOK,
Output: "NonOK",
},
"unknown": {
Rule: cpmtypes.CustomRule{
Path: "./test-data/unknown.sh",
Path: "./test-data/unknown." + ext,
Timeout: &ruleTimeout,
},
ExitStatus: cpmtypes.Unknown,
Output: "UNKNOWN",
},
"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,
},
Expand All @@ -65,45 +74,48 @@ 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,
Output: "01234567890123456789012345678901234567890123456789012345678901234567890123456789",
},
"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,
Output: "NON-DEFINED-EXIT-STATUS",
},
"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)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off

echo 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off

echo NON-DEFINED-EXIT-STATUS
exit 100
4 changes: 4 additions & 0 deletions pkg/custompluginmonitor/plugin/test-data/non-ok.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off

echo NonOK
exit 1
4 changes: 4 additions & 0 deletions pkg/custompluginmonitor/plugin/test-data/ok.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off

echo OK
exit 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@echo off

ping 127.0.0.1 -n 3 > nul
echo SLEEP 3S SECOND
exit 0
4 changes: 4 additions & 0 deletions pkg/custompluginmonitor/plugin/test-data/unknown.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@echo off

echo UNKNOWN
exit 3
Loading