mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
All of the changes for func logs
This commit is contained in:
128
api/logs/bolt.go
Normal file
128
api/logs/bolt.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
)
|
||||
|
||||
|
||||
type BoltLogDatastore struct {
|
||||
callLogsBucket []byte
|
||||
db *bolt.DB
|
||||
log logrus.FieldLogger
|
||||
datastore models.Datastore
|
||||
}
|
||||
|
||||
|
||||
func NewBolt(url *url.URL) (models.FnLog, error) {
|
||||
dir := filepath.Dir(url.Path)
|
||||
log := logrus.WithFields(logrus.Fields{"logdb": url.Scheme, "dir": dir})
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorln("Could not create data directory for log.db")
|
||||
return nil, err
|
||||
}
|
||||
log.WithFields(logrus.Fields{"path": url.Path}).Debug("Creating bolt log.db")
|
||||
db, err := bolt.Open(url.Path, 0655, &bolt.Options{Timeout: 1 * time.Second})
|
||||
if err != nil {
|
||||
log.WithError(err).Errorln("Error on bolt.Open")
|
||||
return nil, err
|
||||
}
|
||||
// I don't think we need a prefix here do we? Made it blank. If we do, we should call the query param "prefix" instead of bucket.
|
||||
bucketPrefix := ""
|
||||
if url.Query()["bucket"] != nil {
|
||||
bucketPrefix = url.Query()["bucket"][0]
|
||||
}
|
||||
callLogsBucketName := []byte(bucketPrefix + "call_logs")
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
for _, name := range [][]byte{callLogsBucketName} {
|
||||
_, err := tx.CreateBucketIfNotExists(name)
|
||||
if err != nil {
|
||||
log.WithError(err).WithFields(logrus.Fields{"name": name}).Error("create bucket")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Errorln("Error creating bolt buckets")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fnl := &BoltLogDatastore{
|
||||
callLogsBucket: callLogsBucketName,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
log.WithFields(logrus.Fields{"prefix": bucketPrefix, "file": url.Path}).Debug("BoltDB initialized")
|
||||
|
||||
return NewValidator(fnl), nil
|
||||
}
|
||||
|
||||
func (fnl *BoltLogDatastore) InsertLog(ctx context.Context, callID string, callLog string) error {
|
||||
log := &models.FnCallLog{
|
||||
CallID: callID,
|
||||
Log: callLog,
|
||||
}
|
||||
id := []byte(callID)
|
||||
err := fnl.db.Update(
|
||||
func(tx *bolt.Tx) error {
|
||||
bIm := tx.Bucket(fnl.callLogsBucket)
|
||||
buf, err := json.Marshal(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bIm.Put(id, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (fnl *BoltLogDatastore) GetLog(ctx context.Context, callID string) (*models.FnCallLog, error) {
|
||||
var res *models.FnCallLog
|
||||
err := fnl.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(fnl.callLogsBucket)
|
||||
v := b.Get([]byte(callID))
|
||||
if v != nil {
|
||||
fnCall := &models.FnCallLog{}
|
||||
err := json.Unmarshal(v, fnCall)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
res = fnCall
|
||||
} else {
|
||||
return models.ErrCallLogNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (fnl *BoltLogDatastore) DeleteLog(ctx context.Context, callID string) error {
|
||||
_, err := fnl.GetLog(ctx, callID)
|
||||
//means object does not exist
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
id := []byte(callID)
|
||||
err = fnl.db.Update(func(tx *bolt.Tx) error {
|
||||
bIm := tx.Bucket(fnl.callLogsBucket)
|
||||
err := bIm.Delete(id)
|
||||
return err
|
||||
})
|
||||
return err
|
||||
}
|
||||
34
api/logs/bolt_test.go
Normal file
34
api/logs/bolt_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gitlab-odx.oracle.com/odx/functions/api/datastore/bolt"
|
||||
logTesting "gitlab-odx.oracle.com/odx/functions/api/logs/testing"
|
||||
)
|
||||
|
||||
const tmpLogDb = "/tmp/func_test_log.db"
|
||||
const tmpDatastore = "/tmp/func_test_datastore.db"
|
||||
|
||||
|
||||
func TestDatastore(t *testing.T) {
|
||||
os.Remove(tmpLogDb)
|
||||
os.Remove(tmpDatastore)
|
||||
uLog, err := url.Parse("bolt://" + tmpLogDb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse url: %v", err)
|
||||
}
|
||||
uDatastore, err := url.Parse("bolt://" + tmpDatastore)
|
||||
|
||||
fnl, err := NewBolt(uLog)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create bolt log datastore: %v", err)
|
||||
}
|
||||
ds, err := bolt.New(uDatastore)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create bolt datastore: %v", err)
|
||||
}
|
||||
logTesting.Test(t, fnl, ds)
|
||||
}
|
||||
22
api/logs/log.go
Normal file
22
api/logs/log.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
)
|
||||
|
||||
func New(dbURL string) (models.FnLog, error) {
|
||||
u, err := url.Parse(dbURL)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{"url": dbURL}).Fatal("bad DB URL")
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{"db": u.Scheme}).Debug("creating new datastore")
|
||||
switch u.Scheme {
|
||||
case "bolt":
|
||||
return NewBolt(u)
|
||||
default:
|
||||
return nil, fmt.Errorf("db type not supported %v", u.Scheme)
|
||||
}
|
||||
}
|
||||
47
api/logs/mock.go
Normal file
47
api/logs/mock.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type mock struct {
|
||||
Logs map[string]*models.FnCallLog
|
||||
ds models.Datastore
|
||||
}
|
||||
|
||||
func NewMock() models.FnLog {
|
||||
return NewMockInit(nil)
|
||||
}
|
||||
|
||||
func NewMockInit(logs map[string]*models.FnCallLog) models.FnLog {
|
||||
if logs == nil {
|
||||
logs = map[string]*models.FnCallLog{}
|
||||
}
|
||||
fnl := NewValidator(&mock{logs, nil})
|
||||
return fnl
|
||||
}
|
||||
|
||||
func (m *mock) SetDatastore(ctx context.Context, ds models.Datastore) {
|
||||
m.ds = ds
|
||||
}
|
||||
|
||||
func (m *mock) InsertLog(ctx context.Context, callID string, callLog string) error {
|
||||
m.Logs[callID] = &models.FnCallLog{CallID: callID, Log:callLog}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mock) GetLog(ctx context.Context, callID string) (*models.FnCallLog, error) {
|
||||
logEntry := m.Logs[callID]
|
||||
if logEntry == nil {
|
||||
return nil, errors.New("Call log not found")
|
||||
}
|
||||
|
||||
return m.Logs[callID], nil
|
||||
}
|
||||
|
||||
func (m *mock) DeleteLog(ctx context.Context, callID string) error {
|
||||
delete(m.Logs, callID)
|
||||
return nil
|
||||
}
|
||||
91
api/logs/testing/test.go
Normal file
91
api/logs/testing/test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"gitlab-odx.oracle.com/odx/functions/api/id"
|
||||
)
|
||||
|
||||
|
||||
var testApp = &models.App{
|
||||
Name: "Test",
|
||||
}
|
||||
|
||||
var testRoute = &models.Route{
|
||||
AppName: testApp.Name,
|
||||
Path: "/test",
|
||||
Image: "funcy/hello",
|
||||
Type: "sync",
|
||||
Format: "http",
|
||||
}
|
||||
|
||||
func SetUpTestTask() *models.Task {
|
||||
task := &models.Task{}
|
||||
task.CreatedAt = strfmt.DateTime(time.Now())
|
||||
task.Status = "success"
|
||||
task.StartedAt = strfmt.DateTime(time.Now())
|
||||
task.CompletedAt = strfmt.DateTime(time.Now())
|
||||
task.AppName = testApp.Name
|
||||
task.Path = testRoute.Path
|
||||
return task
|
||||
}
|
||||
|
||||
func Test(t *testing.T, fnl models.FnLog, ds models.Datastore) {
|
||||
ctx := context.Background()
|
||||
task := SetUpTestTask()
|
||||
|
||||
t.Run("call-log-insert", func(t *testing.T) {
|
||||
task.ID = id.New().String()
|
||||
err := ds.InsertTask(ctx, task)
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertTask(ctx, &task): unexpected error `%v`", err)
|
||||
}
|
||||
err = fnl.InsertLog(ctx, task.ID, "test")
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertLog(ctx, task.ID, logText): unexpected error during inserting log `%v`", err)
|
||||
}
|
||||
})
|
||||
t.Run("call-log-insert-get", func(t *testing.T) {
|
||||
task.ID = id.New().String()
|
||||
err := ds.InsertTask(ctx, task)
|
||||
logText := "test"
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertTask(ctx, &task): unexpected error `%v`", err)
|
||||
}
|
||||
err = fnl.InsertLog(ctx, task.ID, logText)
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertLog(ctx, task.ID, logText): unexpected error during inserting log `%v`", err)
|
||||
}
|
||||
logEntry, err := fnl.GetLog(ctx, task.ID)
|
||||
if !strings.Contains(logEntry.Log, logText) {
|
||||
t.Fatalf("Test GetLog(ctx, task.ID, logText): unexpected error, log mismatch. " +
|
||||
"Expected: `%v`. Got `%v`.", logText, logEntry.Log)
|
||||
}
|
||||
})
|
||||
t.Run("call-log-insert-get-delete", func(t *testing.T) {
|
||||
task.ID = id.New().String()
|
||||
err := ds.InsertTask(ctx, task)
|
||||
logText := "test"
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertTask(ctx, &task): unexpected error `%v`", err)
|
||||
}
|
||||
err = fnl.InsertLog(ctx, task.ID, logText)
|
||||
if err != nil {
|
||||
t.Fatalf("Test InsertLog(ctx, task.ID, logText): unexpected error during inserting log `%v`", err)
|
||||
}
|
||||
logEntry, err := fnl.GetLog(ctx, task.ID)
|
||||
if !strings.Contains(logEntry.Log, logText) {
|
||||
t.Fatalf("Test GetLog(ctx, task.ID, logText): unexpected error, log mismatch. " +
|
||||
"Expected: `%v`. Got `%v`.", logText, logEntry.Log)
|
||||
}
|
||||
err = fnl.DeleteLog(ctx, task.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("Test DeleteLog(ctx, task.ID): unexpected error during deleting log `%v`", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
35
api/logs/validator.go
Normal file
35
api/logs/validator.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitlab-odx.oracle.com/odx/functions/api/models"
|
||||
)
|
||||
|
||||
type FnLog interface {
|
||||
|
||||
InsertLog(ctx context.Context, callID string, callLog string) error
|
||||
GetLog(ctx context.Context, callID string) (*models.FnCallLog, error)
|
||||
DeleteLog(ctx context.Context, callID string) error
|
||||
}
|
||||
|
||||
type validator struct {
|
||||
fnl FnLog
|
||||
}
|
||||
|
||||
func NewValidator(fnl FnLog) models.FnLog {
|
||||
return &validator{fnl}
|
||||
}
|
||||
|
||||
|
||||
func (v *validator) InsertLog(ctx context.Context, callID string, callLog string) error {
|
||||
return v.fnl.InsertLog(ctx, callID, callLog)
|
||||
}
|
||||
|
||||
func (v *validator) GetLog(ctx context.Context, callID string) (*models.FnCallLog, error) {
|
||||
return v.fnl.GetLog(ctx, callID)
|
||||
}
|
||||
|
||||
func (v *validator) DeleteLog(ctx context.Context, callID string) error {
|
||||
return v.fnl.DeleteLog(ctx, callID)
|
||||
}
|
||||
Reference in New Issue
Block a user