mirror of
https://github.com/coredns/coredns.git
synced 2026-10-09 12:05:22 -04:00
Signed-off-by: Paco Cartones <pacocartones@users.noreply.github.com> Co-authored-by: Paco Cartones <pacocartones@users.noreply.github.com>
204 lines
5.6 KiB
Go
204 lines
5.6 KiB
Go
package ready
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/plugin/erratic"
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
|
"github.com/coredns/coredns/plugin/test"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func init() { clog.Discard() }
|
|
|
|
func TestReady(t *testing.T) {
|
|
rd := &ready{Addr: ":0"}
|
|
e := &erratic.Erratic{}
|
|
plugins.Append(e, "erratic")
|
|
|
|
if err := rd.onStartup(); err != nil {
|
|
t.Fatalf("Unable to startup the readiness server: %v", err)
|
|
}
|
|
|
|
defer rd.onFinalShutdown()
|
|
|
|
address := fmt.Sprintf("http://%s/ready", rd.ln.Addr().String())
|
|
|
|
response, err := http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusServiceUnavailable {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 503, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
|
|
// make it ready by giving erratic 3 queries.
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("example.org.", dns.TypeA)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
|
|
response, err = http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusOK {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 200, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
|
|
// make erratic not-ready by giving it more queries, this should not change the process readiness
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
|
|
response, err = http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusOK {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 200, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
}
|
|
|
|
func TestReady_Continuously(t *testing.T) {
|
|
rd := &ready{Addr: ":0"}
|
|
e := &erratic.Erratic{}
|
|
plugins.Append(e, "erratic")
|
|
plugins.keepReadiness = true
|
|
|
|
if err := rd.onStartup(); err != nil {
|
|
t.Fatalf("Unable to startup the readiness server: %v", err)
|
|
}
|
|
|
|
defer rd.onFinalShutdown()
|
|
|
|
address := fmt.Sprintf("http://%s/ready", rd.ln.Addr().String())
|
|
|
|
response, err := http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusServiceUnavailable {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 503, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
|
|
// make it ready by giving erratic 3 queries.
|
|
m := new(dns.Msg)
|
|
m.SetQuestion("example.org.", dns.TypeA)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
|
|
response, err = http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusOK {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 200, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
|
|
// make erratic not-ready by giving it more queries, this should change the process readiness
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
e.ServeDNS(context.TODO(), &test.ResponseWriter{}, m)
|
|
|
|
response, err = http.Get(address)
|
|
if err != nil {
|
|
t.Fatalf("Unable to query %s: %v", address, err)
|
|
}
|
|
if response.StatusCode != http.StatusServiceUnavailable {
|
|
t.Errorf("Invalid status code: expecting %d, got %d", 503, response.StatusCode)
|
|
}
|
|
response.Body.Close()
|
|
}
|
|
|
|
func TestReadyShutdownDoesNotHoldLockWhileWaitingForHandlers(t *testing.T) {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
accepted := make(chan struct{})
|
|
proceed := make(chan struct{})
|
|
shutdownStarted := make(chan struct{})
|
|
rd := &ready{Addr: ln.Addr().String(), done: true, ln: ln}
|
|
rd.srv = &http.Server{Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
close(accepted)
|
|
<-proceed
|
|
rd.Lock()
|
|
defer rd.Unlock()
|
|
if !rd.done {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
io.WriteString(w, "Shutting down")
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
})}
|
|
rd.srv.RegisterOnShutdown(func() { close(shutdownStarted) })
|
|
go rd.srv.Serve(ln)
|
|
|
|
response := make(chan *http.Response, 1)
|
|
requestErr := make(chan error, 1)
|
|
go func() {
|
|
res, err := http.Get("http://" + ln.Addr().String())
|
|
if err != nil {
|
|
requestErr <- err
|
|
return
|
|
}
|
|
response <- res
|
|
}()
|
|
select {
|
|
case <-accepted:
|
|
case err := <-requestErr:
|
|
t.Fatalf("readiness request failed before reaching handler: %v", err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("readiness request did not reach handler")
|
|
}
|
|
|
|
shutdownDone := make(chan error, 1)
|
|
go func() { shutdownDone <- rd.onFinalShutdown() }()
|
|
select {
|
|
case <-shutdownStarted:
|
|
case err := <-shutdownDone:
|
|
t.Fatalf("shutdown returned before reaching server: %v", err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("ready server shutdown did not start")
|
|
}
|
|
close(proceed)
|
|
|
|
select {
|
|
case err := <-requestErr:
|
|
t.Fatalf("readiness request failed: %v", err)
|
|
case res := <-response:
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusServiceUnavailable {
|
|
t.Fatalf("expected shutdown response %d, got %d", http.StatusServiceUnavailable, res.StatusCode)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("readiness request blocked behind shutdown")
|
|
}
|
|
|
|
select {
|
|
case err := <-shutdownDone:
|
|
if err != nil {
|
|
t.Fatalf("shutdown failed: %v", err)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("ready server shutdown blocked waiting for its handler")
|
|
}
|
|
}
|