mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Datastore refactor and added postgres tests (#259)
* fix apps & routes creation/update * refactor datastore and added postgres tests * added test-datastore and fixed circleci test
This commit is contained in:
@@ -72,18 +72,31 @@ func New(url *url.URL) (models.Datastore, error) {
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (ds *BoltDatastore) StoreApp(app *models.App) (*models.App, error) {
|
||||
func (ds *BoltDatastore) InsertApp(app *models.App) (*models.App, error) {
|
||||
if app == nil {
|
||||
return nil, models.ErrDatastoreEmptyApp
|
||||
}
|
||||
|
||||
if app.Name == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
appname := []byte(app.Name)
|
||||
|
||||
err := ds.db.Update(func(tx *bolt.Tx) error {
|
||||
bIm := tx.Bucket(ds.appsBucket)
|
||||
|
||||
v := bIm.Get(appname)
|
||||
if v != nil {
|
||||
return models.ErrAppsAlreadyExists
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bIm.Put([]byte(app.Name), buf)
|
||||
|
||||
err = bIm.Put(appname, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -94,6 +107,62 @@ func (ds *BoltDatastore) StoreApp(app *models.App) (*models.App, error) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return app, err
|
||||
}
|
||||
|
||||
func (ds *BoltDatastore) UpdateApp(newapp *models.App) (*models.App, error) {
|
||||
if newapp == nil {
|
||||
return nil, models.ErrDatastoreEmptyApp
|
||||
}
|
||||
|
||||
if newapp.Name == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
var app *models.App
|
||||
appname := []byte(newapp.Name)
|
||||
|
||||
err := ds.db.Update(func(tx *bolt.Tx) error {
|
||||
bIm := tx.Bucket(ds.appsBucket)
|
||||
|
||||
v := bIm.Get(appname)
|
||||
if v == nil {
|
||||
return models.ErrAppsNotFound
|
||||
}
|
||||
|
||||
err := json.Unmarshal(v, &app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update app fields
|
||||
if newapp.Config != nil {
|
||||
if app.Config == nil {
|
||||
app.Config = map[string]string{}
|
||||
}
|
||||
for k, v := range newapp.Config {
|
||||
app.Config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bIm.Put(appname, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bjParent := tx.Bucket(ds.routesBucket)
|
||||
_, err = bjParent.CreateBucketIfNotExists([]byte(app.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return app, err
|
||||
}
|
||||
|
||||
@@ -181,23 +250,114 @@ func (ds *BoltDatastore) getRouteBucketForApp(tx *bolt.Tx, appName string) (*bol
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (ds *BoltDatastore) StoreRoute(route *models.Route) (*models.Route, error) {
|
||||
func (ds *BoltDatastore) InsertRoute(route *models.Route) (*models.Route, error) {
|
||||
if route == nil {
|
||||
return nil, models.ErrDatastoreEmptyApp
|
||||
}
|
||||
|
||||
if route.AppName == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
if route.Path == "" {
|
||||
return nil, models.ErrDatastoreEmptyRoutePath
|
||||
}
|
||||
|
||||
routePath := []byte(route.Path)
|
||||
|
||||
err := ds.db.Update(func(tx *bolt.Tx) error {
|
||||
b, err := ds.getRouteBucketForApp(tx, route.AppName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := b.Get(routePath)
|
||||
if v != nil {
|
||||
return models.ErrRoutesAlreadyExists
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(route)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.Put([]byte(route.Path), buf)
|
||||
err = b.Put(routePath, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return route, nil
|
||||
}
|
||||
|
||||
func (ds *BoltDatastore) UpdateRoute(newroute *models.Route) (*models.Route, error) {
|
||||
if newroute == nil {
|
||||
return nil, models.ErrDatastoreEmptyRoute
|
||||
}
|
||||
|
||||
if newroute.AppName == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
if newroute.Path == "" {
|
||||
return nil, models.ErrDatastoreEmptyRoutePath
|
||||
}
|
||||
|
||||
routePath := []byte(newroute.Path)
|
||||
|
||||
var route *models.Route
|
||||
|
||||
err := ds.db.Update(func(tx *bolt.Tx) error {
|
||||
b, err := ds.getRouteBucketForApp(tx, newroute.AppName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := b.Get(routePath)
|
||||
if v == nil {
|
||||
return models.ErrRoutesNotFound
|
||||
}
|
||||
|
||||
err = json.Unmarshal(v, &route)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Update route fields
|
||||
if newroute.Image != "" {
|
||||
route.Image = newroute.Image
|
||||
}
|
||||
if route.Memory != 0 {
|
||||
route.Memory = newroute.Memory
|
||||
}
|
||||
if route.Type != "" {
|
||||
route.Type = newroute.Type
|
||||
}
|
||||
if newroute.Headers != nil {
|
||||
if route.Config == nil {
|
||||
route.Config = map[string]string{}
|
||||
}
|
||||
for k, v := range newroute.Headers {
|
||||
route.Headers[k] = v
|
||||
}
|
||||
}
|
||||
if newroute.Config != nil {
|
||||
if route.Config == nil {
|
||||
route.Config = map[string]string{}
|
||||
}
|
||||
for k, v := range newroute.Config {
|
||||
route.Config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(route)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.Put(routePath, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,15 +21,16 @@ func setLogBuffer() *bytes.Buffer {
|
||||
return &buf
|
||||
}
|
||||
|
||||
const tmpBolt = "/tmp/func_test_bolt.db"
|
||||
|
||||
func TestBolt(t *testing.T) {
|
||||
buf := setLogBuffer()
|
||||
|
||||
const tmpBolt = "/tmp/func_test_bolt.db"
|
||||
os.Remove(tmpBolt)
|
||||
ds, err := New("bolt://" + tmpBolt)
|
||||
if err != nil {
|
||||
t.Fatal("Error when creating datastore: %s", err)
|
||||
t.Fatalf("Error when creating datastore: %v", err)
|
||||
}
|
||||
defer os.Remove(tmpBolt)
|
||||
|
||||
testApp := &models.App{
|
||||
Name: "Test",
|
||||
@@ -41,24 +42,47 @@ func TestBolt(t *testing.T) {
|
||||
Image: "iron/hello",
|
||||
}
|
||||
|
||||
// Testing store app
|
||||
_, err = ds.StoreApp(nil)
|
||||
if err == nil {
|
||||
// Testing insert app
|
||||
_, err = ds.InsertApp(nil)
|
||||
if err != models.ErrDatastoreEmptyApp {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test StoreApp: expected error when using nil app", err)
|
||||
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyApp, err)
|
||||
}
|
||||
|
||||
_, err = ds.StoreApp(testApp)
|
||||
_, err = ds.InsertApp(&models.App{})
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertApp(testApp)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test StoreApp: error when Bolt was storing new app: %s", err)
|
||||
t.Fatalf("Test InsertApp: error when Bolt was storing new app: %s", err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertApp(testApp)
|
||||
if err != models.ErrAppsAlreadyExists {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp duplicated: expected error `%v`, but it was `%v`", models.ErrAppsAlreadyExists, err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateApp(&models.App{
|
||||
Name: testApp.Name,
|
||||
Config: map[string]string{
|
||||
"TEST": "1",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateApp: error when Bolt was updating app: %v", err)
|
||||
}
|
||||
|
||||
// Testing get app
|
||||
_, err = ds.GetApp("")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApp: expected error when using empty app name", err)
|
||||
t.Fatalf("Test GetApp: expected error to be %v, but it was %s", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
app, err := ds.GetApp(testApp.Name)
|
||||
@@ -75,7 +99,7 @@ func TestBolt(t *testing.T) {
|
||||
apps, err := ds.GetApps(&models.AppFilter{})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApps: error: %s", err)
|
||||
t.Fatalf("Test GetApps: unexpected error %v", err)
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
t.Fatal("Test GetApps: expected result count to be greater than 0")
|
||||
@@ -87,9 +111,9 @@ func TestBolt(t *testing.T) {
|
||||
|
||||
// Testing app delete
|
||||
err = ds.RemoveApp("")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: expected error when using empty app name", err)
|
||||
t.Fatalf("Test RemoveApp: expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveApp(testApp.Name)
|
||||
@@ -107,39 +131,63 @@ func TestBolt(t *testing.T) {
|
||||
t.Fatalf("Test RemoveApp: failed to remove the app")
|
||||
}
|
||||
|
||||
// Store app again to test routes
|
||||
ds.StoreApp(testApp)
|
||||
|
||||
// Testing store route
|
||||
_, err = ds.StoreRoute(nil)
|
||||
if err == nil {
|
||||
// Test update inexistent app
|
||||
_, err = ds.UpdateApp(&models.App{
|
||||
Name: testApp.Name,
|
||||
Config: map[string]string{
|
||||
"TEST": "1",
|
||||
},
|
||||
})
|
||||
if err != models.ErrAppsNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test StoreRoute: expected error when using nil route", err)
|
||||
t.Fatalf("Test UpdateApp inexistent: expected error to be %v, but it was %v", models.ErrAppsNotFound, err)
|
||||
}
|
||||
|
||||
_, err = ds.StoreRoute(testRoute)
|
||||
// Insert app again to test routes
|
||||
ds.InsertApp(testApp)
|
||||
|
||||
// Testing insert route
|
||||
_, err = ds.InsertRoute(nil)
|
||||
if err == models.ErrDatastoreEmptyRoute {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertRoute(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoute, err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertRoute(testRoute)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test StoreReoute: error when Bolt was storing new route: %s", err)
|
||||
t.Fatalf("Test InsertRoute: error when Bolt was storing new route: %s", err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertRoute(testRoute)
|
||||
if err != models.ErrRoutesAlreadyExists {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertRoute duplicated: expected error to be `%v`, but it was `%v`", models.ErrRoutesAlreadyExists, err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateRoute(testRoute)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Testing get
|
||||
_, err = ds.GetRoute("a", "")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyRoutePath {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: expected error when using empty route name", err)
|
||||
t.Fatalf("Test GetRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
|
||||
}
|
||||
|
||||
_, err = ds.GetRoute("", "a")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: expected error when using empty app name", err)
|
||||
t.Fatalf("Test GetRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
route, err := ds.GetRoute(testApp.Name, testRoute.Path)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: error: %s", err)
|
||||
t.Fatalf("Test GetRoute: unexpected error %v", err)
|
||||
}
|
||||
if route.Path != testRoute.Path {
|
||||
t.Log(buf.String())
|
||||
@@ -150,7 +198,7 @@ func TestBolt(t *testing.T) {
|
||||
routes, err := ds.GetRoutesByApp(testApp.Name, &models.RouteFilter{})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoutes: error: %s", err)
|
||||
t.Fatalf("Test GetRoutes: unexpected error %v", err)
|
||||
}
|
||||
if len(routes) == 0 {
|
||||
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
|
||||
@@ -176,21 +224,31 @@ func TestBolt(t *testing.T) {
|
||||
|
||||
// Testing app delete
|
||||
err = ds.RemoveRoute("", "")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveRoute: expected error when using empty app name", err)
|
||||
t.Fatalf("Test RemoveRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveRoute("a", "")
|
||||
if err == nil {
|
||||
if err != models.ErrDatastoreEmptyRoutePath {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveRoute: expected error when using empty route name", err)
|
||||
t.Fatalf("Test RemoveRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveRoute(testRoute.AppName, testRoute.Path)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: error: %s", err)
|
||||
t.Fatalf("Test RemoveApp: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateRoute(&models.Route{
|
||||
AppName: testRoute.AppName,
|
||||
Path: testRoute.Path,
|
||||
Image: "test",
|
||||
})
|
||||
if err != models.ErrRoutesNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateRoute inexistent: expected error to be `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
|
||||
}
|
||||
|
||||
route, err = ds.GetRoute(testRoute.AppName, testRoute.Path)
|
||||
|
||||
@@ -27,7 +27,12 @@ func (m *Mock) GetApps(appFilter *models.AppFilter) ([]*models.App, error) {
|
||||
return m.FakeApps, nil
|
||||
}
|
||||
|
||||
func (m *Mock) StoreApp(app *models.App) (*models.App, error) {
|
||||
func (m *Mock) InsertApp(app *models.App) (*models.App, error) {
|
||||
// TODO: improve this mock method
|
||||
return m.FakeApp, nil
|
||||
}
|
||||
|
||||
func (m *Mock) UpdateApp(app *models.App) (*models.App, error) {
|
||||
// TODO: improve this mock method
|
||||
return m.FakeApp, nil
|
||||
}
|
||||
@@ -69,7 +74,12 @@ func (m *Mock) GetRoutesByApp(appName string, routeFilter *models.RouteFilter) (
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func (m *Mock) StoreRoute(route *models.Route) (*models.Route, error) {
|
||||
func (m *Mock) InsertRoute(route *models.Route) (*models.Route, error) {
|
||||
// TODO: improve this mock method
|
||||
return m.FakeRoute, nil
|
||||
}
|
||||
|
||||
func (m *Mock) UpdateRoute(route *models.Route) (*models.Route, error) {
|
||||
// TODO: improve this mock method
|
||||
return m.FakeRoute, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/iron-io/functions/api/models"
|
||||
"github.com/lib/pq"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
@@ -75,17 +76,56 @@ func New(url *url.URL) (models.Datastore, error) {
|
||||
return pg, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) StoreApp(app *models.App) (*models.App, error) {
|
||||
func (ds *PostgresDatastore) InsertApp(app *models.App) (*models.App, error) {
|
||||
var cbyte []byte
|
||||
var err error
|
||||
|
||||
if app == nil {
|
||||
return nil, models.ErrDatastoreEmptyApp
|
||||
}
|
||||
|
||||
if app.Name == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
if app.Config != nil {
|
||||
cbyte, err = json.Marshal(app.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = ds.db.Exec(`INSERT INTO apps (name, config) VALUES ($1, $2);`,
|
||||
app.Name,
|
||||
string(cbyte),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pqErr := err.(*pq.Error)
|
||||
if pqErr.Code == "23505" {
|
||||
return nil, models.ErrAppsAlreadyExists
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) UpdateApp(app *models.App) (*models.App, error) {
|
||||
if app == nil {
|
||||
return nil, models.ErrAppsNotFound
|
||||
}
|
||||
|
||||
cbyte, err := json.Marshal(app.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = ds.db.Exec(`
|
||||
INSERT INTO apps (name, config)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (name) DO UPDATE SET
|
||||
config = $2;
|
||||
res, err := ds.db.Exec(`
|
||||
UPDATE apps SET
|
||||
config = $2
|
||||
WHERE name = $1
|
||||
RETURNING *;
|
||||
`,
|
||||
app.Name,
|
||||
string(cbyte),
|
||||
@@ -95,10 +135,23 @@ func (ds *PostgresDatastore) StoreApp(app *models.App) (*models.App, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil, models.ErrAppsNotFound
|
||||
}
|
||||
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) RemoveApp(appName string) error {
|
||||
if appName == "" {
|
||||
return models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
_, err := ds.db.Exec(`
|
||||
DELETE FROM apps
|
||||
WHERE name = $1
|
||||
@@ -112,6 +165,10 @@ func (ds *PostgresDatastore) RemoveApp(appName string) error {
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) GetApp(name string) (*models.App, error) {
|
||||
if name == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
row := ds.db.QueryRow("SELECT name, config FROM apps WHERE name=$1", name)
|
||||
|
||||
var resName string
|
||||
@@ -125,6 +182,9 @@ func (ds *PostgresDatastore) GetApp(name string) (*models.App, error) {
|
||||
json.Unmarshal([]byte(config), &res.Config)
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, models.ErrAppsNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -132,10 +192,19 @@ func (ds *PostgresDatastore) GetApp(name string) (*models.App, error) {
|
||||
}
|
||||
|
||||
func scanApp(scanner rowScanner, app *models.App) error {
|
||||
var configStr string
|
||||
|
||||
err := scanner.Scan(
|
||||
&app.Name,
|
||||
&configStr,
|
||||
)
|
||||
|
||||
if configStr == "" {
|
||||
return models.ErrAppsNotFound
|
||||
}
|
||||
|
||||
json.Unmarshal([]byte(configStr), &app.Config)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -156,18 +225,25 @@ func (ds *PostgresDatastore) GetApps(filter *models.AppFilter) ([]*models.App, e
|
||||
err := scanApp(rows, &app)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == sql.ErrNoRows {
|
||||
return res, nil
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
res = append(res, &app)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, error) {
|
||||
func (ds *PostgresDatastore) InsertRoute(route *models.Route) (*models.Route, error) {
|
||||
if route == nil {
|
||||
return nil, models.ErrDatastoreEmptyRoute
|
||||
}
|
||||
|
||||
hbyte, err := json.Marshal(route.Headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -187,14 +263,47 @@ func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, err
|
||||
headers,
|
||||
config
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (app_name, path) DO UPDATE SET
|
||||
path = $2,
|
||||
VALUES ($1, $2, $3, $4, $5, $6);`,
|
||||
route.AppName,
|
||||
route.Path,
|
||||
route.Image,
|
||||
route.Memory,
|
||||
string(hbyte),
|
||||
string(cbyte),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
pqErr := err.(*pq.Error)
|
||||
if pqErr.Code == "23505" {
|
||||
return nil, models.ErrRoutesAlreadyExists
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return route, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) UpdateRoute(route *models.Route) (*models.Route, error) {
|
||||
if route == nil {
|
||||
return nil, models.ErrDatastoreEmptyRoute
|
||||
}
|
||||
|
||||
hbyte, err := json.Marshal(route.Headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbyte, err := json.Marshal(route.Config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := ds.db.Exec(`
|
||||
UPDATE routes SET
|
||||
image = $3,
|
||||
memory = $4,
|
||||
headers = $5,
|
||||
config = $6;
|
||||
`,
|
||||
config = $6
|
||||
WHERE app_name = $1 AND path = $2;`,
|
||||
route.AppName,
|
||||
route.Path,
|
||||
route.Image,
|
||||
@@ -206,11 +315,29 @@ func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil, models.ErrRoutesNotFound
|
||||
}
|
||||
|
||||
return route, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) RemoveRoute(appName, routePath string) error {
|
||||
_, err := ds.db.Exec(`
|
||||
if appName == "" {
|
||||
return models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
if routePath == "" {
|
||||
return models.ErrDatastoreEmptyRoutePath
|
||||
}
|
||||
|
||||
res, err := ds.db.Exec(`
|
||||
DELETE FROM routes
|
||||
WHERE path = $1 AND app_name = $2
|
||||
`, routePath, appName)
|
||||
@@ -218,6 +345,16 @@ func (ds *PostgresDatastore) RemoveRoute(appName, routePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return models.ErrRoutesRemoving
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -244,10 +381,18 @@ func scanRoute(scanner rowScanner, route *models.Route) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func getRoute(qr rowQuerier, routePath string) (*models.Route, error) {
|
||||
func (ds *PostgresDatastore) GetRoute(appName, routePath string) (*models.Route, error) {
|
||||
if appName == "" {
|
||||
return nil, models.ErrDatastoreEmptyAppName
|
||||
}
|
||||
|
||||
if routePath == "" {
|
||||
return nil, models.ErrDatastoreEmptyRoutePath
|
||||
}
|
||||
|
||||
var route models.Route
|
||||
|
||||
row := qr.QueryRow(fmt.Sprintf("%s WHERE path=$1", routeSelector), routePath)
|
||||
row := ds.db.QueryRow(fmt.Sprintf("%s WHERE app_name=$1 AND path=$2", routeSelector), appName, routePath)
|
||||
err := scanRoute(row, &route)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -258,10 +403,6 @@ func getRoute(qr rowQuerier, routePath string) (*models.Route, error) {
|
||||
return &route, nil
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) GetRoute(appName, routePath string) (*models.Route, error) {
|
||||
return getRoute(ds.db, routePath)
|
||||
}
|
||||
|
||||
func (ds *PostgresDatastore) GetRoutes(filter *models.RouteFilter) ([]*models.Route, error) {
|
||||
res := []*models.Route{}
|
||||
filterQuery := buildFilterQuery(filter)
|
||||
|
||||
277
api/datastore/postgres_test.go
Normal file
277
api/datastore/postgres_test.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/iron-io/functions/api/models"
|
||||
)
|
||||
|
||||
const tmpPostgres = "postgres://postgres@127.0.0.1:15432/funcs?sslmode=disable"
|
||||
|
||||
func preparePostgresTest() func() {
|
||||
fmt.Println("initializing postgres for test")
|
||||
exec.Command("docker", "rm", "-f", "iron-postgres-test").Run()
|
||||
exec.Command("docker", "run", "--name", "iron-postgres-test", "-p", "15432:5432", "-d", "postgres").Run()
|
||||
for {
|
||||
db, err := sql.Open("postgres", "postgres://postgres@127.0.0.1:15432?sslmode=disable")
|
||||
if err != nil {
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
db.Exec(`CREATE DATABASE funcs;`)
|
||||
_, err = db.Exec(`GRANT ALL PRIVILEGES ON DATABASE funcs TO postgres;`)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
fmt.Println("postgres for test ready")
|
||||
return func() {
|
||||
exec.Command("docker", "rm", "-f", "iron-postgres-test").Run()
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgres(t *testing.T) {
|
||||
close := preparePostgresTest()
|
||||
defer close()
|
||||
buf := setLogBuffer()
|
||||
|
||||
ds, err := New(tmpPostgres)
|
||||
if err != nil {
|
||||
t.Fatalf("Error when creating datastore: %v", err)
|
||||
}
|
||||
|
||||
testApp := &models.App{
|
||||
Name: "Test",
|
||||
}
|
||||
|
||||
testRoute := &models.Route{
|
||||
AppName: testApp.Name,
|
||||
Path: "/test",
|
||||
Image: "iron/hello",
|
||||
}
|
||||
|
||||
// Testing insert app
|
||||
_, err = ds.InsertApp(nil)
|
||||
if err != models.ErrDatastoreEmptyApp {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyApp, err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertApp(&models.App{})
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertApp(testApp)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp: error when storing new app: %s", err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertApp(testApp)
|
||||
if err != models.ErrAppsAlreadyExists {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertApp duplicated: expected error `%v`, but it was `%v`", models.ErrAppsAlreadyExists, err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateApp(&models.App{
|
||||
Name: testApp.Name,
|
||||
Config: map[string]string{
|
||||
"TEST": "1",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateApp: error when updating app: %v", err)
|
||||
}
|
||||
|
||||
// Testing get app
|
||||
_, err = ds.GetApp("")
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApp: expected error to be %v, but it was %s", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
app, err := ds.GetApp(testApp.Name)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApp: error: %s", err)
|
||||
}
|
||||
if app.Name != testApp.Name {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApp: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
|
||||
}
|
||||
|
||||
// Testing list apps
|
||||
apps, err := ds.GetApps(&models.AppFilter{})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApps: unexpected error %v", err)
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
t.Fatal("Test GetApps: expected result count to be greater than 0")
|
||||
}
|
||||
if apps[0].Name != testApp.Name {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApps: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
|
||||
}
|
||||
|
||||
// Testing app delete
|
||||
err = ds.RemoveApp("")
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveApp(testApp.Name)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: error: %s", err)
|
||||
}
|
||||
app, err = ds.GetApp(testApp.Name)
|
||||
if err != models.ErrAppsNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetApp(removed): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
||||
}
|
||||
if app != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: failed to remove the app")
|
||||
}
|
||||
|
||||
// Test update inexistent app
|
||||
_, err = ds.UpdateApp(&models.App{
|
||||
Name: testApp.Name,
|
||||
Config: map[string]string{
|
||||
"TEST": "1",
|
||||
},
|
||||
})
|
||||
if err != models.ErrAppsNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateApp(inexistent): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
||||
}
|
||||
|
||||
// Insert app again to test routes
|
||||
ds.InsertApp(testApp)
|
||||
|
||||
// Testing insert route
|
||||
_, err = ds.InsertRoute(nil)
|
||||
if err != models.ErrDatastoreEmptyRoute {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertRoute(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoute, err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertRoute(testRoute)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertRoute: error when storing new route: %s", err)
|
||||
}
|
||||
|
||||
_, err = ds.InsertRoute(testRoute)
|
||||
if err != models.ErrRoutesAlreadyExists {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test InsertRoute duplicated: expected error to be `%v`, but it was `%v`", models.ErrRoutesAlreadyExists, err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateRoute(testRoute)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Testing get
|
||||
_, err = ds.GetRoute("a", "")
|
||||
if err != models.ErrDatastoreEmptyRoutePath {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
|
||||
}
|
||||
|
||||
_, err = ds.GetRoute("", "a")
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
route, err := ds.GetRoute(testApp.Name, testRoute.Path)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: unexpected error %v", err)
|
||||
}
|
||||
if route.Path != testRoute.Path {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: expected `route.Path` to be `%s` but it was `%s`", route.Path, testRoute.Path)
|
||||
}
|
||||
|
||||
// Testing list routes
|
||||
routes, err := ds.GetRoutesByApp(testApp.Name, &models.RouteFilter{})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoutes: unexpected error %v", err)
|
||||
}
|
||||
if len(routes) == 0 {
|
||||
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
|
||||
}
|
||||
if routes[0].Path != testRoute.Path {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
|
||||
}
|
||||
|
||||
// Testing list routes
|
||||
routes, err = ds.GetRoutes(&models.RouteFilter{Image: testRoute.Image})
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoutes: error: %s", err)
|
||||
}
|
||||
if len(routes) == 0 {
|
||||
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
|
||||
}
|
||||
if routes[0].Path != testRoute.Path {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
|
||||
}
|
||||
|
||||
// Testing app delete
|
||||
err = ds.RemoveRoute("", "")
|
||||
if err != models.ErrDatastoreEmptyAppName {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveRoute("a", "")
|
||||
if err != models.ErrDatastoreEmptyRoutePath {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
|
||||
}
|
||||
|
||||
err = ds.RemoveRoute(testRoute.AppName, testRoute.Path)
|
||||
if err != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
_, err = ds.UpdateRoute(&models.Route{
|
||||
AppName: testRoute.AppName,
|
||||
Path: testRoute.Path,
|
||||
Image: "test",
|
||||
})
|
||||
if err != models.ErrRoutesNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test UpdateRoute inexistent: expected error to be `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
|
||||
}
|
||||
|
||||
route, err = ds.GetRoute(testRoute.AppName, testRoute.Path)
|
||||
if err != models.ErrRoutesNotFound {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test GetRoute: expected error `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
|
||||
}
|
||||
if route != nil {
|
||||
t.Log(buf.String())
|
||||
t.Fatalf("Test RemoveApp: failed to remove the route")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user