/
status.go
158 lines (138 loc) · 4.15 KB
/
status.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
// Package debugstatus provides facilities for inspecting information
// about a running HTTP service.
package debugstatus
import (
"fmt"
"sync"
"time"
"golang.org/x/net/context"
"gopkg.in/mgo.v2"
)
// Check collects the status check results from the given checkers.
func Check(ctx context.Context, checkers ...CheckerFunc) map[string]CheckResult {
var mu sync.Mutex
results := make(map[string]CheckResult, len(checkers))
var wg sync.WaitGroup
for _, c := range checkers {
c := c
wg.Add(1)
go func() {
defer wg.Done()
t0 := time.Now()
key, result := c(ctx)
result.Duration = time.Since(t0)
mu.Lock()
results[key] = result
mu.Unlock()
}()
}
wg.Wait()
return results
}
// CheckResult holds the result of a single status check.
type CheckResult struct {
// Name is the human readable name for the check.
Name string
// Value is the check result.
Value string
// Passed reports whether the check passed.
Passed bool
// Duration holds the duration that the
// status check took to run.
Duration time.Duration
}
// CheckerFunc represents a function returning the check machine friendly key
// and the result.
type CheckerFunc func(ctx context.Context) (key string, result CheckResult)
// StartTime holds the time that the code started running.
var StartTime = time.Now().UTC()
// ServerStartTime reports the time when the application was started.
func ServerStartTime(context.Context) (key string, result CheckResult) {
return "server_started", CheckResult{
Name: "Server started",
Value: StartTime.String(),
Passed: true,
}
}
// Connection returns a status checker reporting whether the given Pinger is
// connected.
func Connection(p Pinger) CheckerFunc {
return func(context.Context) (key string, result CheckResult) {
result.Name = "MongoDB is connected"
if err := p.Ping(); err != nil {
result.Value = "Ping error: " + err.Error()
return "mongo_connected", result
}
result.Value = "Connected"
result.Passed = true
return "mongo_connected", result
}
}
// Pinger is an interface that wraps the Ping method.
// It is implemented by mgo.Session.
type Pinger interface {
Ping() error
}
var _ Pinger = (*mgo.Session)(nil)
// MongoCollections returns a status checker checking that all the
// expected Mongo collections are present in the database.
func MongoCollections(c Collector) CheckerFunc {
return func(context.Context) (key string, result CheckResult) {
key = "mongo_collections"
result.Name = "MongoDB collections"
names, err := c.CollectionNames()
if err != nil {
result.Value = "Cannot get collections: " + err.Error()
return key, result
}
var missing []string
for _, coll := range c.Collections() {
found := false
for _, name := range names {
if name == coll.Name {
found = true
break
}
}
if !found {
missing = append(missing, coll.Name)
}
}
if len(missing) == 0 {
result.Value = "All required collections exist"
result.Passed = true
return key, result
}
result.Value = fmt.Sprintf("Missing collections: %s", missing)
return key, result
}
}
// Collector is an interface that groups the methods used to check that
// a Mongo database has the expected collections.
// It is usually implemented by types extending mgo.Database to add the
// Collections() method.
type Collector interface {
// Collections returns the Mongo collections that we expect to exist in
// the Mongo database.
Collections() []*mgo.Collection
// CollectionNames returns the names of the collections actually present in
// the Mongo database.
CollectionNames() ([]string, error)
}
// Rename changes the key and/or result name returned by the given check.
// It is possible to pass an empty string to avoid changing one of the values.
// This means that if both key are name are empty, this closure is a no-op.
func Rename(newKey, newName string, check CheckerFunc) CheckerFunc {
return func(ctx context.Context) (key string, result CheckResult) {
key, result = check(ctx)
if newKey == "" {
newKey = key
}
if newName != "" {
result.Name = newName
}
return newKey, result
}
}