checkers/reachable.go
package checkers
import (
"net"
"net/url"
"time"
)
const (
// ReachableDDHealthErrors is the datadog name used when there is a failure in the reachable checker
ReachableDDHealthErrors = "health.errors"
// ReachableDefaultPort is the default port used if no port is defined in a reachable checker
ReachableDefaultPort = "80"
// ReachableDefaultNetwork is the default network used in the reachable checker
ReachableDefaultNetwork = "tcp"
)
var (
// ReachableDefaultTimeout is the default timeout used when reachable is checking the URL
ReachableDefaultTimeout = time.Duration(3) * time.Second
)
// ReachableDialer is the signature for a function that checks if an address is reachable
type ReachableDialer func(network, address string, timeout time.Duration) (net.Conn, error)
// ReachableDatadogIncrementer is any datadog client that has the Incr method for tracking metrics
type ReachableDatadogIncrementer interface {
Incr(name string, tags []string, rate float64) error
}
// ReachableConfig is used for configuring an HTTP check. The only required field is `URL`.
//
// "Dialer" is optional and defaults to using net.DialTimeout.
//
// "Timeout" is optional and defaults to "3s".
//
// "Network" is optional and defaults to "tcp"; it should be one of "tcp",
// "tcp4", "tcp6", "unix", "unixpacket", "udp", "udp4", "udp6", "unixgram" or an
// IP transport. The IP transports are "ip", "ip4", or "ip6" followed by a colon
// and a literal protocol number or a protocol name, as in "ip:1" or "ip:icmp".
//
// "DatadogClient" is optional; if defined metrics will be sent via statsd.
//
// "DatadogTags" is optional; defines the tags that are passed to datadog when there is a failure
type ReachableConfig struct {
URL *url.URL // Required
Dialer ReachableDialer // Optional (default net.DialTimeout)
Timeout time.Duration // Optional (default 3s)
Network string // Optional (default tcp)
DatadogClient ReachableDatadogIncrementer // Optional
DatadogTags []string // Optional
}
// ReachableChecker checks that URL responds to a TCP request
type ReachableChecker struct {
dialer ReachableDialer
timeout time.Duration
network string
url *url.URL
datadog ReachableDatadogIncrementer
tags []string
}
// NewReachableChecker creates a new reachable health checker
func NewReachableChecker(cfg *ReachableConfig) (*ReachableChecker, error) {
t := ReachableDefaultTimeout
if cfg.Timeout != 0 {
t = cfg.Timeout
}
d := net.DialTimeout
if cfg.Dialer != nil {
d = cfg.Dialer
}
n := ReachableDefaultNetwork
if cfg.Network != "" {
n = cfg.Network
}
r := &ReachableChecker{
dialer: d,
timeout: t,
network: n,
url: cfg.URL,
datadog: cfg.DatadogClient,
tags: cfg.DatadogTags,
}
return r, nil
}
// Status checks if the endpoint is reachable
func (r *ReachableChecker) Status() (interface{}, error) {
// We must provide a port so when a port is not set in the URL provided use
// the default port (80)
port := r.url.Port()
if len(port) == 0 {
port = ReachableDefaultPort
}
conn, err := r.dialer(r.network, r.url.Hostname()+":"+port, r.timeout)
if err != nil {
return r.fail(err)
}
if conn != nil {
if errClose := conn.Close(); errClose != nil {
return r.fail(errClose)
}
}
return nil, nil
}
func (r *ReachableChecker) fail(err error) (interface{}, error) {
if r.datadog != nil {
r.datadog.Incr(ReachableDDHealthErrors, r.tags, 1.0)
}
return nil, err
}