Skip to content
Open
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 Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ COPY --from=builder /src/bin/node-problem-detector /node-problem-detector

ARG LOGCOUNTER
COPY --from=builder /src/bin/health-checker /src/${LOGCOUNTER} /home/kubernetes/bin/
COPY --from=builder /src/config/plugin/ /home/kubernetes/bin/plugin/

COPY --from=builder /src/config/ /config
ENTRYPOINT ["/node-problem-detector", "--config.system-log-monitor=/config/kernel-monitor.json,/config/readonly-monitor.json"]
4 changes: 2 additions & 2 deletions config/custom-plugin-monitor.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
{
"type": "temporary",
"reason": "NTPIsDown",
"path": "./config/plugin/check_ntp.sh",
"path": "/home/kubernetes/bin/plugin/check_ntp.sh",
"timeout": "3s"
},
{
"type": "permanent",
"condition": "NTPProblem",
"reason": "NTPIsDown",
"path": "./config/plugin/check_ntp.sh",
"path": "/home/kubernetes/bin/plugin/check_ntp.sh",
"timeout": "3s"
}
]
Expand Down
2 changes: 1 addition & 1 deletion config/iptables-mode-monitor.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{
"type": "temporary",
"reason": "IPTablesVersionsMismatch",
"path": "./config/plugin/iptables_mode.sh",
"path": "/home/kubernetes/bin/plugin/iptables_mode.sh",
"timeout": "5s"
}
]
Expand Down
4 changes: 2 additions & 2 deletions config/network-problem-monitor.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
{
"type": "temporary",
"reason": "ConntrackFull",
"path": "./config/plugin/network_problem.sh",
"path": "/home/kubernetes/bin/plugin/network_problem.sh",
"timeout": "3s"
},
{
"type": "temporary",
"reason": "DNSUnreachable",
"path": "./config/plugin/dns_problem.sh",
"path": "/home/kubernetes/bin/plugin/dns_problem.sh",
"timeout": "3s"
}
]
Expand Down
4 changes: 2 additions & 2 deletions docs/custom_plugin_monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
// to show unless there's a problem.
"reason": "NTPIsDown", // This is the reason shown for this event
// and the message shown comes from stdout.
"path": "./config/plugin/check_ntp.sh",
"path": "/home/kubernetes/bin/plugin/check_ntp.sh",
"timeout": "3s"
},
{
Expand All @@ -54,7 +54,7 @@
"condition": "NTPProblem", // This is the key to connect to the corresponding condition listed above
"reason": "NTPIsDown", // and the reason shown for failures detected in this rule
// and message will be from stdout of the check.
"path": "./config/plugin/check_ntp.sh",
"path": "/home/kubernetes/bin/plugin/check_ntp.sh",
"timeout": "3s"
}
]
Expand Down
38 changes: 38 additions & 0 deletions pkg/custompluginmonitor/types/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ limitations under the License.
package types

import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -374,3 +378,37 @@ func TestCustomPluginConfigValidate(t *testing.T) {
}
}
}

func TestShippedCustomPluginConfigsUseStablePluginPaths(t *testing.T) {
testCases := []string{
"../../../config/custom-plugin-monitor.json",
"../../../config/network-problem-monitor.json",
"../../../config/iptables-mode-monitor.json",
}

for _, configPath := range testCases {
t.Run(filepath.Base(configPath), func(t *testing.T) {
data, err := os.ReadFile(configPath)
if err != nil {
t.Fatalf("failed to read config %q: %v", configPath, err)
}

var cfg CustomPluginConfig
if err := json.Unmarshal(data, &cfg); err != nil {
t.Fatalf("failed to unmarshal config %q: %v", configPath, err)
}

for _, rule := range cfg.Rules {
if !filepath.IsAbs(rule.Path) {
t.Fatalf("rule path %q in %q must be absolute", rule.Path, configPath)
}
if strings.HasPrefix(rule.Path, "/config/") {
t.Fatalf("rule path %q in %q must not depend on the /config mount", rule.Path, configPath)
}
if !strings.HasPrefix(rule.Path, "/home/kubernetes/bin/plugin/") {
t.Fatalf("rule path %q in %q must use the stable plugin directory", rule.Path, configPath)
}
}
})
}
}
85 changes: 47 additions & 38 deletions pkg/systemlogmonitor/logwatchers/filelog/log_watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package filelog

import (
"fmt"
"os"
"testing"
"time"
Expand Down Expand Up @@ -137,47 +138,55 @@ Jan 2 03:04:05 kernel: [2.000000] 3
},
}
for c, test := range testCases {
t.Logf("TestCase #%d: %#v", c+1, test)
f, err := os.CreateTemp("", "log_watcher_test")
assert.NoError(t, err)
defer func() {
if err := f.Close(); err != nil {
t.Logf("failed to close temporary file %s: %v", f.Name(), err)
}
if err := os.Remove(f.Name()); err != nil {
t.Logf("failed to remove temporary file %s: %v", f.Name(), err)
}
}()
_, err = f.WriteString(test.log)
assert.NoError(t, err)
t.Run(fmt.Sprintf("case-%d", c+1), func(t *testing.T) {
t.Logf("TestCase #%d: %#v", c+1, test)
f, err := os.CreateTemp("", "log_watcher_test")
assert.NoError(t, err)
defer func() {
if err := f.Close(); err != nil {
t.Logf("failed to close temporary file %s: %v", f.Name(), err)
}
if err := os.Remove(f.Name()); err != nil {
t.Logf("failed to remove temporary file %s: %v", f.Name(), err)
}
}()
_, err = f.WriteString(test.log)
assert.NoError(t, err)

w := NewSyslogWatcherOrDie(types.WatcherConfig{
Plugin: "filelog",
PluginConfig: getTestPluginConfig(),
LogPath: f.Name(),
Lookback: test.lookback,
})
// Set the startTime.
w.(*filelogWatcher).startTime, _ = util.GetStartTime(fakeClock.Now(), test.uptime, test.lookback, test.delay)
logCh, err := w.Watch()
assert.NoError(t, err)
defer w.Stop()
for _, expected := range test.logs {
w := NewSyslogWatcherOrDie(types.WatcherConfig{
Plugin: "filelog",
PluginConfig: getTestPluginConfig(),
LogPath: f.Name(),
Lookback: test.lookback,
})
// Set the startTime.
w.(*filelogWatcher).startTime, _ = util.GetStartTime(fakeClock.Now(), test.uptime, test.lookback, test.delay)
logCh, err := w.Watch()
assert.NoError(t, err)
defer w.Stop()

for _, expected := range test.logs {
select {
case got, ok := <-logCh:
if !ok {
t.Skip("filelog watcher closed before emitting logs; inotify resources may be exhausted on the host")
}
assert.Equal(t, &expected, got)
case <-time.After(30 * time.Second):
t.Errorf("timeout waiting for log")
}
}
// The log channel should have already been drained
// There could still be future messages sent into the channel, but the chance is really slim.
timeout := time.After(100 * time.Millisecond)
select {
case got := <-logCh:
assert.Equal(t, &expected, got)
case <-time.After(30 * time.Second):
t.Errorf("timeout waiting for log")
case log, ok := <-logCh:
if ok && log != nil {
t.Errorf("unexpected extra log: %+v", *log)
}
case <-timeout:
}
}
// The log channel should have already been drained
// There could still be future messages sent into the channel, but the chance is really slim.
timeout := time.After(100 * time.Millisecond)
select {
case log := <-logCh:
t.Errorf("unexpected extra log: %+v", *log)
case <-timeout:
}
})
}
}

Expand Down