mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
HTTP Triggers hookup (#1086)
* Initial suypport for invoking tiggers * dupe method * tighten server constraints * runner tests not working yet * basic route tests passing * post rebase fixes * add hybrid support for trigger invoke and tests * consoloidate all hybrid evil into one place * cleanup and make triggers unique by source * fix oops with Agent * linting * review fixes
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -52,18 +51,21 @@ type ResourceProvider interface {
|
||||
|
||||
// BasicResourceProvider supplies simple objects and can be used as a base for custom resource providers
|
||||
type BasicResourceProvider struct {
|
||||
idCount uint32
|
||||
rand *rand.Rand
|
||||
}
|
||||
|
||||
// DataStoreFunc provides an instance of a data store
|
||||
type DataStoreFunc func(*testing.T) models.Datastore
|
||||
|
||||
//NewBasicResourceProvider creates a dumb resource provider that generates resources that have valid, random names (and other unique attributes)
|
||||
func NewBasicResourceProvider() ResourceProvider {
|
||||
return &BasicResourceProvider{}
|
||||
return &BasicResourceProvider{
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
}
|
||||
|
||||
func (brp *BasicResourceProvider) NextID() uint32 {
|
||||
return atomic.AddUint32(&brp.idCount, rand.Uint32())
|
||||
return brp.rand.Uint32()
|
||||
}
|
||||
|
||||
func (brp *BasicResourceProvider) DefaultCtx() context.Context {
|
||||
@@ -86,7 +88,7 @@ func (brp *BasicResourceProvider) ValidTrigger(appId, funcId string) *models.Tri
|
||||
AppID: appId,
|
||||
FnID: funcId,
|
||||
Type: "http",
|
||||
Source: "ASource",
|
||||
Source: fmt.Sprintf("/source_%09d", brp.NextID()),
|
||||
}
|
||||
|
||||
return trigger
|
||||
@@ -1241,7 +1243,24 @@ func RunTriggersTest(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
||||
t.Fatalf("expected empty trigger list and no error, but got list [%v] and err %s", triggers.Items, err)
|
||||
}
|
||||
})
|
||||
t.Run("duplicate trigger source of same type on same app", func(t *testing.T) {
|
||||
h := NewHarness(t, ctx, ds)
|
||||
defer h.Cleanup()
|
||||
app := h.GivenAppInDb(rp.ValidApp())
|
||||
fn := h.GivenFnInDb(rp.ValidFn(app.ID))
|
||||
origT := h.GivenTriggerInDb(rp.ValidTrigger(app.ID, fn.ID))
|
||||
|
||||
newT := rp.ValidTrigger(app.ID, fn.ID)
|
||||
|
||||
newT.Source = origT.Source
|
||||
|
||||
_, err := ds.InsertTrigger(ctx, newT)
|
||||
|
||||
if err != models.ErrTriggerSourceExists {
|
||||
t.Errorf("Expecting to fail with duplicate source on same app, got %s", err)
|
||||
}
|
||||
//todo ensure this doesn't apply when type is not equal
|
||||
})
|
||||
t.Run("app id not same as fn id ", func(t *testing.T) {
|
||||
h := NewHarness(t, ctx, ds)
|
||||
defer h.Cleanup()
|
||||
@@ -1532,6 +1551,39 @@ func RunTriggersTest(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
||||
})
|
||||
}
|
||||
|
||||
func RunTriggerBySourceTests(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
||||
|
||||
t.Run("http_trigger_access", func(t *testing.T) {
|
||||
ds := dsf(t)
|
||||
ctx := rp.DefaultCtx()
|
||||
t.Run("get_non_existant_trigger", func(t *testing.T) {
|
||||
_, err := ds.GetTriggerBySource(ctx, "none", "http", "source")
|
||||
if err != models.ErrTriggerNotFound {
|
||||
t.Fatalf("Expecting trigger not found, got %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("get_trigger_specific_http_route", func(t *testing.T) {
|
||||
h := NewHarness(t, ctx, ds)
|
||||
defer h.Cleanup()
|
||||
testApp := h.GivenAppInDb(rp.ValidApp())
|
||||
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
||||
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
||||
trigger, err := ds.GetTriggerBySource(ctx, testApp.ID, testTrigger.Type, testTrigger.Source)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expecting trigger, got error %s", err)
|
||||
}
|
||||
|
||||
if !trigger.Equals(testTrigger) {
|
||||
t.Errorf("Expecting trigger %#v got %#v", testTrigger, trigger)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func RunAllTests(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
||||
buf := setLogBuffer()
|
||||
defer func() {
|
||||
@@ -1544,5 +1596,6 @@ func RunAllTests(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
||||
RunRoutesTest(t, dsf, rp)
|
||||
RunFnsTest(t, dsf, rp)
|
||||
RunTriggersTest(t, dsf, rp)
|
||||
RunTriggerBySourceTests(t, dsf, rp)
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ type metricds struct {
|
||||
ds models.Datastore
|
||||
}
|
||||
|
||||
func (m *metricds) GetTriggerBySource(ctx context.Context, appId string, triggerType, source string) (*models.Trigger, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ds_get_trigger_by_source")
|
||||
defer span.End()
|
||||
return m.ds.GetTriggerBySource(ctx, appId, triggerType, source)
|
||||
}
|
||||
|
||||
func (m *metricds) GetAppID(ctx context.Context, appName string) (string, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "ds_get_app_id")
|
||||
defer span.End()
|
||||
|
||||
@@ -28,6 +28,18 @@ func NewMock() models.Datastore {
|
||||
return NewMockInit()
|
||||
}
|
||||
|
||||
var _ models.Datastore = &mock{}
|
||||
|
||||
func (m *mock) GetTriggerBySource(ctx context.Context, appId string, triggerType, source string) (*models.Trigger, error) {
|
||||
for _, t := range m.Triggers {
|
||||
if t.AppID == appId && t.Type == triggerType && t.Source == source {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, models.ErrTriggerNotFound
|
||||
}
|
||||
|
||||
// args helps break tests less if we change stuff
|
||||
func NewMockInit(args ...interface{}) models.Datastore {
|
||||
var mocker mock
|
||||
@@ -416,6 +428,12 @@ func (m *mock) InsertTrigger(ctx context.Context, trigger *models.Trigger) (*mod
|
||||
t.Name == trigger.Name) {
|
||||
return nil, models.ErrTriggerExists
|
||||
}
|
||||
|
||||
if t.AppID == trigger.AppID &&
|
||||
t.Source == trigger.Source &&
|
||||
t.Type == trigger.Type {
|
||||
return nil, models.ErrTriggerSourceExists
|
||||
}
|
||||
}
|
||||
|
||||
cl := trigger.Clone()
|
||||
|
||||
@@ -127,6 +127,8 @@ const (
|
||||
triggerSelector = `SELECT id,name,app_id,fn_id,type,source,annotations,created_at,updated_at FROM triggers`
|
||||
triggerIDSelector = triggerSelector + ` WHERE id=?`
|
||||
|
||||
triggerIDSourceSelector = triggerSelector + ` WHERE app_id=? AND type=? AND source=?`
|
||||
|
||||
EnvDBPingMaxRetries = "FN_DS_DB_PING_MAX_RETRIES"
|
||||
)
|
||||
|
||||
@@ -1185,6 +1187,8 @@ func (ds *SQLStore) InsertTrigger(ctx context.Context, newTrigger *models.Trigge
|
||||
if err := r.Scan(new(int)); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return models.ErrAppsNotFound
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1194,12 +1198,23 @@ func (ds *SQLStore) InsertTrigger(ctx context.Context, newTrigger *models.Trigge
|
||||
if err := r.Scan(&app_id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return models.ErrFnsNotFound
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if app_id != trigger.AppID {
|
||||
return models.ErrTriggerFnIDNotSameApp
|
||||
}
|
||||
|
||||
query = tx.Rebind(`SELECT 1 FROM triggers WHERE app_id=? AND type=? and source=?`)
|
||||
r = tx.QueryRowContext(ctx, query, trigger.AppID, trigger.Type, trigger.Source)
|
||||
err := r.Scan(new(int))
|
||||
if err == nil {
|
||||
return models.ErrTriggerSourceExists
|
||||
} else if err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
|
||||
query = tx.Rebind(`INSERT INTO triggers (
|
||||
id,
|
||||
name,
|
||||
@@ -1318,8 +1333,7 @@ func (ds *SQLStore) GetTriggerByID(ctx context.Context, triggerID string) (*mode
|
||||
err := row.StructScan(&trigger)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, models.ErrTriggerNotFound
|
||||
}
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1403,10 +1417,26 @@ func (ds *SQLStore) GetTriggers(ctx context.Context, filter *models.TriggerFilte
|
||||
if err == sql.ErrNoRows {
|
||||
return res, nil // no error for empty list
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ds *SQLStore) GetTriggerBySource(ctx context.Context, appId string, triggerType, source string) (*models.Trigger, error) {
|
||||
var trigger models.Trigger
|
||||
|
||||
query := ds.db.Rebind(triggerIDSourceSelector)
|
||||
row := ds.db.QueryRowxContext(ctx, query, appId, triggerType, source)
|
||||
|
||||
err := row.StructScan(&trigger)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, models.ErrTriggerNotFound
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &trigger, nil
|
||||
}
|
||||
|
||||
// Close closes the database, releasing any open resources.
|
||||
func (ds *SQLStore) Close() error {
|
||||
return ds.db.Close()
|
||||
|
||||
Reference in New Issue
Block a user