mirror of
https://github.com/kubernetes/node-problem-detector.git
synced 2026-02-26 15:54:02 +00:00
Merge pull request #517 from jeremyje/fixwin
Windows Support: Fix Build Regressions, Tests Pass
This commit is contained in:
@@ -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
Makefile
26
Makefile
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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) \
|
||||
@@ -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) .
|
||||
|
||||
@@ -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,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")
|
||||
|
||||
@@ -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
|
||||
4
pkg/custompluginmonitor/plugin/test-data/non-ok.cmd
Normal file
4
pkg/custompluginmonitor/plugin/test-data/non-ok.cmd
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
|
||||
echo NonOK
|
||||
exit 1
|
||||
4
pkg/custompluginmonitor/plugin/test-data/ok.cmd
Normal file
4
pkg/custompluginmonitor/plugin/test-data/ok.cmd
Normal file
@@ -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
|
||||
4
pkg/custompluginmonitor/plugin/test-data/unknown.cmd
Normal file
4
pkg/custompluginmonitor/plugin/test-data/unknown.cmd
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
|
||||
echo UNKNOWN
|
||||
exit 3
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
"k8s.io/node-problem-detector/pkg/types"
|
||||
problemutil "k8s.io/node-problem-detector/pkg/util"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
)
|
||||
|
||||
@@ -53,33 +53,43 @@ func newTestCondition(condition string) types.Condition {
|
||||
func TestNeedUpdates(t *testing.T) {
|
||||
m, _, _ := newTestManager()
|
||||
var c types.Condition
|
||||
for desc, test := range map[string]struct {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
condition string
|
||||
update bool
|
||||
}{
|
||||
"Init condition needs update": {
|
||||
{
|
||||
name: "Init condition needs update",
|
||||
condition: "TestCondition",
|
||||
update: true,
|
||||
},
|
||||
"Same condition doesn't need update": {
|
||||
{
|
||||
name: "Same condition doesn't need update",
|
||||
// not set condition, the test will reuse the condition in last case.
|
||||
update: false,
|
||||
},
|
||||
"Same condition with different timestamp need update": {
|
||||
{
|
||||
name: "Same condition with different timestamp need update",
|
||||
condition: "TestCondition",
|
||||
update: true,
|
||||
},
|
||||
"New condition needs update": {
|
||||
{
|
||||
name: "New condition needs update",
|
||||
condition: "TestConditionNew",
|
||||
update: true,
|
||||
},
|
||||
} {
|
||||
if test.condition != "" {
|
||||
c = newTestCondition(test.condition)
|
||||
tc := testCase
|
||||
t.Log(tc.name)
|
||||
if tc.condition != "" {
|
||||
// Guarantee that the time advances before creating a new condition.
|
||||
for now := time.Now(); now == time.Now(); {
|
||||
}
|
||||
c = newTestCondition(tc.condition)
|
||||
}
|
||||
m.UpdateCondition(c)
|
||||
assert.Equal(t, test.update, m.needUpdates(), desc)
|
||||
assert.Equal(t, c, m.conditions[c.Type], desc)
|
||||
assert.Equal(t, tc.update, m.needUpdates(), tc.name)
|
||||
assert.Equal(t, c, m.conditions[c.Type], tc.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
pkg/util/exec_linux.go
Normal file
42
pkg/util/exec_linux.go
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Exec creates a new process with the specified arguments.
|
||||
func Exec(name string, arg ...string) *exec.Cmd {
|
||||
// create a process group
|
||||
sysProcAttr := &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.SysProcAttr = sysProcAttr
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Kill the process and subprocesses.
|
||||
func Kill(cmd *exec.Cmd) error {
|
||||
if cmd.Process == nil {
|
||||
return fmt.Errorf("%v does not have a process handle", cmd)
|
||||
}
|
||||
return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
62
pkg/util/exec_test.go
Normal file
62
pkg/util/exec_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExec(t *testing.T) {
|
||||
var cmds [][]string
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
cmds = [][]string{
|
||||
{"powershell.exe"},
|
||||
{"cmd.exe", "/C", "echo", "Hello"},
|
||||
{"cmd.exe", "/K", "echo", "Wait", "forever"},
|
||||
{"testdata/hello-world.cmd"},
|
||||
{"testdata/hello-world.bat"},
|
||||
{"testdata/hello-world.ps1"},
|
||||
}
|
||||
} else {
|
||||
cmds = [][]string{
|
||||
{"/bin/sh"},
|
||||
{"/bin/bash"},
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range cmds {
|
||||
args := v
|
||||
t.Run(fmt.Sprintf("%v", args), func(t *testing.T) {
|
||||
cmd := Exec(args[0], args[1:]...)
|
||||
|
||||
if err := Kill(cmd); err == nil {
|
||||
t.Error("Kill(cmd) expected to have error because of empty handle, got none")
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Errorf("Start() got error, %v", err)
|
||||
}
|
||||
|
||||
if err := Kill(cmd); err != nil {
|
||||
t.Errorf("Kill(cmd) for %s %v got error, %v", cmd.Path, cmd.Args, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
81
pkg/util/exec_windows.go
Normal file
81
pkg/util/exec_windows.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Exec creates a new process with the specified arguments.
|
||||
func Exec(name string, arg ...string) *exec.Cmd {
|
||||
// Windows does not handle relative path names in exec very well.
|
||||
name = filepath.Clean(name)
|
||||
cmdArgs := arg
|
||||
|
||||
// Detect scripts via file extension and automatically invoke them within a shell.
|
||||
// This mirrors the Linux behavior if the execute bit on file where a shell context
|
||||
// is automatically created.
|
||||
switch strings.ToLower(filepath.Ext(name)) {
|
||||
// Batch Scripts
|
||||
case ".cmd", ".bat":
|
||||
cmdArgs = append([]string{"/C", name}, cmdArgs...)
|
||||
name = "cmd.exe"
|
||||
// Powershell Scripts
|
||||
case ".ps1":
|
||||
cmdArgs = append([]string{"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "RemoteSigned", name}, cmdArgs...)
|
||||
name = "powershell.exe"
|
||||
default:
|
||||
// Run directly.
|
||||
}
|
||||
|
||||
return exec.Command(name, cmdArgs...)
|
||||
}
|
||||
|
||||
// ExitStatus returns the exit code of the application.
|
||||
func ExitStatus(cmd *exec.Cmd) int {
|
||||
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
|
||||
// Kill the process and subprocesses.
|
||||
func Kill(cmd *exec.Cmd) error {
|
||||
if cmd.Process == nil {
|
||||
return fmt.Errorf("%v does not have a process handle", cmd)
|
||||
}
|
||||
|
||||
// Use taskkill to kill the child process by process id.
|
||||
// /F = Force
|
||||
// /T = Kill child processes.
|
||||
// https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/taskkill
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
|
||||
kill.Stderr = os.Stderr
|
||||
kill.Stdout = os.Stdout
|
||||
err := kill.Run()
|
||||
if execErr, ok := err.(*exec.ExitError); ok {
|
||||
// Error code 128 (ERROR_WAIT_NO_CHILDREN) means that taskkill couldn't find the process, it probably died already.
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
|
||||
if execErr.ExitCode() == 128 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func ParsePrometheusMetrics(metricsText string) ([]Float64MetricRepresentation,
|
||||
var metrics []Float64MetricRepresentation
|
||||
|
||||
var textParser expfmt.TextParser
|
||||
metricsText = strings.ReplaceAll(metricsText, "\r", "")
|
||||
metricFamilies, err := textParser.TextToMetricFamilies(strings.NewReader(metricsText))
|
||||
if err != nil {
|
||||
return metrics, err
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestPrometheusMetricsParsingAndMatching(t *testing.T) {
|
||||
Name: "host_uptime",
|
||||
Labels: map[string]string{"kernel_version": "mismatched-version"},
|
||||
},
|
||||
// Non-exsistant metric.
|
||||
// Non-existant metric.
|
||||
{
|
||||
Name: "host_downtime",
|
||||
Labels: map[string]string{},
|
||||
@@ -109,7 +109,7 @@ func TestPrometheusMetricsParsingAndMatching(t *testing.T) {
|
||||
Name: "host_uptime",
|
||||
Labels: map[string]string{"kernel_version": "mismatched-version"},
|
||||
},
|
||||
// Non-exsistant metric.
|
||||
// Non-existant metric.
|
||||
{
|
||||
Name: "host_downtime",
|
||||
Labels: map[string]string{},
|
||||
|
||||
2
pkg/util/metrics/testdata/hello-world.bat
vendored
Normal file
2
pkg/util/metrics/testdata/hello-world.bat
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
echo Hello World
|
||||
2
pkg/util/metrics/testdata/hello-world.cmd
vendored
Normal file
2
pkg/util/metrics/testdata/hello-world.cmd
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
echo Hello World
|
||||
1
pkg/util/metrics/testdata/hello-world.ps1
vendored
Normal file
1
pkg/util/metrics/testdata/hello-world.ps1
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Write-Host "Hello World"
|
||||
Reference in New Issue
Block a user