Merge pull request #424 from fnproject/call-listener

CallListener - replaces RunnerListener
This commit is contained in:
Reed Allman
2017-10-26 10:36:14 -07:00
committed by GitHub
10 changed files with 111 additions and 79 deletions

View File

@@ -15,6 +15,7 @@ import (
"github.com/fnproject/fn/api/agent/drivers/docker"
"github.com/fnproject/fn/api/agent/protocol"
"github.com/fnproject/fn/api/common"
"github.com/fnproject/fn/api/extensions"
"github.com/fnproject/fn/api/id"
"github.com/fnproject/fn/api/models"
"github.com/opentracing/opentracing-go"
@@ -110,12 +111,14 @@ type Agent interface {
// Return the http.Handler used to handle Prometheus metric requests
PromHandler() http.Handler
AddCallListener(extensions.CallListener)
}
type agent struct {
// TODO maybe these should be on GetCall? idk. was getting bloated.
mq models.MessageQueue
ds models.Datastore
mq models.MessageQueue
ds models.Datastore
callListeners []extensions.CallListener
driver drivers.Driver
@@ -207,7 +210,7 @@ func (a *agent) Submit(callI Call) error {
defer slot.Close() // notify our slot is free once we're done
// TODO Start is checking the timer now, we could do it here, too.
err = call.Start(ctx)
err = call.Start(ctx, a)
if err != nil {
a.stats.Dequeue(callI.Model().Path)
return err
@@ -231,8 +234,7 @@ func (a *agent) Submit(callI Call) error {
// TODO: we need to allocate more time to store the call + logs in case the call timed out,
// but this could put us over the timeout if the call did not reply yet (need better policy).
ctx = opentracing.ContextWithSpan(context.Background(), span)
call.End(ctx, err)
err = call.End(ctx, err, a)
return err
}

View File

@@ -29,13 +29,13 @@ type Call interface {
// etc.
// TODO Start and End can likely be unexported as they are only used in the agent,
// and on a type which is constructed in a specific agent. meh.
Start(ctx context.Context) error
Start(ctx context.Context, t callTrigger) error
// End will be called immediately after attempting a call execution,
// regardless of whether the execution failed or not. An error will be passed
// to End, which if nil indicates a successful execution. Any error returned
// from End will be returned as the error from Submit.
End(ctx context.Context, err error)
End(ctx context.Context, err error, t callTrigger) error
}
// TODO build w/o closures... lazy
@@ -278,7 +278,7 @@ type call struct {
func (c *call) Model() *models.Call { return c.Call }
func (c *call) Start(ctx context.Context) error {
func (c *call) Start(ctx context.Context, t callTrigger) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_start")
defer span.Finish()
@@ -316,22 +316,28 @@ func (c *call) Start(ctx context.Context) error {
return err // let another thread try this
}
}
err := t.fireBeforeCall(ctx, c.Model())
if err != nil {
return fmt.Errorf("BeforeCall: %v", err)
}
return nil
}
func (c *call) End(ctx context.Context, err error) {
func (c *call) End(ctx context.Context, errIn error, t callTrigger) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "agent_call_end")
defer span.Finish()
c.CompletedAt = strfmt.DateTime(time.Now())
switch err {
switch errIn {
case nil:
c.Status = "success"
case context.DeadlineExceeded:
c.Status = "timeout"
default:
// XXX (reed): should we append the error to logs? Error field?
// XXX (reed): should we append the error to logs? Error field? (TR) yes, think so, otherwise it's lost looks like?
c.Status = "error"
}
@@ -344,14 +350,21 @@ func (c *call) End(ctx context.Context, err error) {
// TODO: this should be update, really
if err := c.ds.InsertCall(ctx, c.Call); err != nil {
common.Logger(ctx).WithError(err).Error("error inserting call into datastore")
// note: Not returning err here since the job could have already finished successfully.
}
if err := c.ds.InsertLog(ctx, c.AppName, c.ID, c.stderr); err != nil {
common.Logger(ctx).WithError(err).Error("error uploading log")
// note: Not returning err here since the job could have already finished successfully.
}
// NOTE call this after InsertLog or the buffer will get reset
c.stderr.Close()
err := t.fireAfterCall(ctx, c.Model())
if err != nil {
return fmt.Errorf("AfterCall: %v", err)
}
return errIn
}
func fakeHandler(http.ResponseWriter, *http.Request, Params) {}

37
api/agent/listeners.go Normal file
View File

@@ -0,0 +1,37 @@
package agent
import (
"context"
"github.com/fnproject/fn/api/extensions"
"github.com/fnproject/fn/api/models"
)
type callTrigger interface {
fireBeforeCall(context.Context, *models.Call) error
fireAfterCall(context.Context, *models.Call) error
}
func (a *agent) AddCallListener(listener extensions.CallListener) {
a.callListeners = append(a.callListeners, listener)
}
func (a *agent) fireBeforeCall(ctx context.Context, call *models.Call) error {
for _, l := range a.callListeners {
err := l.BeforeCall(ctx, call)
if err != nil {
return err
}
}
return nil
}
func (a *agent) fireAfterCall(ctx context.Context, call *models.Call) error {
for _, l := range a.callListeners {
err := l.AfterCall(ctx, call)
if err != nil {
return err
}
}
return nil
}