Skip to content
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

Merged
merged 1 commit into from
Jul 22, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions ld-relay.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
Expand All @@ -15,7 +16,6 @@ import (
"os"
"regexp"
"strings"
"sync"
"time"
)

Expand Down Expand Up @@ -45,10 +45,13 @@ type Config struct {
Environment map[string]*EnvConfig
}

type StatusEntry struct {
Status string `json:"status"`
}

var configFile string

func main() {
Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

var wg sync.WaitGroup

flag.StringVar(&configFile, "config", "/etc/ld-relay.conf", "configuration file location")

Expand All @@ -74,17 +77,23 @@ func main() {

handlers := cmap.New()

wg.Add(len(c.Environment))
clients := cmap.New()

for envName, _ := range c.Environment {
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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)
Expand All @@ -101,7 +110,39 @@ func main() {
}(envName, *envConfig)
}

wg.Wait()
http.Handle("/status", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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..

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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) {
Expand Down