Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curoverse.com>
type eventSource interface {
NewSink() eventSink
DB() *sql.DB
type eventSource interface {
NewSink() eventSink
DB() *sql.DB
+func (ps *pgEventSource) DBHealth() error {
+ ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
+ var i int
+ return ps.db.QueryRowContext(ctx, "SELECT 1").Scan(&i)
+}
+
func (ps *pgEventSource) DebugStatus() interface{} {
ps.mtx.Lock()
defer ps.mtx.Unlock()
func (ps *pgEventSource) DebugStatus() interface{} {
ps.mtx.Lock()
defer ps.mtx.Unlock()
case <-time.After(10 * time.Second):
c.Fatal("timed out")
}
case <-time.After(10 * time.Second):
c.Fatal("timed out")
}
+
+ c.Check(pges.DBHealth(), check.IsNil)
rtr.mux.Handle("/arvados/v1/events.ws", rtr.makeServer(newSessionV1))
rtr.mux.HandleFunc("/debug.json", jsonHandler(rtr.DebugStatus))
rtr.mux.HandleFunc("/status.json", jsonHandler(rtr.Status))
rtr.mux.Handle("/arvados/v1/events.ws", rtr.makeServer(newSessionV1))
rtr.mux.HandleFunc("/debug.json", jsonHandler(rtr.DebugStatus))
rtr.mux.HandleFunc("/status.json", jsonHandler(rtr.Status))
+ rtr.mux.HandleFunc("/_health/ping", jsonHandler(rtr.HealthFunc(func() error { return nil })))
+ rtr.mux.HandleFunc("/_health/db", jsonHandler(rtr.HealthFunc(rtr.eventSource.DBHealth)))
}
func (rtr *router) makeServer(newSession sessionFactory) *websocket.Server {
}
func (rtr *router) makeServer(newSession sessionFactory) *websocket.Server {
+var pingResponseOK = map[string]string{"health": "OK"}
+
+func (rtr *router) HealthFunc(f func() error) func() interface{} {
+ return func() interface{} {
+ err := f()
+ if err == nil {
+ return pingResponseOK
+ }
+ return map[string]string{
+ "health": "ERROR",
+ "error": err.Error(),
+ }
+ }
+}
+
func (rtr *router) Status() interface{} {
return map[string]interface{}{
"Clients": atomic.LoadInt64(&rtr.status.ReqsActive),
func (rtr *router) Status() interface{} {
return map[string]interface{}{
"Clients": atomic.LoadInt64(&rtr.status.ReqsActive),
+ "io/ioutil"
+ "net/http"
var _ = check.Suite(&serverSuite{})
type serverSuite struct {
var _ = check.Suite(&serverSuite{})
type serverSuite struct {
+ cfg *wsConfig
+ srv *server
+ wg sync.WaitGroup
-func testConfig() *wsConfig {
+func (s *serverSuite) SetUpTest(c *check.C) {
+ s.cfg = s.testConfig()
+ s.srv = &server{wsConfig: s.cfg}
+}
+
+func (*serverSuite) testConfig() *wsConfig {
cfg := defaultConfig()
cfg.Client = *(arvados.NewClientFromEnv())
cfg.Postgres = testDBConfig()
cfg := defaultConfig()
cfg.Client = *(arvados.NewClientFromEnv())
cfg.Postgres = testDBConfig()
// TestBadDB ensures Run() returns an error (instead of panicking or
// deadlocking) if it can't connect to the database server at startup.
func (s *serverSuite) TestBadDB(c *check.C) {
// TestBadDB ensures Run() returns an error (instead of panicking or
// deadlocking) if it can't connect to the database server at startup.
func (s *serverSuite) TestBadDB(c *check.C) {
- cfg := testConfig()
- cfg.Postgres["password"] = "1234"
- srv := &server{wsConfig: cfg}
+ s.cfg.Postgres["password"] = "1234"
var wg sync.WaitGroup
wg.Add(1)
go func() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
c.Check(err, check.NotNil)
wg.Done()
}()
wg.Add(1)
go func() {
c.Check(err, check.NotNil)
wg.Done()
}()
wg.Add(1)
go func() {
-func newTestServer() *server {
- srv := &server{wsConfig: testConfig()}
- go srv.Run()
- srv.WaitReady()
- return srv
+func (s *serverSuite) TestHealth(c *check.C) {
+ go s.srv.Run()
+ s.srv.WaitReady()
+ resp, err := http.Get("http://" + s.srv.listener.Addr().String() + "/_health/ping")
+ c.Check(err, check.IsNil)
+ buf, err := ioutil.ReadAll(resp.Body)
+ c.Check(err, check.IsNil)
+ c.Check(string(buf), check.Equals, `{"health":"OK"}`+"\n")
var _ = check.Suite(&v0Suite{})
type v0Suite struct {
var _ = check.Suite(&v0Suite{})
type v0Suite struct {
- token string
- toDelete []string
+ serverSuite serverSuite
+ token string
+ toDelete []string
}
func (s *v0Suite) SetUpTest(c *check.C) {
}
func (s *v0Suite) SetUpTest(c *check.C) {
+ s.serverSuite.SetUpTest(c)
s.token = arvadostest.ActiveToken
}
s.token = arvadostest.ActiveToken
}
}
func (s *v0Suite) testClient() (*server, *websocket.Conn, *json.Decoder, *json.Encoder) {
}
func (s *v0Suite) testClient() (*server, *websocket.Conn, *json.Decoder, *json.Encoder) {
+ go s.serverSuite.srv.Run()
+ s.serverSuite.srv.WaitReady()
+ srv := s.serverSuite.srv
conn, err := websocket.Dial("ws://"+srv.listener.Addr().String()+"/websocket?api_token="+s.token, "", "http://"+srv.listener.Addr().String())
if err != nil {
panic(err)
conn, err := websocket.Dial("ws://"+srv.listener.Addr().String()+"/websocket?api_token="+s.token, "", "http://"+srv.listener.Addr().String())
if err != nil {
panic(err)