-
Notifications
You must be signed in to change notification settings - Fork 15
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
Export Consumer.Frozen and add http introspection handler #79
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 |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package httputil | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
// Consumer contains just the Metafora methods exposed by the HTTP | ||
// introspection endpoints. | ||
type Consumer interface { | ||
Frozen() bool | ||
Tasks() []string | ||
} | ||
|
||
// InfoResponse is the JSON response marshalled by the MakeInfoHandler. | ||
type InfoResponse struct { | ||
Frozen bool `json:"frozen"` | ||
Node string `json:"node"` | ||
Started time.Time `json:"started"` | ||
Tasks []string `json:"tasks"` | ||
} | ||
|
||
// MakeInfoHandler returns an HTTP handler which can be added to an exposed | ||
// HTTP server mux by Metafora applications to provide operators with basic | ||
// node introspection. | ||
func MakeInfoHandler(c Consumer, node string, started time.Time) http.HandlerFunc { | ||
return func(w http.ResponseWriter, _ *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. Could the 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. Good question. For better or worse (I honestly don't know), the node name is an etcd Coordinator-only concept. It's not used or exposed anywhere in the core Consumer or interfaces. A better idea might be to make the name a Consumer level concept, and the Consumer informs the Coordinator of its name via the Seems out of scope for this PR, so I created #80. Further comments welcome! I think it's a good enhancement. |
||
w.Header().Set("Content-Type", "application/json") | ||
json.NewEncoder(w).Encode(&InfoResponse{ | ||
Frozen: c.Frozen(), | ||
Node: node, | ||
Started: started, | ||
Tasks: c.Tasks(), | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package httputil_test | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http/httptest" | ||
"testing" | ||
"time" | ||
|
||
"github.com/lytics/metafora" | ||
. "github.com/lytics/metafora/httputil" | ||
) | ||
|
||
type tc struct { | ||
stop chan bool | ||
} | ||
|
||
func (*tc) Init(metafora.CoordinatorContext) error { return nil } | ||
func (c *tc) Watch() (string, error) { | ||
<-c.stop | ||
return "", nil | ||
} | ||
func (c *tc) Claim(string) bool { return false } | ||
func (c *tc) Release(string) {} | ||
func (c *tc) Done(string) {} | ||
func (c *tc) Command() (metafora.Command, error) { | ||
<-c.stop | ||
return nil, nil | ||
} | ||
func (c *tc) Close() { close(c.stop) } | ||
|
||
func TestMakeInfoHandler(t *testing.T) { | ||
t.Parallel() | ||
|
||
c, _ := metafora.NewConsumer(&tc{stop: make(chan bool)}, nil, &metafora.DumbBalancer{}) | ||
defer c.Shutdown() | ||
name := "test-name" | ||
now := time.Now().Truncate(time.Second) | ||
|
||
resp := httptest.NewRecorder() | ||
MakeInfoHandler(c, name, now)(resp, nil) | ||
|
||
info := InfoResponse{} | ||
if err := json.Unmarshal(resp.Body.Bytes(), &info); err != nil { | ||
t.Fatalf("Error unmarshalling response body: %v", err) | ||
} | ||
if info.Frozen { | ||
t.Errorf("Consumer should not start frozen.") | ||
} | ||
if !info.Started.Equal(now) { | ||
t.Errorf("Started time %s != %s", info.Started, now) | ||
} | ||
if info.Node != name { | ||
t.Errorf("Node name %s != %s", info.Node, name) | ||
} | ||
if len(info.Tasks) != 0 { | ||
t.Errorf("Unexpected tasks: %v", info.Tasks) | ||
} | ||
} |
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.
Why Consumer for a name? Im not sure that describes the interface's relationship to the metafora project. From the code it appears to be a metadata provider for the current metafora peer. Anyway, not a blocker.
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 think I understand the name now... Its a
consumed
by the http handler.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.
Eh, I wasn't even that clever... I just stole the name from metafora.go where it's implemented. I'll give it a few minutes of thought to try to come up with a better interface name before hitting the merge button. You know how I love making things idiomatic! :)