-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a "status" endpoint for health checks #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
|
@@ -15,7 +16,6 @@ import ( | |
"os" | ||
"regexp" | ||
"strings" | ||
"sync" | ||
"time" | ||
) | ||
|
||
|
@@ -45,10 +45,13 @@ type Config struct { | |
Environment map[string]*EnvConfig | ||
} | ||
|
||
type StatusEntry struct { | ||
Status string `json:"status"` | ||
} | ||
|
||
var configFile string | ||
|
||
func main() { | ||
var wg sync.WaitGroup | ||
|
||
flag.StringVar(&configFile, "config", "/etc/ld-relay.conf", "configuration file location") | ||
|
||
|
@@ -74,17 +77,23 @@ func main() { | |
|
||
handlers := cmap.New() | ||
|
||
wg.Add(len(c.Environment)) | ||
clients := cmap.New() | ||
|
||
for envName, _ := range c.Environment { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pre-seeds the map with all the configured environments. |
||
clients.Set(envName, nil) | ||
} | ||
|
||
for envName, envConfig := range c.Environment { | ||
go func(envName string, envConfig EnvConfig) { | ||
defer wg.Done() | ||
clientConfig := ld.DefaultConfig | ||
clientConfig.Stream = true | ||
clientConfig.FeatureStore = NewSSERelayFeatureStore(envConfig.ApiKey, publisher, c.Main.HeartbeatIntervalSecs) | ||
clientConfig.StreamUri = c.Main.StreamUri | ||
clientConfig.BaseUri = c.Main.BaseUri | ||
|
||
_, err := ld.MakeCustomClient(envConfig.ApiKey, clientConfig, time.Second*10) | ||
client, err := ld.MakeCustomClient(envConfig.ApiKey, clientConfig, time.Second*10) | ||
|
||
clients.Set(envName, client) | ||
|
||
if err != nil { | ||
Error.Printf("Error initializing LaunchDarkly client for %s: %+v\n", envName, err) | ||
|
@@ -101,7 +110,39 @@ func main() { | |
}(envName, *envConfig) | ||
} | ||
|
||
wg.Wait() | ||
http.Handle("/status", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to make the port configurable? i assume it will be listening on port 80 by default.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is-- this isn't where that's handled though. See where the configuration port is usdd |
||
w.Header().Set("Content-Type", "application/json") | ||
envs := make(map[string]StatusEntry) | ||
|
||
healthy := true | ||
for item := range clients.IterBuffered() { | ||
if item.Val == nil { | ||
envs[item.Key] = StatusEntry{Status: "disconnected"} | ||
healthy = false | ||
} else { | ||
client := item.Val.(*ld.LDClient) | ||
if client.Initialized() { | ||
envs[item.Key] = StatusEntry{Status: "connected"} | ||
} else { | ||
envs[item.Key] = StatusEntry{Status: "disconnected"} | ||
healthy = false | ||
} | ||
} | ||
} | ||
|
||
resp := make(map[string]interface{}) | ||
|
||
resp["environments"] = envs | ||
if healthy { | ||
resp["status"] = "healthy" | ||
} else { | ||
resp["status"] = "degraded" | ||
} | ||
|
||
data, _ := json.Marshal(resp) | ||
|
||
w.Write(data) | ||
})) | ||
|
||
// Now make a single handler that dispatches to the appropriate handler based on the Authorization header | ||
http.Handle("/features", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got rid of the wait group, which allows the server to start before all of the environments have been initialized. This makes the whole system a little more resilient-- it can begin serving requests if (e.g.) one environment can't connect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will happen if a connection comes in for an environment that is not yet initialized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The corresponding client won't yet be in the handler map, so you'll get a 404.