210 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"net/http/httptest"
 | |
| 	"os"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/distribution/distribution/v3/configuration"
 | |
| 	"github.com/distribution/distribution/v3/context"
 | |
| 	"github.com/distribution/distribution/v3/health"
 | |
| )
 | |
| 
 | |
| func TestFileHealthCheck(t *testing.T) {
 | |
| 	interval := time.Second
 | |
| 
 | |
| 	tmpfile, err := os.CreateTemp(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 != http.MethodHead {
 | |
| 			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")
 | |
| 	}
 | |
| }
 |