vendor: revendor

Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
Lucas Servén Marín
2022-04-22 12:05:46 +02:00
parent 7291a3bd71
commit bbc4fe30a6
11 changed files with 784 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package healthcheck
import (
"context"
"database/sql"
"fmt"
"net"
"net/http"
"runtime"
"time"
)
// TCPDialCheck returns a Check that checks TCP connectivity to the provided
// endpoint.
func TCPDialCheck(addr string, timeout time.Duration) Check {
return func() error {
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil {
return err
}
return conn.Close()
}
}
// HTTPGetCheck returns a Check that performs an HTTP GET request against the
// specified URL. The check fails if the response times out or returns a non-200
// status code.
func HTTPGetCheck(url string, timeout time.Duration) Check {
return func() error {
return HTTPCheck(url, http.MethodGet, http.StatusOK, timeout)()
}
}
// HTTPCheck returns a Check that performs a HTTP request against the specified URL.
// The Check fails if the response times out or returns an unexpected status code.
func HTTPCheck(url string, method string, status int, timeout time.Duration) Check {
client := &http.Client{
Timeout: timeout,
CheckRedirect: func(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
},
}
return HTTPCheckClient(client, url, method, status, timeout)
}
// HTTPCheckClient returns a Check that performs a HTTP request against the specified URL.
// The Check fails if the response times out or returns an unexpected status code.
// On top of that it uses a custom client specified by the caller.
func HTTPCheckClient(client *http.Client, url string, method string, status int, timeout time.Duration) Check {
return func() error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
req, err := http.NewRequest(method, url, nil)
if err != nil {
return err
}
req = req.WithContext(ctx)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != status {
return fmt.Errorf("returned status %d, expected %d", resp.StatusCode, status)
}
return nil
}
}
// DatabasePingCheck returns a Check that validates connectivity to a
// database/sql.DB using Ping().
func DatabasePingCheck(database *sql.DB, timeout time.Duration) Check {
return func() error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
if database == nil {
return fmt.Errorf("database is nil")
}
return database.PingContext(ctx)
}
}
// DNSResolveCheck returns a Check that makes sure the provided host can resolve
// to at least one IP address within the specified timeout.
func DNSResolveCheck(host string, timeout time.Duration) Check {
resolver := net.Resolver{}
return func() error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
addrs, err := resolver.LookupHost(ctx, host)
if err != nil {
return err
}
if len(addrs) < 1 {
return fmt.Errorf("could not resolve host")
}
return nil
}
}
// GoroutineCountCheck returns a Check that fails if too many goroutines are
// running (which could indicate a resource leak).
func GoroutineCountCheck(threshold int) Check {
return func() error {
count := runtime.NumGoroutine()
if count > threshold {
return fmt.Errorf("too many goroutines (%d > %d)", count, threshold)
}
return nil
}
}

24
vendor/github.com/metalmatze/signal/healthcheck/doc.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package healthcheck helps you implement liveness and readiness checks
for your application. It supports synchronous and asynchronous (background)
checks. It can optionally report each check's status as a set of Prometheus
gauge metrics for cluster-wide monitoring and alerting.
It also includes a small library of generic checks for DNS, TCP, and HTTP
reachability as well as Goroutine usage.
*/
package healthcheck

View File

@@ -0,0 +1,103 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package healthcheck
import (
"encoding/json"
"net/http"
"sync"
)
// basicHandler is a basic Handler implementation.
type basicHandler struct {
http.ServeMux
checksMutex sync.RWMutex
livenessChecks map[string]Check
readinessChecks map[string]Check
}
// NewHandler creates a new basic Handler
func NewHandler() Handler {
h := &basicHandler{
livenessChecks: make(map[string]Check),
readinessChecks: make(map[string]Check),
}
h.Handle("/live", http.HandlerFunc(h.LiveEndpoint))
h.Handle("/ready", http.HandlerFunc(h.ReadyEndpoint))
return h
}
func (s *basicHandler) LiveEndpoint(w http.ResponseWriter, r *http.Request) {
s.handle(w, r, s.livenessChecks)
}
func (s *basicHandler) ReadyEndpoint(w http.ResponseWriter, r *http.Request) {
s.handle(w, r, s.readinessChecks, s.livenessChecks)
}
func (s *basicHandler) AddLivenessCheck(name string, check Check) {
s.checksMutex.Lock()
defer s.checksMutex.Unlock()
s.livenessChecks[name] = check
}
func (s *basicHandler) AddReadinessCheck(name string, check Check) {
s.checksMutex.Lock()
defer s.checksMutex.Unlock()
s.readinessChecks[name] = check
}
func (s *basicHandler) collectChecks(checks map[string]Check, resultsOut map[string]string, statusOut *int) {
s.checksMutex.RLock()
defer s.checksMutex.RUnlock()
for name, check := range checks {
if err := check(); err != nil {
*statusOut = http.StatusServiceUnavailable
resultsOut[name] = err.Error()
} else {
resultsOut[name] = "OK"
}
}
}
func (s *basicHandler) handle(w http.ResponseWriter, r *http.Request, checks ...map[string]Check) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
checkResults := make(map[string]string)
status := http.StatusOK
for _, checks := range checks {
s.collectChecks(checks, checkResults, &status)
}
// write out the response code and content type header
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
// if ?hide=1, return an empty body. Kubernetes only cares about the
// HTTP status code, so we won't waste bytes on the full body.
if r.URL.Query().Get("hide") == "1" {
_, _ = w.Write([]byte("{}\n"))
return
}
// otherwise, write the JSON body ignoring any encoding errors (which
// shouldn't really be possible since we're encoding a map[string]string).
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
_ = encoder.Encode(checkResults)
}

