Files
coredns/plugin/proxy/upstream.go

211 lines
4.6 KiB
Go
Raw Normal View History

2016-03-18 20:57:35 +00:00
package proxy
import (
"fmt"
"net"
2016-03-18 20:57:35 +00:00
"strconv"
"time"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/dnsutil"
"github.com/coredns/coredns/plugin/pkg/healthcheck"
"github.com/coredns/coredns/plugin/pkg/tls"
"github.com/mholt/caddy/caddyfile"
2016-03-19 16:11:30 +00:00
"github.com/miekg/dns"
2016-03-18 20:57:35 +00:00
)
type staticUpstream struct {
from string
healthcheck.HealthCheck
2016-03-18 20:57:35 +00:00
2016-03-19 16:11:30 +00:00
IgnoredSubDomains []string
ex Exchanger
2016-03-18 20:57:35 +00:00
}
// NewStaticUpstreams parses the configuration input and sets up
// static upstreams for the proxy plugin.
func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) {
2016-03-18 20:57:35 +00:00
var upstreams []Upstream
for c.Next() {
u, err := NewStaticUpstream(c)
if err != nil {
return upstreams, err
2016-03-18 20:57:35 +00:00
}
upstreams = append(upstreams, u)
}
return upstreams, nil
}
2016-03-18 20:57:35 +00:00
// NewStaticUpstream parses the configuration of a single upstream
// starting from the FROM
func NewStaticUpstream(c *caddyfile.Dispenser) (Upstream, error) {
upstream := &staticUpstream{
from: ".",
HealthCheck: healthcheck.HealthCheck{
FailTimeout: 5 * time.Second,
MaxFails: 3,
},
ex: newDNSEx(),
}
if !c.Args(&upstream.from) {
return upstream, c.ArgErr()
}
upstream.from = plugin.Host(upstream.from).Normalize()
to := c.RemainingArgs()
if len(to) == 0 {
return upstream, c.ArgErr()
}
2016-03-18 20:57:35 +00:00
// process the host list, substituting in any nameservers in files
toHosts, err := dnsutil.ParseHostPortOrFile(to...)
if err != nil {
return upstream, err
}
if len(toHosts) > max {
return upstream, fmt.Errorf("more than %d TOs configured: %d", max, len(toHosts))
}
for c.NextBlock() {
if err := parseBlock(c, upstream); err != nil {
return upstream, err
2016-03-18 20:57:35 +00:00
}
}
2016-03-18 20:57:35 +00:00
upstream.Hosts = make([]*healthcheck.UpstreamHost, len(toHosts))
for i, host := range toHosts {
uh := &healthcheck.UpstreamHost{
Name: host,
FailTimeout: upstream.FailTimeout,
CheckDown: checkDownFunc(upstream),
2016-03-18 20:57:35 +00:00
}
upstream.Hosts[i] = uh
2016-03-18 20:57:35 +00:00
}
upstream.Start()
return upstream, nil
2016-03-18 20:57:35 +00:00
}
func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
2016-03-18 20:57:35 +00:00
switch c.Val() {
case "policy":
if !c.NextArg() {
return c.ArgErr()
}
policyCreateFunc, ok := healthcheck.SupportedPolicies[c.Val()]
2016-03-18 20:57:35 +00:00
if !ok {
return c.ArgErr()
}
u.Policy = policyCreateFunc()
case "fail_timeout":
if !c.NextArg() {
return c.ArgErr()
}
dur, err := time.ParseDuration(c.Val())
if err != nil {
return err
}
u.FailTimeout = dur
case "max_fails":
if !c.NextArg() {
return c.ArgErr()
}
n, err := strconv.Atoi(c.Val())
if err != nil {
return err
}
u.MaxFails = int32(n)
case "health_check":
if !c.NextArg() {
return c.ArgErr()
}
var err error
u.HealthCheck.Path, u.HealthCheck.Port, err = net.SplitHostPort(c.Val())
if err != nil {
return err
}
plugin/proxy: decrease health timeouts (#1107) Turn down the timeouts and numbers a bit: FailTimeout 10s -> 5s Future 60s -> 12s TryDuration 60s -> 16s The timeout for decrementing the fails in a host: 10s -> 2s And the biggest change: don't set fails when the error is Timeout(), meaning we loop for a bit and may try the same server again, but we don't mark our upstream as bad, see comments in proxy.go. Testing this with "ANY isc.org" and "MX miek.nl" we see: ~~~ ::1 - [24/Sep/2017:08:06:17 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.001621221s 24/Sep/2017:08:06:17 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:37420->8.8.8.8:53: i/o timeout ::1 - [24/Sep/2017:08:06:17 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 35.957284ms 127.0.0.1 - [24/Sep/2017:08:06:18 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.002051726s 24/Sep/2017:08:06:18 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:54901->8.8.8.8:53: i/o timeout ::1 - [24/Sep/2017:08:06:19 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 56.848416ms 127.0.0.1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 48.118349ms ::1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 1.055172915s ~~~ So the ANY isc.org queries show up twice, because we retry internally - this is I think WAI. The `miek.nl MX` queries are just processed normally as no backend is marked as unreachable. May fix #1035 #486
2017-09-24 20:05:36 +01:00
u.HealthCheck.Interval = 4 * time.Second
2016-03-18 20:57:35 +00:00
if c.NextArg() {
dur, err := time.ParseDuration(c.Val())
if err != nil {
return err
}
u.HealthCheck.Interval = dur
}
case "except":
2016-03-19 16:11:30 +00:00
ignoredDomains := c.RemainingArgs()
if len(ignoredDomains) == 0 {
2016-03-18 20:57:35 +00:00
return c.ArgErr()
}
2016-03-19 16:11:30 +00:00
for i := 0; i < len(ignoredDomains); i++ {
ignoredDomains[i] = plugin.Host(ignoredDomains[i]).Normalize()
2016-03-19 16:11:30 +00:00
}
u.IgnoredSubDomains = ignoredDomains
case "spray":
u.Spray = &healthcheck.Spray{}
case "protocol":
encArgs := c.RemainingArgs()
if len(encArgs) == 0 {
return c.ArgErr()
}
switch encArgs[0] {
case "dns":
if len(encArgs) > 1 {
if encArgs[1] == "force_tcp" {
opts := Options{ForceTCP: true}
u.ex = newDNSExWithOption(opts)
} else {
return fmt.Errorf("only force_tcp allowed as parameter to dns")
}
} else {
u.ex = newDNSEx()
}
case "https_google":
boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
boot = encArgs[2:]
}
u.ex = newGoogle("", boot) // "" for default in google.go
case "grpc":
if len(encArgs) == 2 && encArgs[1] == "insecure" {
u.ex = newGrpcClient(nil, u)
return nil
}
tls, err := tls.NewTLSConfigFromArgs(encArgs[1:]...)
if err != nil {
return err
}
u.ex = newGrpcClient(tls, u)
default:
return fmt.Errorf("%s: %s", errInvalidProtocol, encArgs[0])
}
2016-03-18 20:57:35 +00:00
default:
return c.Errf("unknown property '%s'", c.Val())
}
return nil
}
func (u *staticUpstream) IsAllowedDomain(name string) bool {
2017-02-07 21:03:17 +00:00
if dns.Name(name) == dns.Name(u.From()) {
return true
}
2017-02-07 21:03:17 +00:00
for _, ignoredSubDomain := range u.IgnoredSubDomains {
if plugin.Name(ignoredSubDomain).Matches(name) {
2016-03-19 16:11:30 +00:00
return false
2016-03-18 20:57:35 +00:00
}
2016-03-19 16:11:30 +00:00
}
2016-03-18 20:57:35 +00:00
return true
}
func (u *staticUpstream) Exchanger() Exchanger { return u.ex }
func (u *staticUpstream) From() string { return u.from }
const max = 15