mirror of
https://github.com/coredns/coredns.git
synced 2025-11-02 02:03:13 -05:00
reload: use OnRestart (#1709)
* reload: use OnRestart
Close the listener on OnRestart for health and metrics so the default
setup function can setup the listener when the plugin is "starting up".
Lightly test with some SIGUSR1-ing. Also checked the reload plugin with
this, seems fine:
.com.:1043
.:1043
2018/04/20 15:01:25 [INFO] CoreDNS-1.1.1
2018/04/20 15:01:25 [INFO] linux/amd64, go1.10,
CoreDNS-1.1.1
linux/amd64, go1.10,
2018/04/20 15:01:25 [INFO] Running configuration MD5 = aa8b3f03946fb60546ca1f725d482714
2018/04/20 15:02:01 [INFO] Reloading
2018/04/20 15:02:01 [INFO] Running configuration MD5 = b34a96d99e01db4015a892212560155f
2018/04/20 15:02:01 [INFO] Reloading complete
^C2018/04/20 15:02:06 [INFO] SIGINT: Shutting down
With this corefile:
.com {
proxy . 127.0.0.1:53
prometheus :9054
whoami
reload
}
. {
proxy . 127.0.0.1:53
prometheus :9054
whoami
reload
}
The prometheus port was 9053, changed that to 54 so reload would pick it
up.
From a cursory look it seems this also fixes:
Fixes #1604 #1618 #1686 #1492
* At least make it test
* Use onfinalshutdown
* reload: add reload test
This test #1604 adn right now fails.
* Address review comments
* Add bug section explaining things a bit
* compile tests
* Fix tests
* fixes
* slightly less crazy
* try to make prometheus setup less confusing
* Use ephermal port for test
* Don't use the listener
* These are shared between goroutines, just use the boolean in the main
structure.
* Fix text in the reload README,
* Set addr to TODO once stopping it
* Morph fturb's comment into test, to test reload and scrape health and
metric endpoint
This commit is contained in:
52
plugin/metrics/addr.go
Normal file
52
plugin/metrics/addr.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package metrics
|
||||
|
||||
// addrs keeps track on which addrs we listen, so we only start one listener, is
|
||||
// prometheus is used in multiple Server Blocks.
|
||||
type addrs struct {
|
||||
a map[string]value
|
||||
}
|
||||
|
||||
type value struct {
|
||||
state int
|
||||
f func() error
|
||||
}
|
||||
|
||||
var uniqAddr addrs
|
||||
|
||||
func newAddress() addrs {
|
||||
return addrs{a: make(map[string]value)}
|
||||
}
|
||||
|
||||
func (a addrs) setAddress(addr string, f func() error) {
|
||||
if a.a[addr].state == done {
|
||||
return
|
||||
}
|
||||
a.a[addr] = value{todo, f}
|
||||
}
|
||||
|
||||
// setAddressTodo sets addr to 'todo' again.
|
||||
func (a addrs) setAddressTodo(addr string) {
|
||||
v, ok := a.a[addr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
v.state = todo
|
||||
a.a[addr] = v
|
||||
}
|
||||
|
||||
// forEachTodo iterates for a and executes f for each element that is 'todo' and sets it to 'done'.
|
||||
func (a addrs) forEachTodo() error {
|
||||
for k, v := range a.a {
|
||||
if v.state == todo {
|
||||
v.f()
|
||||
}
|
||||
v.state = done
|
||||
a.a[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
todo = 1
|
||||
done = 2
|
||||
)
|
||||
17
plugin/metrics/addr_test.go
Normal file
17
plugin/metrics/addr_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package metrics
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestForEachTodo(t *testing.T) {
|
||||
a, i := newAddress(), 0
|
||||
a.setAddress("test", func() error { i++; return nil })
|
||||
|
||||
a.forEachTodo()
|
||||
if i != 1 {
|
||||
t.Errorf("Failed to executed f for %s", "test")
|
||||
}
|
||||
a.forEachTodo()
|
||||
if i != 1 {
|
||||
t.Errorf("Executed f twice instead of once")
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,12 @@ import (
|
||||
|
||||
// Metrics holds the prometheus configuration. The metrics' path is fixed to be /metrics
|
||||
type Metrics struct {
|
||||
Next plugin.Handler
|
||||
Addr string
|
||||
Reg *prometheus.Registry
|
||||
ln net.Listener
|
||||
mux *http.ServeMux
|
||||
Next plugin.Handler
|
||||
Addr string
|
||||
Reg *prometheus.Registry
|
||||
ln net.Listener
|
||||
lnSetup bool
|
||||
mux *http.ServeMux
|
||||
|
||||
zoneNames []string
|
||||
zoneMap map[string]bool
|
||||
@@ -93,7 +94,8 @@ func (m *Metrics) OnStartup() error {
|
||||
}
|
||||
|
||||
m.ln = ln
|
||||
ListenAddr = m.ln.Addr().String()
|
||||
m.lnSetup = true
|
||||
ListenAddr = m.ln.Addr().String() // For tests
|
||||
|
||||
m.mux = http.NewServeMux()
|
||||
m.mux.Handle("/metrics", promhttp.HandlerFor(m.Reg, promhttp.HandlerOpts{}))
|
||||
@@ -104,14 +106,29 @@ func (m *Metrics) OnStartup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnShutdown tears down the metrics listener on shutdown and restart.
|
||||
func (m *Metrics) OnShutdown() error {
|
||||
// OnRestart stops the listener on reload.
|
||||
func (m *Metrics) OnRestart() error {
|
||||
if !m.lnSetup {
|
||||
return nil
|
||||
}
|
||||
|
||||
uniqAddr.setAddressTodo(m.Addr)
|
||||
|
||||
m.ln.Close()
|
||||
m.lnSetup = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnFinalShutdown tears down the metrics listener on shutdown and restart.
|
||||
func (m *Metrics) OnFinalShutdown() error {
|
||||
// We allow prometheus statements in multiple Server Blocks, but only the first
|
||||
// will open the listener, for the rest they are all nil; guard against that.
|
||||
if m.ln != nil {
|
||||
return m.ln.Close()
|
||||
if !m.lnSetup {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
m.lnSetup = false
|
||||
return m.ln.Close()
|
||||
}
|
||||
|
||||
func keys(m map[string]bool) []string {
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestMetrics(t *testing.T) {
|
||||
if err := met.OnStartup(); err != nil {
|
||||
t.Fatalf("Failed to start metrics handler: %s", err)
|
||||
}
|
||||
defer met.OnShutdown()
|
||||
defer met.OnFinalShutdown()
|
||||
|
||||
met.AddZone("example.org.")
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func init() {
|
||||
Action: setup,
|
||||
})
|
||||
|
||||
uniqAddr = addrs{a: make(map[string]int)}
|
||||
uniqAddr = newAddress()
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
@@ -29,14 +29,15 @@ func setup(c *caddy.Controller) error {
|
||||
return m
|
||||
})
|
||||
|
||||
for a, v := range uniqAddr.a {
|
||||
if v == todo {
|
||||
c.OncePerServerBlock(m.OnStartup)
|
||||
}
|
||||
uniqAddr.a[a] = done
|
||||
}
|
||||
c.OncePerServerBlock(func() error {
|
||||
c.OnStartup(func() error {
|
||||
return uniqAddr.forEachTodo()
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnShutdown(m.OnShutdown)
|
||||
c.OnRestart(m.OnRestart)
|
||||
c.OnFinalShutdown(m.OnFinalShutdown)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -45,7 +46,7 @@ func prometheusParse(c *caddy.Controller) (*Metrics, error) {
|
||||
var met = New(defaultAddr)
|
||||
|
||||
defer func() {
|
||||
uniqAddr.SetAddress(met.Addr)
|
||||
uniqAddr.setAddress(met.Addr, met.OnStartup)
|
||||
}()
|
||||
|
||||
i := 0
|
||||
@@ -75,25 +76,5 @@ func prometheusParse(c *caddy.Controller) (*Metrics, error) {
|
||||
return met, nil
|
||||
}
|
||||
|
||||
var uniqAddr addrs
|
||||
|
||||
// Keep track on which addrs we listen, so we only start one listener.
|
||||
type addrs struct {
|
||||
a map[string]int
|
||||
}
|
||||
|
||||
func (a *addrs) SetAddress(addr string) {
|
||||
// If already there and set to done, we've already started this listener.
|
||||
if a.a[addr] == done {
|
||||
return
|
||||
}
|
||||
a.a[addr] = todo
|
||||
}
|
||||
|
||||
// defaultAddr is the address the where the metrics are exported by default.
|
||||
const defaultAddr = "localhost:9153"
|
||||
|
||||
const (
|
||||
todo = 1
|
||||
done = 2
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user