-
Notifications
You must be signed in to change notification settings - Fork 1
/
probes.go
142 lines (119 loc) · 3.38 KB
/
probes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package health
import (
"context"
"database/sql"
"fmt"
"net/http"
"github.com/gomodule/redigo/redis"
vault "github.com/hashicorp/vault/api"
"github.com/nats-io/go-nats"
"go.mongodb.org/mongo-driver/mongo/readpref"
"google.golang.org/grpc/connectivity"
)
// Interface matching a gRPC client's state method.
type GrpcStateReporter interface {
GetState() connectivity.State
}
// Checks a grpc connection for readiness.
//
// Example:
// cc, _ := grpc.Dial(...)
// checker.AddReadinessProbe("my-grpc-service", health.GrpcProbe(cc))
func GrpcProbe(conn GrpcStateReporter) Probe {
return func() error {
state := conn.GetState()
if state != connectivity.Ready {
return fmt.Errorf("grpc connection is in unready state: %v", state)
}
return nil
}
}
// Pings a http endpoint for readiness. Called endpoint should return 2xx as status.
// **INFO:** If you check another service using this lib, always use the `/.well-known/alive endpoint` to prevent cascading requests.
//
// Example:
// checker.AddReadinessProbe("my-http-service", health.HTTPProbe("http://my-service:8080/.well-known/alive"))
func HTTPProbe(endpoint string) Probe {
return func() error {
// #nosec G107
resp, err := http.Get(endpoint)
if err != nil {
return fmt.Errorf("endpoint could not be reached: %v", err)
}
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
return nil
}
return fmt.Errorf("service is not ready: %v - %v", resp.StatusCode, resp.Status)
}
}
// Interface matching a mongodb client's ping method.
type MongoStateReporter interface {
Ping(ctx context.Context, rp *readpref.ReadPref) error
}
// Checks a mongodb connection for readiness.
//
// Example:
// client, _ := mongo.Connect(ctx, options.Client().ApplyURI(uri))
// checker.AddReadinessProbe("my-mongo-client", health.MongoProbe(client))
func MongoProbe(client MongoStateReporter) Probe {
return func() error {
return client.Ping(context.Background(), readpref.Primary())
}
}
// Interface matching a nats client's status method.
type NatsStateReporter interface {
Status() nats.Status
}
// Checks a nats connection for readiness.
//
// Example:
// sc, _ := stan.Connect(...)
// checker.AddReadinessProbe("my-stan-service", health.NatsProbe(sc.NatsConn()))
func NatsProbe(conn NatsStateReporter) Probe {
return func() error {
state := conn.Status()
if state != nats.CONNECTED {
return fmt.Errorf("nats connection is in unready state: %v", state)
}
return nil
}
}
// Checks a pool of redis connection for readiness.
func RedisPoolProbe(pool *redis.Pool) Probe {
return func() error {
err := pool.Get().Err()
if err != nil {
return fmt.Errorf("redis connection is not useable: %v", err.Error())
}
return nil
}
}
// Checks a SQL connection for readiness.
func SQLProbe(db *sql.DB) Probe {
return func() error {
return db.Ping()
}
}
// Interface matching a vault client's health method.
type VaultHealthReporter interface {
Health() (*vault.HealthResponse, error)
}
// Checks a vault connection for readiness
func VaultProbe(hr VaultHealthReporter) Probe {
return func() error {
hc, err := hr.Health()
if err != nil {
return fmt.Errorf("could not get vault health: %v", err.Error())
}
if !hc.Initialized {
return fmt.Errorf("vault is not initialized")
}
if hc.Sealed {
return fmt.Errorf("vault is sealed")
}
if hc.Standby {
return fmt.Errorf("vault is on standby")
}
return nil
}
}