211 lines
4.6 KiB
Go
211 lines
4.6 KiB
Go
|
package handlers
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"net/http/httptest"
|
||
|
"os"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/docker/distribution/configuration"
|
||
|
"github.com/docker/distribution/context"
|
||
|
"github.com/docker/distribution/health"
|
||
|
)
|
||
|
|
||
|
func TestFileHealthCheck(t *testing.T) {
|
||
|
interval := time.Second
|
||
|
|
||
|
tmpfile, err := ioutil.TempFile(os.TempDir(), "healthcheck")
|
||
|
if err != nil {
|
||
|
t.Fatalf("could not create temporary file: %v", err)
|
||
|
}
|
||
|
defer tmpfile.Close()
|
||
|
|
||
|
config := &configuration.Configuration{
|
||
|
Storage: configuration.Storage{
|
||
|
"inmemory": configuration.Parameters{},
|
||
|
"maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{
|
||
|
"enabled": false,
|
||
|
}},
|
||
|
},
|
||
|
Health: configuration.Health{
|
||
|
FileCheckers: []configuration.FileChecker{
|
||
|
{
|
||
|
Interval: interval,
|
||
|
File: tmpfile.Name(),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ctx := context.Background()
|
||
|
|
||
|
app := NewApp(ctx, config)
|
||
|
healthRegistry := health.NewRegistry()
|
||
|
app.RegisterHealthChecks(healthRegistry)
|
||
|
|
||
|
// Wait for health check to happen
|
||
|
<-time.After(2 * interval)
|
||
|
|
||
|
status := healthRegistry.CheckStatus()
|
||
|
if len(status) != 1 {
|
||
|
t.Fatal("expected 1 item in health check results")
|
||
|
}
|
||
|
if status[tmpfile.Name()] != "file exists" {
|
||
|
t.Fatal(`did not get "file exists" result for health check`)
|
||
|
}
|
||
|
|
||
|
os.Remove(tmpfile.Name())
|
||
|
|
||
|
<-time.After(2 * interval)
|
||
|
if len(healthRegistry.CheckStatus()) != 0 {
|
||
|
t.Fatal("expected 0 items in health check results")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTCPHealthCheck(t *testing.T) {
|
||
|
interval := time.Second
|
||
|
|
||
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
if err != nil {
|
||
|
t.Fatalf("could not create listener: %v", err)
|
||
|
}
|
||
|
addrStr := ln.Addr().String()
|
||
|
|
||
|
// Start accepting
|
||
|
go func() {
|
||
|
for {
|
||
|
conn, err := ln.Accept()
|
||
|
if err != nil {
|
||
|
// listener was closed
|
||
|
return
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
config := &configuration.Configuration{
|
||
|
Storage: configuration.Storage{
|
||
|
"inmemory": configuration.Parameters{},
|
||
|
"maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{
|
||
|
"enabled": false,
|
||
|
}},
|
||
|
},
|
||
|
Health: configuration.Health{
|
||
|
TCPCheckers: []configuration.TCPChecker{
|
||
|
{
|
||
|
Interval: interval,
|
||
|
Addr: addrStr,
|
||
|
Timeout: 500 * time.Millisecond,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ctx := context.Background()
|
||
|
|
||
|
app := NewApp(ctx, config)
|
||
|
healthRegistry := health.NewRegistry()
|
||
|
app.RegisterHealthChecks(healthRegistry)
|
||
|
|
||
|
// Wait for health check to happen
|
||
|
<-time.After(2 * interval)
|
||
|
|
||
|
if len(healthRegistry.CheckStatus()) != 0 {
|
||
|
t.Fatal("expected 0 items in health check results")
|
||
|
}
|
||
|
|
||
|
ln.Close()
|
||
|
<-time.After(2 * interval)
|
||
|
|
||
|
// Health check should now fail
|
||
|
status := healthRegistry.CheckStatus()
|
||
|
if len(status) != 1 {
|
||
|
t.Fatal("expected 1 item in health check results")
|
||
|
}
|
||
|
if status[addrStr] != "connection to "+addrStr+" failed" {
|
||
|
t.Fatal(`did not get "connection failed" result for health check`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestHTTPHealthCheck(t *testing.T) {
|
||
|
interval := time.Second
|
||
|
threshold := 3
|
||
|
|
||
|
stopFailing := make(chan struct{})
|
||
|
|
||
|
checkedServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
if r.Method != "HEAD" {
|
||
|
t.Fatalf("expected HEAD request, got %s", r.Method)
|
||
|
}
|
||
|
select {
|
||
|
case <-stopFailing:
|
||
|
w.WriteHeader(http.StatusOK)
|
||
|
default:
|
||
|
w.WriteHeader(http.StatusInternalServerError)
|
||
|
}
|
||
|
}))
|
||
|
|
||
|
config := &configuration.Configuration{
|
||
|
Storage: configuration.Storage{
|
||
|
"inmemory": configuration.Parameters{},
|
||
|
"maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{
|
||
|
"enabled": false,
|
||
|
}},
|
||
|
},
|
||
|
Health: configuration.Health{
|
||
|
HTTPCheckers: []configuration.HTTPChecker{
|
||
|
{
|
||
|
Interval: interval,
|
||
|
URI: checkedServer.URL,
|
||
|
Threshold: threshold,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ctx := context.Background()
|
||
|
|
||
|
app := NewApp(ctx, config)
|
||
|
healthRegistry := health.NewRegistry()
|
||
|
app.RegisterHealthChecks(healthRegistry)
|
||
|
|
||
|
for i := 0; ; i++ {
|
||
|
<-time.After(interval)
|
||
|
|
||
|
status := healthRegistry.CheckStatus()
|
||
|
|
||
|
if i < threshold-1 {
|
||
|
// definitely shouldn't have hit the threshold yet
|
||
|
if len(status) != 0 {
|
||
|
t.Fatal("expected 1 item in health check results")
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
if i < threshold+1 {
|
||
|
// right on the threshold - don't expect a failure yet
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if len(status) != 1 {
|
||
|
t.Fatal("expected 1 item in health check results")
|
||
|
}
|
||
|
if status[checkedServer.URL] != "downstream service returned unexpected status: 500" {
|
||
|
t.Fatal("did not get expected result for health check")
|
||
|
}
|
||
|
|
||
|
break
|
||
|
}
|
||
|
|
||
|
// Signal HTTP handler to start returning 200
|
||
|
close(stopFailing)
|
||
|
|
||
|
<-time.After(2 * interval)
|
||
|
|
||
|
if len(healthRegistry.CheckStatus()) != 0 {
|
||
|
t.Fatal("expected 0 items in health check results")
|
||
|
}
|
||
|
}
|