Skip to content

Commit 5cdf376

Browse files
authored
Merge pull request mackerelio#767 from mackerelio/improve-check-tcp
[check-tcp] Supports option to monitor that ports are closed.
2 parents 0f93286 + 06922b7 commit 5cdf376

File tree

3 files changed

+131
-7
lines changed

3 files changed

+131
-7
lines changed

check-tcp/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ command = ["check-tcp", "-H", "localhost", "-p", "4224", "-w", "3", "-c", "5"]
5858
-c, --critical= Response time to result in critical status (seconds)
5959
-E, --escape Can use \n, \r, \t or \ in send or quit string. Must come before send or quit option. By default, nothing added to send, \r\n added to end of quit
6060
-W, --error-warning Set the error level to warning when exiting with unexpected error (default: critical). In the case of request succeeded, evaluation result of -c option eval takes priority.
61+
-C, --expect-closed Verify that the port/unixsock is closed. If the port/unixsock is closed, OK; if open, follow the ErrWarning flag. This option only verifies the connection.
6162
```
6263

6364
## For more information

check-tcp/lib/check-tcp.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ type tcpOpts struct {
1818
Service string `long:"service" description:"Service name. e.g. ftp, smtp, pop, imap and so on"`
1919
Hostname string `short:"H" long:"hostname" description:"Host name or IP Address"`
2020
exchange
21-
Timeout float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
22-
MaxBytes int `short:"m" long:"maxbytes" description:"Close connection once more than this number of bytes are received"`
23-
Delay float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
24-
Warning float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
25-
Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
26-
Escape bool `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
27-
ErrWarning bool `short:"W" long:"error-warning" description:"Set the error level to warning when exiting with unexpected error (default: critical). In the case of request succeeded, evaluation result of -c option eval takes priority."`
21+
Timeout float64 `short:"t" long:"timeout" default:"10" description:"Seconds before connection times out"`
22+
MaxBytes int `short:"m" long:"maxbytes" description:"Close connection once more than this number of bytes are received"`
23+
Delay float64 `short:"d" long:"delay" description:"Seconds to wait between sending string and polling for response"`
24+
Warning float64 `short:"w" long:"warning" description:"Response time to result in warning status (seconds)"`
25+
Critical float64 `short:"c" long:"critical" description:"Response time to result in critical status (seconds)"`
26+
Escape bool `short:"E" long:"escape" description:"Can use \\n, \\r, \\t or \\ in send or quit string. Must come before send or quit option. By default, nothing added to send, \\r\\n added to end of quit"`
27+
ErrWarning bool `short:"W" long:"error-warning" description:"Set the error level to warning when exiting with unexpected error (default: critical). In the case of request succeeded, evaluation result of -c option eval takes priority."`
28+
ExpectClosed bool `short:"C" long:"expect-closed" description:"Verify that the port/unixsock is closed. If the port/unixsock is closed, OK; if open, follow the ErrWarning flag. This option only verifies the connection."`
2829
}
2930

