Merge pull request #10301 from dragonchaser/basic-server-probes

Basic server probes
This commit is contained in:
Christian Richter
2024-10-15 13:30:54 +00:00
committed by GitHub
15 changed files with 77 additions and 50 deletions

View File

@@ -17,8 +17,9 @@ type check func(ctx context.Context) error
// CheckHandlerConfiguration defines the configuration for the CheckHandler.
type CheckHandlerConfiguration struct {
Checks map[string]check
logger log.Logger
checks map[string]check
limit int
statusFailed int
statusSuccess int
@@ -27,7 +28,8 @@ type CheckHandlerConfiguration struct {
// NewCheckHandlerConfiguration initializes a new CheckHandlerConfiguration.
func NewCheckHandlerConfiguration() CheckHandlerConfiguration {
return CheckHandlerConfiguration{
checks: make(map[string]check),
Checks: make(map[string]check),
limit: -1,
statusFailed: http.StatusInternalServerError,
statusSuccess: http.StatusOK,
@@ -42,17 +44,17 @@ func (c CheckHandlerConfiguration) WithLogger(l log.Logger) CheckHandlerConfigur
// WithCheck sets a check for the CheckHandlerConfiguration.
func (c CheckHandlerConfiguration) WithCheck(name string, f check) CheckHandlerConfiguration {
if _, ok := c.checks[name]; ok {
if _, ok := c.Checks[name]; ok {
c.logger.Panic().Str("check", name).Msg("check already exists")
}
c.checks[name] = f
c.Checks[name] = f
return c
}
// WithInheritedChecksFrom appends the checks from another CheckHandlerConfiguration.
func (c CheckHandlerConfiguration) WithInheritedChecksFrom(other CheckHandlerConfiguration) CheckHandlerConfiguration {
for name, check := range other.checks {
for name, check := range other.Checks {
c.WithCheck(name, check)
}
@@ -79,27 +81,27 @@ func (c CheckHandlerConfiguration) WithStatusSuccess(status int) CheckHandlerCon
// CheckHandler is a http Handler that performs different checks.
type CheckHandler struct {
conf CheckHandlerConfiguration
Conf CheckHandlerConfiguration
}
// NewCheckHandler initializes a new CheckHandler.
func NewCheckHandler(c CheckHandlerConfiguration) *CheckHandler {
c.checks = maps.Clone(c.checks) // prevent check duplication after initialization
c.Checks = maps.Clone(c.Checks) // prevent check duplication after initialization
return &CheckHandler{
conf: c,
Conf: c,
}
}
// AddCheck adds a check to the CheckHandler.
func (h *CheckHandler) AddCheck(name string, c check) {
h.conf.WithCheck(name, c)
h.Conf.WithCheck(name, c)
}
func (h *CheckHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
g, ctx := errgroup.WithContext(r.Context())
g.SetLimit(h.conf.limit)
g.SetLimit(h.Conf.limit)
for name, check := range h.conf.checks {
for name, check := range h.Conf.Checks {
checker := check
checkerName := name
g.Go(func() error { // https://go.dev/blog/loopvar-preview per iteration scope since go 1.22
@@ -111,16 +113,16 @@ func (h *CheckHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
})
}
status := h.conf.statusSuccess
status := h.Conf.statusSuccess
if err := g.Wait(); err != nil {
status = h.conf.statusFailed
h.conf.logger.Error().Err(err).Msg("check failed")
status = h.Conf.statusFailed
h.Conf.logger.Error().Err(err).Msg("check failed")
}
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(status)
if _, err := io.WriteString(w, http.StatusText(status)); err != nil { // io.WriteString should not fail, but if it does, we want to know.
h.conf.logger.Panic().Err(err).Msg("failed to write response")
h.Conf.logger.Panic().Err(err).Msg("failed to write response")
}
}

View File

@@ -0,0 +1,23 @@
package handlers
import (
"context"
"fmt"
"net/http"
"time"
)
// NewHttpCheck checks the reachability of a http server.
func NewHTTPCheck(url string) func(context.Context) error {
return func(_ context.Context) error {
c := http.Client{
Timeout: 3 * time.Second,
}
resp, err := c.Get(url)
if err != nil {
return fmt.Errorf("could not connect to http server: %v", err)
}
_ = resp.Body.Close()
return nil
}
}

View File

@@ -14,13 +14,15 @@ func Server(opts ...Option) (*http.Server, error) {
healthHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("http reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
readyHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger).
WithCheck("nats reachability", handlers.NewNatsCheck(options.Config.Events.Cluster)),
WithCheck("nats reachability", handlers.NewNatsCheck(options.Config.Events.Cluster)).
WithInheritedChecksFrom(healthHandler.Conf),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,13 @@ import (
func Server(opts ...Option) (*http.Server, error) {
options := newOptions(opts...)
checkHandler := handlers.NewCheckHandler(
healthHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger).
WithCheck("http reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
readinessHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger).
WithCheck("tcp-check", func(ctx context.Context) error {
@@ -28,7 +34,8 @@ func Server(opts ...Option) (*http.Server, error) {
}
return handlers.NewTCPCheck(tcpURL)(ctx)
}),
}).
WithInheritedChecksFrom(healthHandler.Conf),
)
return debug.NewService(
@@ -39,7 +46,7 @@ func Server(opts ...Option) (*http.Server, error) {
debug.Token(options.Config.Debug.Token),
debug.Pprof(options.Config.Debug.Pprof),
debug.Zpages(options.Config.Debug.Zpages),
debug.Health(checkHandler),
debug.Ready(checkHandler),
debug.Health(healthHandler),
debug.Ready(readinessHandler),
), nil
}

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -18,7 +18,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
var configDumpFunc http.HandlerFunc = configDump(options.Config)

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -1,14 +1,10 @@
package debug
import (
"context"
"fmt"
"net"
"net/http"
"github.com/owncloud/ocis/v2/ocis-pkg/handlers"
"github.com/owncloud/ocis/v2/ocis-pkg/service/debug"
"github.com/owncloud/ocis/v2/ocis-pkg/version"
"net/http"
)
// Server initializes the debug service and server.
@@ -17,20 +13,7 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger).WithCheck("web reachability", func(ctx context.Context) error {
conn, err := net.Dial("tcp", options.Config.HTTP.Addr)
defer func(conn net.Conn) {
err := conn.Close()
if err != nil {
return
}
}(conn)
if err != nil {
return fmt.Errorf("could not connect to web server: %v", err)
}
return nil
}),
WithLogger(options.Logger).WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(

View File

@@ -14,7 +14,8 @@ func Server(opts ...Option) (*http.Server, error) {
checkHandler := handlers.NewCheckHandler(
handlers.NewCheckHandlerConfiguration().
WithLogger(options.Logger),
WithLogger(options.Logger).
WithCheck("web reachability", handlers.NewHTTPCheck(options.Config.HTTP.Addr)),
)
return debug.NewService(