View File

@@ -0,0 +1,72 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package healthcheck
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
)
type metricsHandler struct {
handler Handler
registry prometheus.Registerer
}
// NewMetricsHandler returns a healthy Handler that writes the current check status
// into the provided Prometheus registry.
func NewMetricsHandler(handler Handler, registry prometheus.Registerer) Handler {
return &metricsHandler{
handler: handler,
registry: registry,
}
}
func (h *metricsHandler) AddLivenessCheck(name string, check Check) {
h.handler.AddLivenessCheck(name, h.wrap(prometheus.Labels{"name": name, "check": "live"}, check))
}
func (h *metricsHandler) AddReadinessCheck(name string, check Check) {
h.handler.AddReadinessCheck(name, h.wrap(prometheus.Labels{"name": name, "check": "ready"}, check))
}
func (h *metricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.handler.ServeHTTP(w, r)
}
func (h *metricsHandler) LiveEndpoint(w http.ResponseWriter, r *http.Request) {
h.handler.LiveEndpoint(w, r)
}
func (h *metricsHandler) ReadyEndpoint(w http.ResponseWriter, r *http.Request) {
h.handler.ReadyEndpoint(w, r)
}
func (h *metricsHandler) wrap(labels prometheus.Labels, check Check) Check {
h.registry.MustRegister(prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "healthcheck",
Help: "Indicates if check is healthy (1 is healthy, 0 is unhealthy)",
ConstLabels: labels,
},
func() float64 {
if check() != nil {
return 0
}
return 1
},
))
return check
}

View File

@@ -0,0 +1,52 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package healthcheck
import (
"fmt"
"time"
)
// TimeoutError is the error returned when a Timeout-wrapped Check takes too long
type timeoutError time.Duration
func (e timeoutError) Error() string {
return fmt.Sprintf("timed out after %s", time.Duration(e).String())
}
// Timeout returns whether this error is a timeout (always true for timeoutError)
func (e timeoutError) Timeout() bool {
return true
}
// Temporary returns whether this error is temporary (always true for timeoutError)
func (e timeoutError) Temporary() bool {
return true
}
// Timeout adds a timeout to a Check. If the underlying check takes longer than
// the timeout, it returns an error.
func Timeout(check Check, timeout time.Duration) Check {
return func() error {
c := make(chan error, 1)
go func() { c <- check() }()
select {
case err := <-c:
return err
case <-time.After(timeout):
return timeoutError(timeout)
}
}
}

View File

@@ -0,0 +1,52 @@
// Copyright 2020 by the contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package healthcheck
import (
"net/http"
)
// Check is a health/readiness check.
type Check func() error
// Handler is an http.Handler with additional methods that register health and
// readiness checks. It handles handle "/live" and "/ready" HTTP
// endpoints.
type Handler interface {
// The Handler is an http.Handler, so it can be exposed directly and handle
// /live and /ready endpoints.
http.Handler
// AddLivenessCheck adds a check that indicates that this instance of the
// application should be destroyed or restarted. A failed liveness check
// indicates that this instance is unhealthy, not some upstream dependency.
// Every liveness check is also included as a readiness check.
AddLivenessCheck(name string, check Check)
// AddReadinessCheck adds a check that indicates that this instance of the
// application is currently unable to serve requests because of an upstream
// or some transient failure. If a readiness check fails, this instance
// should no longer receiver requests, but should not be restarted or
// destroyed.
AddReadinessCheck(name string, check Check)
// LiveEndpoint is the HTTP handler for just the /live endpoint, which is
// useful if you need to attach it into your own HTTP handler tree.
LiveEndpoint(http.ResponseWriter, *http.Request)
// ReadyEndpoint is the HTTP handler for just the /ready endpoint, which is
// useful if you need to attach it into your own HTTP handler tree.
ReadyEndpoint(http.ResponseWriter, *http.Request)
}