3031
type exchange struct {
@@ -179,13 +180,35 @@ func (opts *tcpOpts) run() *checkers.Checker {
179180

180181
conn, err := dial(proto, addr, opts.SSL, opts.NoCheckCertificate, timeout)
181182
if err != nil {
183+
if opts.ExpectClosed {
184+
var msg string
185+
if opts.UnixSock == "" {
186+
msg = fmt.Sprintf("Verified that the port is closed. (port=%d)", opts.Port)
187+
} else {
188+
msg = fmt.Sprintf("Verified that the unixsock is closed. (sock=%s)", opts.UnixSock)
189+
}
190+
return checkers.Ok(msg)
191+
}
182192
if opts.ErrWarning {
183193
return checkers.Warning(err.Error())
184194
}
185195
return checkers.Critical(err.Error())
186196
}
187197
defer conn.Close()
188198

199+
if opts.ExpectClosed {
200+
var msg string
201+
if opts.UnixSock == "" {
202+
msg = fmt.Sprintf("Unexpectedly open port. (port=%d)", opts.Port)
203+
} else {
204+
msg = fmt.Sprintf("Unexpectedly open unixsock. (sock=%s)", opts.UnixSock)
205+
}
206+
if opts.ErrWarning {
207+
return checkers.Warning(msg)
208+
}
209+
return checkers.Critical(msg)
210+
}
211+
189212
if opts.Send != "" {
190213
err := write(conn, []byte(opts.Send), timeout)
191214
if err != nil {

check-tcp/lib/check-tcp_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,35 @@ func TestHTTP(t *testing.T) {
218218
assert.Regexp(t, `seconds response time on`, ckr.Message, "Unexpected response")
219219
}
220220
testOverCritWithErrWarn()
221+
222+
errMsgWithExpectClosed := fmt.Sprintf("Unexpectedly open port. (port=%s)", port)
223+
224+
testOkIfPortClosed := func() {
225+
opts, err := parseArgs([]string{"-H", host, "-p", "1", "--expect-closed"})
226+
assert.Equal(t, nil, err, "no errors")
227+
ckr := opts.run()
228+
fmt.Println(ckr)
229+
assert.Equal(t, checkers.OK, ckr.Status, "Verified that the port is closed. (port=1)")
230+
}
231+
testOkIfPortClosed()
232+
233+
testCriticalIfPortOpened := func() {
234+
opts, err := parseArgs([]string{"-H", host, "-p", port, "--expect-closed"})
235+
assert.Equal(t, nil, err, "no errors")
236+
ckr := opts.run()
237+
fmt.Println(ckr)
238+
assert.Equal(t, checkers.CRITICAL, ckr.Status, errMsgWithExpectClosed)
239+
}
240+
testCriticalIfPortOpened()
241+
242+
testWarningIfPortOpened := func() {
243+
opts, err := parseArgs([]string{"-H", host, "-p", port, "--expect-closed", "--error-warning"})
244+
assert.Equal(t, nil, err, "no errors")
245+
ckr := opts.run()
246+
fmt.Println(ckr)
247+
assert.Equal(t, checkers.WARNING, ckr.Status, errMsgWithExpectClosed)
248+
}
249+
testWarningIfPortOpened()
221250
}
222251

223252
func TestUnixDomainSocket(t *testing.T) {
@@ -290,6 +319,35 @@ func TestUnixDomainSocket(t *testing.T) {
290319
assert.Regexp(t, `seconds response time on`, ckr.Message, "Unexpected response")
291320
}
292321
testOverCrit()
322+
323+
errMsgWithExpectClosed := fmt.Sprintf("Unexpectedly open unixsock. (sock=%s)", sock)
324+
325+
testOkIfUnixSocketClosed := func() {
326+
opts, err := parseArgs([]string{"-U", "/foo/bar.sock", "--send", `PING`, "-E", "-e", "OKOK", "--expect-closed"})
327+
assert.Equal(t, nil, err, "no errors")
328+
ckr := opts.run()
329+
fmt.Println(ckr)
330+
assert.Equal(t, checkers.OK, ckr.Status, "Verified that the unixsock is closed. (sock=/foo/bar.sock)")
331+
}
332+
testOkIfUnixSocketClosed()
333+
334+
testCriticalIfUnixSocketOpened := func() {
335+
opts, err := parseArgs([]string{"-U", sock, "--send", `PING`, "-E", "-e", "OKOK", "--expect-closed"})
336+
assert.Equal(t, nil, err, "no errors")
337+
ckr := opts.run()
338+
fmt.Println(ckr)
339+
assert.Equal(t, checkers.CRITICAL, ckr.Status, errMsgWithExpectClosed)
340+
}
341+
testCriticalIfUnixSocketOpened()
342+
343+
testWarningIfUnixSocketOpened := func() {
344+
opts, err := parseArgs([]string{"-U", sock, "--send", `PING`, "-E", "-e", "OKOK", "--expect-closed", "--error-warning"})
345+
assert.Equal(t, nil, err, "no errors")
346+
ckr := opts.run()
347+
fmt.Println(ckr)
348+
assert.Equal(t, checkers.WARNING, ckr.Status, errMsgWithExpectClosed)
349+
}
350+
testWarningIfUnixSocketOpened()
293351
}
294352

295353
func TestHTTPIPv6(t *testing.T) {
@@ -356,3 +414,45 @@ func TestHTTPIPv6(t *testing.T) {
356414
}
357415
testOverCrit()
358416
}
417+
418+
func TestExpectClosedWithPort(t *testing.T) {
419+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
420+
time.Sleep(time.Second / 5)
421+
w.WriteHeader(200)
422+
w.Header().Set("Content-Type", "text/plain")
423+
fmt.Fprint(w, "OKOK")
424+
}))
425+
defer ts.Close()
426+
427+
u, _ := url.Parse(ts.URL)
428+
host, port, _ := net.SplitHostPort(u.Host)
429+
430+
errMsg := fmt.Sprintf("Unexpectedly open port. (port=%s)", port)
431+
432+
testOkIfPortClosed := func() {
433+
opts, err := parseArgs([]string{"-H", host, "-p", "1", "--expect-closed"})
434+
assert.Equal(t, nil, err, "no errors")
435+
ckr := opts.run()
436+
fmt.Println(ckr)
437+
assert.Equal(t, checkers.OK, ckr.Status, "Verified that the port(1) is closed.")
438+
}
439+
testOkIfPortClosed()
440+
441+
testCriticalIfPortOpened := func() {
442+
opts, err := parseArgs([]string{"-H", host, "-p", port, "--expect-closed"})
443+
assert.Equal(t, nil, err, "no errors")
444+
ckr := opts.run()
445+
fmt.Println(ckr)
446+
assert.Equal(t, checkers.CRITICAL, ckr.Status, errMsg)
447+
}
448+
testCriticalIfPortOpened()
449+
450+
testWarningIfPortOpened := func() {
451+
opts, err := parseArgs([]string{"-H", host, "-p", port, "--expect-closed", "--error-warning"})
452+
assert.Equal(t, nil, err, "no errors")
453+
ckr := opts.run()
454+
fmt.Println(ckr)
455+
assert.Equal(t, checkers.WARNING, ckr.Status, errMsg)
456+
}
457+
testWarningIfPortOpened()
458+
}

0 commit comments

Comments
 (0)