mirror of
https://github.com/coredns/coredns.git
synced 2025-12-07 02:45:11 -05:00
Remove the word middleware (#1067)
* Rename middleware to plugin first pass; mostly used 'sed', few spots where I manually changed text. This still builds a coredns binary. * fmt error * Rename AddMiddleware to AddPlugin * Readd AddMiddleware to remain backwards compat
This commit is contained in:
23
plugin/health/README.md
Normal file
23
plugin/health/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# health
|
||||
|
||||
This module enables a simple health check endpoint. By default it will listen on port 8080.
|
||||
|
||||
## Syntax
|
||||
|
||||
~~~
|
||||
health [ADDRESS]
|
||||
~~~
|
||||
|
||||
Optionally takes an address; the default is `:8080`. The health path is fixed to `/health`. The
|
||||
health endpoint returns a 200 response code and the word "OK" when CoreDNS is healthy. It returns
|
||||
a 503. *health* periodically (1s) polls plugin that exports health information. If any of the
|
||||
plugin signals that it is unhealthy, the server will go unhealthy too. Each plugin that
|
||||
supports health checks has a section "Health" in their README.
|
||||
|
||||
## Examples
|
||||
|
||||
Run another health endpoint on http://localhost:8091.
|
||||
|
||||
~~~
|
||||
health localhost:8091
|
||||
~~~
|
||||
69
plugin/health/health.go
Normal file
69
plugin/health/health.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Package health implements an HTTP handler that responds to health checks.
|
||||
package health
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
type health struct {
|
||||
Addr string
|
||||
|
||||
ln net.Listener
|
||||
mux *http.ServeMux
|
||||
|
||||
// A slice of Healthers that the health plugin will poll every second for their health status.
|
||||
h []Healther
|
||||
sync.RWMutex
|
||||
ok bool // ok is the global boolean indicating an all healthy plugin stack
|
||||
}
|
||||
|
||||
func (h *health) Startup() error {
|
||||
if h.Addr == "" {
|
||||
h.Addr = defAddr
|
||||
}
|
||||
|
||||
once.Do(func() {
|
||||
ln, err := net.Listen("tcp", h.Addr)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to start health handler: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
h.ln = ln
|
||||
|
||||
h.mux = http.NewServeMux()
|
||||
|
||||
h.mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||
if h.Ok() {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
io.WriteString(w, ok)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
})
|
||||
|
||||
go func() {
|
||||
http.Serve(h.ln, h.mux)
|
||||
}()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *health) Shutdown() error {
|
||||
if h.ln != nil {
|
||||
return h.ln.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
ok = "OK"
|
||||
defAddr = ":8080"
|
||||
path = "/health"
|
||||
)
|
||||
47
plugin/health/health_test.go
Normal file
47
plugin/health/health_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package health
|
||||
|
||||
// TODO(miek): enable again if plugin gets health check.
|
||||
/*
|
||||
func TestHealth(t *testing.T) {
|
||||
h := health{Addr: ":0"}
|
||||
h.h = append(h.h, &erratic.Erratic{})
|
||||
|
||||
if err := h.Startup(); err != nil {
|
||||
t.Fatalf("Unable to startup the health server: %v", err)
|
||||
}
|
||||
defer h.Shutdown()
|
||||
|
||||
// Reconstruct the http address based on the port allocated by operating system.
|
||||
address := fmt.Sprintf("http://%s%s", h.ln.Addr().String(), path)
|
||||
|
||||
// Norhing set should be unhealthy
|
||||
response, err := http.Get(address)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to query %s: %v", address, err)
|
||||
}
|
||||
if response.StatusCode != 503 {
|
||||
t.Errorf("Invalid status code: expecting '503', got '%d'", response.StatusCode)
|
||||
}
|
||||
response.Body.Close()
|
||||
|
||||
// Make healthy
|
||||
h.Poll()
|
||||
|
||||
response, err = http.Get(address)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to query %s: %v", address, err)
|
||||
}
|
||||
if response.StatusCode != 200 {
|
||||
t.Errorf("Invalid status code: expecting '200', got '%d'", response.StatusCode)
|
||||
}
|
||||
content, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get response body from %s: %v", address, err)
|
||||
}
|
||||
response.Body.Close()
|
||||
|
||||
if string(content) != ok {
|
||||
t.Errorf("Invalid response body: expecting 'OK', got '%s'", string(content))
|
||||
}
|
||||
}
|
||||
*/
|
||||
42
plugin/health/healther.go
Normal file
42
plugin/health/healther.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package health
|
||||
|
||||
// Healther interface needs to be implemented by each plugin willing to
|
||||
// provide healthhceck information to the health plugin. As a second step
|
||||
// the plugin needs to registered against the health plugin, by addding
|
||||
// it to healthers map. Note this method should return quickly, i.e. just
|
||||
// checking a boolean status, as it is called every second from the health
|
||||
// plugin.
|
||||
type Healther interface {
|
||||
// Health returns a boolean indicating the health status of a plugin.
|
||||
// False indicates unhealthy.
|
||||
Health() bool
|
||||
}
|
||||
|
||||
// Ok returns the global health status of all plugin configured in this server.
|
||||
func (h *health) Ok() bool {
|
||||
h.RLock()
|
||||
defer h.RUnlock()
|
||||
return h.ok
|
||||
}
|
||||
|
||||
// SetOk sets the global health status of all plugin configured in this server.
|
||||
func (h *health) SetOk(ok bool) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
h.ok = ok
|
||||
}
|
||||
|
||||
// poll polls all healthers and sets the global state.
|
||||
func (h *health) poll() {
|
||||
for _, m := range h.h {
|
||||
if !m.Health() {
|
||||
h.SetOk(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
h.SetOk(true)
|
||||
}
|
||||
|
||||
// Middleware that implements the Healther interface.
|
||||
// TODO(miek): none yet.
|
||||
var healthers = map[string]bool{}
|
||||
73
plugin/health/setup.go
Normal file
73
plugin/health/setup.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/plugin"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("health", caddy.Plugin{
|
||||
ServerType: "dns",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
addr, err := healthParse(c)
|
||||
if err != nil {
|
||||
return plugin.Error("health", err)
|
||||
}
|
||||
|
||||
h := &health{Addr: addr}
|
||||
|
||||
c.OnStartup(func() error {
|
||||
for he := range healthers {
|
||||
m := dnsserver.GetConfig(c).Handler(he)
|
||||
if x, ok := m.(Healther); ok {
|
||||
h.h = append(h.h, x)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnStartup(func() error {
|
||||
h.poll()
|
||||
go func() {
|
||||
for {
|
||||
<-time.After(1 * time.Second)
|
||||
h.poll()
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
})
|
||||
|
||||
c.OnStartup(h.Startup)
|
||||
c.OnFinalShutdown(h.Shutdown)
|
||||
|
||||
// Don't do AddMiddleware, as health is not *really* a plugin just a separate webserver running.
|
||||
return nil
|
||||
}
|
||||
|
||||
func healthParse(c *caddy.Controller) (string, error) {
|
||||
addr := ""
|
||||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
addr = args[0]
|
||||
if _, _, e := net.SplitHostPort(addr); e != nil {
|
||||
return "", e
|
||||
}
|
||||
default:
|
||||
return "", c.ArgErr()
|
||||
}
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
35
plugin/health/setup_test.go
Normal file
35
plugin/health/setup_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package health
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func TestSetupHealth(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
}{
|
||||
{`health`, false},
|
||||
{`health localhost:1234`, false},
|
||||
{`health bla:a`, false},
|
||||
{`health bla`, true},
|
||||
{`health bla bla`, true},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.input)
|
||||
_, err := healthParse(c)
|
||||
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if !test.shouldErr {
|
||||
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user