Skip to content

Commit a7f78c5

Browse files
committed
Enable NPD to run as a Windows Service.
1 parent 1e8008b commit a7f78c5

34 files changed

+2877
-17
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
ci.env
55
pr.env
66
junit*.xml
7+
debug.test

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ make test
229229
230230
# Run with containerd log monitoring enabled. (Assumes containerd is installed.)
231231
%CD%\bin\windows_amd64\node-problem-detector.exe --logtostderr --enable-k8s-exporter=false --config.system-log-monitor=%CD%\config\windows-containerd-monitor-filelog.json
232+
233+
# Configure NPD to run as a Windows Service
234+
sc.exe create NodeProblemDetector binpath= "%CD%\node-problem-detector.exe [FLAGS]" start= demand
235+
sc.exe failure NodeProblemDetector reset= 0 actions= restart/10000
236+
sc.exe start NodeProblemDetector
232237
```
233238

234239
## Try It Out

cmd/nodeproblemdetector/node_problem_detector.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ limitations under the License.
1717
package main
1818

1919
import (
20-
"os"
21-
2220
"github.com/golang/glog"
23-
"github.com/spf13/pflag"
2421

2522
_ "k8s.io/node-problem-detector/cmd/nodeproblemdetector/exporterplugins"
2623
_ "k8s.io/node-problem-detector/cmd/nodeproblemdetector/problemdaemonplugins"
@@ -34,15 +31,19 @@ import (
3431
"k8s.io/node-problem-detector/pkg/version"
3532
)
3633

37-
func main() {
38-
npdo := options.NewNodeProblemDetectorOptions()
39-
npdo.AddFlags(pflag.CommandLine)
34+
func npdInteractive(npdo *options.NodeProblemDetectorOptions) {
35+
termCh := make(chan error, 1)
36+
defer close(termCh)
4037

41-
pflag.Parse()
38+
if err := npdMain(npdo, termCh); err != nil {
39+
glog.Fatalf("Problem detector failed with error: %v", err)
40+
}
41+
}
4242

43+
func npdMain(npdo *options.NodeProblemDetectorOptions, termCh <-chan error) error {
4344
if npdo.PrintVersion {
4445
version.PrintVersion()
45-
os.Exit(0)
46+
return nil
4647
}
4748

4849
npdo.SetNodeNameOrDie()
@@ -78,7 +79,5 @@ func main() {
7879

7980
// Initialize NPD core.
8081
p := problemdetector.NewProblemDetector(problemDaemons, npdExporters)
81-
if err := p.Run(); err != nil {
82-
glog.Fatalf("Problem detector failed with error: %v", err)
83-
}
82+
return p.Run(termCh)
8483
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"github.com/spf13/pflag"
21+
"k8s.io/node-problem-detector/cmd/options"
22+
)
23+
24+
func main() {
25+
npdo := options.NewNodeProblemDetectorOptions()
26+
npdo.AddFlags(pflag.CommandLine)
27+
28+
pflag.Parse()
29+
npdInteractive(npdo)
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"errors"
21+
"fmt"
22+
"io/ioutil"
23+
"os"
24+
"strings"
25+
"testing"
26+
27+
_ "k8s.io/node-problem-detector/cmd/nodeproblemdetector/exporterplugins"
28+
_ "k8s.io/node-problem-detector/cmd/nodeproblemdetector/problemdaemonplugins"
29+
"k8s.io/node-problem-detector/cmd/options"
30+
"k8s.io/node-problem-detector/pkg/exporters"
31+
"k8s.io/node-problem-detector/pkg/types"
32+
)
33+
34+
const (
35+
fakeConfigFilePattern = `
36+
{
37+
"plugin": "filelog",
38+
"pluginConfig": {
39+
"timestamp": "^time=\"(\\S*)\"",
40+
"message": "msg=\"([^\n]*)\"",
41+
"timestampFormat": "2006-01-02T15:04:05.999999999-07:00"
42+
},
43+
"logPath": "%s",
44+
"lookback": "5m",
45+
"bufferSize": 10,
46+
"source": "containerd",
47+
"conditions": [],
48+
"rules": [
49+
{
50+
"type": "temporary",
51+
"reason": "MissingPigz",
52+
"pattern": "unpigz not found.*"
53+
},
54+
{
55+
"type": "temporary",
56+
"reason": "IncompatibleContainer",
57+
"pattern": ".*CreateComputeSystem.*"
58+
}
59+
]
60+
}
61+
`
62+
)
63+
64+
func init() {
65+
exporters.Register("nil", types.ExporterHandler{
66+
CreateExporterOrDie: func(types.CommandLineOptions) types.Exporter {
67+
return &nullExporter{}
68+
},
69+
})
70+
}
71+
72+
type nullExporter struct {
73+
}
74+
75+
func (ne *nullExporter) ExportProblems(*types.Status) {
76+
}
77+
78+
func TestNPDMain(t *testing.T) {
79+
npdo, cleanup := setupNPD(t)
80+
defer cleanup()
81+
82+
termCh := make(chan error, 2)
83+
termCh <- errors.New("close")
84+
defer close(termCh)
85+
86+
if err := npdMain(npdo, termCh); err != nil {
87+
t.Errorf("termination signal should not return error got, %v", err)
88+
}
89+
}
90+
91+
func writeTempFile(t *testing.T, ext string, contents string) (string, error) {
92+
f, err := ioutil.TempFile("", "*."+ext)
93+
if err != nil {
94+
return "", fmt.Errorf("cannot create temp file, %v", err)
95+
}
96+
97+
fileName := f.Name()
98+
99+
if err := ioutil.WriteFile(fileName, []byte(contents), 0644); err != nil {
100+
os.Remove(fileName)
101+
return "", fmt.Errorf("cannot write config to temp file %s, %v", fileName, err)
102+
}
103+
104+
return fileName, nil
105+
}
106+
107+
func setupNPD(t *testing.T) (*options.NodeProblemDetectorOptions, func()) {
108+
fakeLogFileName, err := writeTempFile(t, "log", "")
109+
if err != nil {
110+
os.Remove(fakeLogFileName)
111+
t.Fatalf("cannot create temp config file, %v", err)
112+
}
113+
114+
fakeConfigFileContents := fmt.Sprintf(fakeConfigFilePattern, strings.ReplaceAll(fakeLogFileName, "\\", "\\\\"))
115+
116+
fakeConfigFileName, err := writeTempFile(t, "json", fakeConfigFileContents)
117+
if err != nil {
118+
os.Remove(fakeLogFileName)
119+
os.Remove(fakeConfigFileName)
120+
t.Fatalf("cannot create temp config file, %v", err)
121+
}
122+
123+
return &options.NodeProblemDetectorOptions{
124+
MonitorConfigPaths: map[types.ProblemDaemonType]*[]string{
125+
"system-log-monitor": {
126+
fakeConfigFileName,
127+
},
128+
},
129+
}, func() {
130+
os.Remove(fakeLogFileName)
131+
os.Remove(fakeConfigFileName)
132+
}
133+
}

0 commit comments

Comments
 (0)