Skip to content

Commit 27ed6d2

Browse files
authoredNov 2, 2018
Merge pull request hound-search#297 from etsy/health-check
Add the ability to respond to health checks prior to indexes being built.
2 parents c3ae284 + a22cb80 commit 27ed6d2

File tree

4 files changed

+119
-25
lines changed

4 files changed

+119
-25
lines changed
 

‎cmds/houndd/main.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/etsy/hound/config"
1818
"github.com/etsy/hound/searcher"
1919
"github.com/etsy/hound/ui"
20+
"github.com/etsy/hound/web"
2021
)
2122

2223
const gracefulShutdownSignal = syscall.SIGTERM
@@ -128,6 +129,9 @@ func main() {
128129
panic(err)
129130
}
130131

132+
// Start the web server on a background routine.
133+
ws := web.Start(&cfg, *flagAddr, *flagDev)
134+
131135
// It's not safe to be killed during makeSearchers, so register the
132136
// shutdown signal here and defer processing it until we are ready.
133137
shutdownCh := registerShutdownSignal()
@@ -162,7 +166,6 @@ func main() {
162166

163167
info_log.Printf("running server at http://%s...\n", host)
164168

165-
if err := runHttp(*flagAddr, *flagDev, &cfg, idx); err != nil {
166-
panic(err)
167-
}
169+
// Fully enable the web server now that we have indexes
170+
panic(ws.ServeWithIndex(idx))
168171
}

‎config/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
defaultVcs = "git"
1616
defaultBaseUrl = "{url}/blob/master/{path}{anchor}"
1717
defaultAnchor = "#L{line}"
18+
defaultHealthChekURI = "/healthz"
1819
)
1920

2021
type UrlPattern struct {
@@ -56,6 +57,7 @@ type Config struct {
5657
DbPath string `json:"dbpath"`
5758
Repos map[string]*Repo `json:"repos"`
5859
MaxConcurrentIndexers int `json:"max-concurrent-indexers"`
60+
HealthCheckURI string `json:"health-check-uri"`
5961
}
6062

6163
// SecretMessage is just like json.RawMessage but it will not
@@ -117,6 +119,10 @@ func initConfig(c *Config) {
117119
if c.MaxConcurrentIndexers == 0 {
118120
c.MaxConcurrentIndexers = defaultMaxConcurrentIndexers
119121
}
122+
123+
if c.HealthCheckURI == "" {
124+
c.HealthCheckURI = defaultHealthChekURI
125+
}
120126
}
121127

122128
func (c *Config) LoadFromFile(filename string) error {

‎ui/bindata.go

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎web/web.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package web
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"sync"
7+
8+
"github.com/etsy/hound/api"
9+
"github.com/etsy/hound/config"
10+
"github.com/etsy/hound/searcher"
11+
"github.com/etsy/hound/ui"
12+
)
13+
14+
// Server is an HTTP server that handles all
15+
// http traffic for hound. It is able to serve
16+
// some traffic before indexes are built and
17+
// then transition to all traffic afterwards.
18+
type Server struct {
19+
cfg *config.Config
20+
dev bool
21+
ch chan error
22+
23+
mux *http.ServeMux
24+
lck sync.RWMutex
25+
}
26+
27+
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
28+
if r.URL.Path == s.cfg.HealthCheckURI {
29+
fmt.Fprintln(w, "👍")
30+
return
31+
}
32+
33+
s.lck.RLock()
34+
defer s.lck.RUnlock()
35+
if m := s.mux; m != nil {
36+
m.ServeHTTP(w, r)
37+
} else {
38+
http.Error(w,
39+
"Hound is not ready.",
40+
http.StatusServiceUnavailable)
41+
}
42+
}
43+
44+
func (s *Server) serveWith(m *http.ServeMux) {
45+
s.lck.Lock()
46+
defer s.lck.Unlock()
47+
s.mux = m
48+
}
49+
50+
// Start creates a new server that will immediately start handling HTTP traffic.
51+
// The HTTP server will return 200 on the health check, but a 503 on every other
52+
// request until ServeWithIndex is called to begin serving search traffic with
53+
// the given searchers.
54+
func Start(cfg *config.Config, addr string, dev bool) *Server {
55+
ch := make(chan error)
56+
57+
s := &Server{
58+
cfg: cfg,
59+
dev: dev,
60+
ch: ch,
61+
}
62+
63+
go func() {
64+
ch <- http.ListenAndServe(addr, s)
65+
}()
66+
67+
return s
68+
}
69+
70+
// ServeWithIndex allow the server to start offering the search UI and the
71+
// search APIs operating on the given indexes.
72+
func (s *Server) ServeWithIndex(idx map[string]*searcher.Searcher) error {
73+
h, err := ui.Content(s.dev, s.cfg)
74+
if err != nil {
75+
return err
76+
}
77+
78+
m := http.NewServeMux()
79+
m.Handle("/", h)
80+
api.Setup(m, idx)
81+
82+
s.serveWith(m)
83+
84+
return <-s.ch
85+
}

0 commit comments

Comments
 (0)
Please sign in to comment.