mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Merge pull request #25 from pedronasser/api-fix
Added wrappers; API fixes and swagger
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ vendor/
|
|||||||
/gateway
|
/gateway
|
||||||
/functions
|
/functions
|
||||||
bolt.db
|
bolt.db
|
||||||
|
.glide/
|
||||||
|
|
||||||
private.sh
|
private.sh
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
|
## Building
|
||||||
|
|
||||||
## Building/Testing
|
First time or when a dependency changes or when the API changes, run:
|
||||||
|
```
|
||||||
Build:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# one time:
|
|
||||||
glide install
|
glide install
|
||||||
# then every time
|
|
||||||
./build.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Test it, the iron token and project id are for cache.
|
To quick build and run (using default database):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run --env-file .env --rm -it --privileged -p 8080:8080 iron/functions
|
api.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
To build the docker image:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
build.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Releasing
|
## Releasing
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./release.sh
|
release.sh
|
||||||
```
|
```
|
||||||
|
|||||||
82
README.md
82
README.md
@@ -1,54 +1,53 @@
|
|||||||
Note: currently running at: http://gateway.iron.computer:8080/
|
|
||||||
|
|
||||||
# IronFunctions
|
# IronFunctions
|
||||||
|
|
||||||
First, let's fire up an IronFunctions instance. Copy the [example.env](example.env) file into a file named `.env` and fill in the missing values.
|
## [Overview](/iron-io/functions/blob/master/OVERVIEW.md)
|
||||||
|
|
||||||
Then start your functions instance:
|
## Quick Start
|
||||||
|
|
||||||
|
First let's start our IronFunctions API
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run --env-file .env --rm -it --privileged -p 8080:8080 iron/functions
|
docker run --rm --privileged -it -p 8080:8080 iron/functions
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This command will quickly start our API using the default database `Bolt` running on `:8080`
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
First things first, create an app/service:
|
### Creating a application
|
||||||
TOOD: App or service??
|
|
||||||
|
|
||||||
### Create App
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
iron create app APP_NAME
|
curl -H "Content-Type: application/json" -X POST -d '{
|
||||||
# OR
|
"name":"APP_NAME"
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"name":"APP_NAME"}' http://localhost:8080/api/v1/apps
|
}' http://localhost:8080/v1/apps
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create a Route for your Function
|
### Create a route for your Function
|
||||||
|
|
||||||
Now add routes to the app. First we'll add a route to the output of a docker container:
|
Now add routes to the app. First we'll add a route to the output of a docker container:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
iron add route myapp /hello iron/hello
|
curl -H "Content-Type: application/json" -X POST -d '{
|
||||||
# OR
|
"name": "hello",
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/hello", "image":"iron/hello"}' http://localhost:8080/api/v1/apps/myapp/routes
|
"path":"/hello",
|
||||||
```
|
"image":"iron/hello"
|
||||||
|
}' http://localhost:8080/v1/apps/myapp/routes
|
||||||
And how about a [slackbot](https://github.com/treeder/slackbots/tree/master/guppy) too:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/guppy","image":"treeder/guppy:0.0.2", "content_type": "application/json"}' http://localhost:8080/api/v1/apps/myapp/routes
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Calling your Function
|
### Calling your Function
|
||||||
|
|
||||||
Surf to your function: http://localhost:8080/hello?app=APP_NAME . Boom!
|
```
|
||||||
|
curl http://localhost:8080/r/myapp/hello
|
||||||
|
```
|
||||||
|
|
||||||
#### To pass in data to your function,
|
### To pass in data to your function,
|
||||||
|
|
||||||
Your function will get the body of the request as is, and the headers of the request will be passed in as env vars.
|
Your function will get the body of the request as is, and the headers of the request will be passed in as env vars.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"name":"Johnny"}' http://localhost:8080/hello?app=APP_NAME
|
curl -H "Content-Type: application/json" -X POST -d '{
|
||||||
|
"name":"Johnny"
|
||||||
|
}' http://localhost:8080/r/myapp/hello
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using IronFunctions Hosted by Iron.io
|
### Using IronFunctions Hosted by Iron.io
|
||||||
@@ -56,38 +55,35 @@ curl -H "Content-Type: application/json" -X POST -d '{"name":"Johnny"}' http://l
|
|||||||
Simply point to https://functions.iron.io instead of localhost and add your Iron.io Authentication header (TODO: link), like this:
|
Simply point to https://functions.iron.io instead of localhost and add your Iron.io Authentication header (TODO: link), like this:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -H "Authorization: Bearer IRON_TOKEN" -H "Content-Type: application/json" -X POST -d '{"name":"APP_NAME"}' https://functions.iron.io/api/v1/apps
|
curl -H "Authorization: Bearer IRON_TOKEN" -H "Content-Type: application/json" -X POST -d '{"name":"APP_NAME"}' https://functions.iron.io/v1/apps
|
||||||
```
|
```
|
||||||
|
|
||||||
And you'll get an ironfunctions.com host:
|
And you'll get an ironfunctions.com host:
|
||||||
|
|
||||||
```
|
```
|
||||||
APP_NAME.ironfunctions.com/PATH
|
APP_NAME.USER_ID.ironfunctions.com/PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
### Updating Your Images
|
## Configuring your API
|
||||||
|
|
||||||
Tag your images with a version, eg `treeder/guppy:0.0.5` then use that including the tag and update
|
### Databases
|
||||||
the route.
|
|
||||||
|
|
||||||
## Examples
|
These are the current databases supported by IronFunctions:
|
||||||
|
|
||||||
TODO: Link to examples in various languages
|
- [Running with BoltDB](/iron-io/functions/blob/master/docs/database/boltdb.md)
|
||||||
TODO: Link to slackbots (easiest way to host slackbots?)
|
- [Running with Postgres](/iron-io/functions/blob/master/docs/database/postgres.md)
|
||||||
|
|
||||||
## Operations
|
## [Examples](/iron-io/functions/blob/master/examples)
|
||||||
|
|
||||||
This is info on how to run and manage IronFunctions.
|
## Logging
|
||||||
|
|
||||||
### Logging
|
|
||||||
|
|
||||||
Run logspout container on your server.
|
|
||||||
|
|
||||||
#### Monitoring
|
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
### Scaling
|
## Monitoring
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## Scaling
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
|
|||||||
5
api.sh
Executable file
5
api.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
set -ex
|
||||||
|
|
||||||
|
./build.sh
|
||||||
|
|
||||||
|
docker run --rm --privileged -it -p 8080:8080 -e LOG_LEVEL=debug -v $PWD/bolt.db:/app/bolt.db iron/functions
|
||||||
@@ -5,13 +5,14 @@ import "errors"
|
|||||||
type Apps []*App
|
type Apps []*App
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrAppsCreate = errors.New("Could not create app")
|
ErrAppsCreate = errors.New("Could not create app")
|
||||||
ErrAppsUpdate = errors.New("Could not update app")
|
ErrAppsUpdate = errors.New("Could not update app")
|
||||||
ErrAppsRemoving = errors.New("Could not remove app from datastore")
|
ErrAppsRemoving = errors.New("Could not remove app from datastore")
|
||||||
ErrAppsGet = errors.New("Could not get app from datastore")
|
ErrAppsGet = errors.New("Could not get app from datastore")
|
||||||
ErrAppsList = errors.New("Could not list apps from datastore")
|
ErrAppsList = errors.New("Could not list apps from datastore")
|
||||||
ErrAppsNotFound = errors.New("App not found")
|
ErrAppsNotFound = errors.New("App not found")
|
||||||
ErrAppNothingToUpdate = errors.New("Nothing to update")
|
ErrAppsNothingToUpdate = errors.New("Nothing to update")
|
||||||
|
ErrAppsMissingNew = errors.New("Missing new application")
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
|
|||||||
32
api/models/app_wrapper.go
Normal file
32
api/models/app_wrapper.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/go-openapi/errors"
|
||||||
|
|
||||||
|
type AppWrapper struct {
|
||||||
|
App *App `json:"app"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AppWrapper) Validate() error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateApp(); err != nil {
|
||||||
|
// prop
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AppWrapper) validateApp() error {
|
||||||
|
|
||||||
|
if m.App != nil {
|
||||||
|
if err := m.App.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
34
api/models/apps_wrapper.go
Normal file
34
api/models/apps_wrapper.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
strfmt "github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppsWrapper struct {
|
||||||
|
Apps []*App `json:"apps"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AppsWrapper) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateApps(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AppsWrapper) validateApps(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("apps", "body", m.Apps); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -17,6 +17,6 @@ func ApplyAppFilter(app *App, filter *AppFilter) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ApplyRouteFilter(route *Route, filter *RouteFilter) bool {
|
func ApplyRouteFilter(route *Route, filter *RouteFilter) bool {
|
||||||
return (filter.Path != "" && route.Path == filter.Path) &&
|
return (filter.Path == "" || route.Path == filter.Path) &&
|
||||||
(filter.AppName != "" && route.AppName == filter.AppName)
|
(filter.AppName == "" || route.AppName == filter.AppName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ func (m *Error) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidJSON = errors.New("Could not create app")
|
ErrInvalidJSON = errors.New("Invalid JSON")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,27 +3,28 @@ package models
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
apiErrors "github.com/go-openapi/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrRoutesCreate = errors.New("Could not create route")
|
ErrRoutesCreate = errors.New("Could not create route")
|
||||||
ErrRoutesUpdate = errors.New("Could not update route")
|
ErrRoutesUpdate = errors.New("Could not update route")
|
||||||
ErrRoutesRemoving = errors.New("Could not remove route from datastore")
|
ErrRoutesRemoving = errors.New("Could not remove route from datastore")
|
||||||
ErrRoutesGet = errors.New("Could not get route from datastore")
|
ErrRoutesGet = errors.New("Could not get route from datastore")
|
||||||
ErrRoutesList = errors.New("Could not list routes from datastore")
|
ErrRoutesList = errors.New("Could not list routes from datastore")
|
||||||
ErrRoutesNotFound = errors.New("Route not found")
|
ErrRoutesNotFound = errors.New("Route not found")
|
||||||
|
ErrRoutesMissingNew = errors.New("Missing new route")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Routes []*Route
|
type Routes []*Route
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
AppName string `json:"appname"`
|
AppName string `json:"appname"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Image string `json:"image"`
|
Image string `json:"image"`
|
||||||
Type string `json:"type,omitempty"`
|
Headers http.Header `json:"headers,omitempty"`
|
||||||
ContainerPath string `json:"container_path,omitempty"`
|
|
||||||
Headers http.Header `json:"headers,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -34,20 +35,26 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (r *Route) Validate() error {
|
func (r *Route) Validate() error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
if r.Name == "" {
|
if r.Name == "" {
|
||||||
return ErrRoutesValidationName
|
res = append(res, ErrRoutesValidationAppName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Image == "" {
|
if r.Image == "" {
|
||||||
return ErrRoutesValidationImage
|
res = append(res, ErrRoutesValidationImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.AppName == "" {
|
if r.AppName == "" {
|
||||||
return ErrRoutesValidationAppName
|
res = append(res, ErrRoutesValidationAppName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Path == "" {
|
if r.Path == "" {
|
||||||
return ErrRoutesValidationPath
|
res = append(res, ErrRoutesValidationPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return apiErrors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
31
api/models/route_wrapper.go
Normal file
31
api/models/route_wrapper.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/go-openapi/errors"
|
||||||
|
|
||||||
|
type RouteWrapper struct {
|
||||||
|
Route *Route `json:"route"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RouteWrapper) Validate() error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateRoute(); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RouteWrapper) validateRoute() error {
|
||||||
|
|
||||||
|
if m.Route != nil {
|
||||||
|
if err := m.Route.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
36
api/models/routes_wrapper.go
Normal file
36
api/models/routes_wrapper.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
strfmt "github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoutesWrapper struct {
|
||||||
|
Cursor string `json:"cursor,omitempty"`
|
||||||
|
Error *ErrorBody `json:"error,omitempty"`
|
||||||
|
Routes []*Route `json:"routes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoutesWrapper) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateRoutes(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoutesWrapper) validateRoutes(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("routes", "body", m.Routes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -17,8 +17,6 @@ CREATE TABLE IF NOT EXISTS routes (
|
|||||||
path text NOT NULL,
|
path text NOT NULL,
|
||||||
app_name character varying(256) NOT NULL,
|
app_name character varying(256) NOT NULL,
|
||||||
image character varying(256) NOT NULL,
|
image character varying(256) NOT NULL,
|
||||||
type character varying(256) NOT NULL,
|
|
||||||
container_path text NOT NULL,
|
|
||||||
headers text NOT NULL
|
headers text NOT NULL
|
||||||
);`
|
);`
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ const appsTableCreate = `CREATE TABLE IF NOT EXISTS apps (
|
|||||||
name character varying(256) NOT NULL PRIMARY KEY
|
name character varying(256) NOT NULL PRIMARY KEY
|
||||||
);`
|
);`
|
||||||
|
|
||||||
const routeSelector = `SELECT name, path, app_name, image, type, container_path, headers FROM routes`
|
const routeSelector = `SELECT name, path, app_name, image, headers FROM routes`
|
||||||
|
|
||||||
type rowScanner interface {
|
type rowScanner interface {
|
||||||
Scan(dest ...interface{}) error
|
Scan(dest ...interface{}) error
|
||||||
@@ -162,22 +160,18 @@ func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, err
|
|||||||
_, err = ds.db.Exec(`
|
_, err = ds.db.Exec(`
|
||||||
INSERT INTO routes (
|
INSERT INTO routes (
|
||||||
name, app_name, path, image,
|
name, app_name, path, image,
|
||||||
type, container_path, headers
|
headers
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
ON CONFLICT (name) DO UPDATE SET
|
ON CONFLICT (name) DO UPDATE SET
|
||||||
path = $3,
|
path = $3,
|
||||||
image = $4,
|
image = $4,
|
||||||
type = $5,
|
headers = $5;
|
||||||
container_path = $6,
|
|
||||||
headers = $7;
|
|
||||||
`,
|
`,
|
||||||
route.Name,
|
route.Name,
|
||||||
route.AppName,
|
route.AppName,
|
||||||
route.Path,
|
route.Path,
|
||||||
route.Image,
|
route.Image,
|
||||||
route.Type,
|
|
||||||
route.ContainerPath,
|
|
||||||
headers,
|
headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -206,8 +200,6 @@ func scanRoute(scanner rowScanner, route *models.Route) error {
|
|||||||
&route.Path,
|
&route.Path,
|
||||||
&route.AppName,
|
&route.AppName,
|
||||||
&route.Image,
|
&route.Image,
|
||||||
&route.Type,
|
|
||||||
&route.ContainerPath,
|
|
||||||
&headerStr,
|
&headerStr,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -12,22 +12,28 @@ func handleAppCreate(c *gin.Context) {
|
|||||||
store := c.MustGet("store").(models.Datastore)
|
store := c.MustGet("store").(models.Datastore)
|
||||||
log := c.MustGet("log").(logrus.FieldLogger)
|
log := c.MustGet("log").(logrus.FieldLogger)
|
||||||
|
|
||||||
app := &models.App{}
|
wapp := &models.AppWrapper{}
|
||||||
|
|
||||||
err := c.BindJSON(app)
|
err := c.BindJSON(wapp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrInvalidJSON)
|
log.WithError(err).Debug(models.ErrInvalidJSON)
|
||||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.Validate(); err != nil {
|
if wapp.App == nil {
|
||||||
|
log.Debug(models.ErrAppsMissingNew)
|
||||||
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsMissingNew))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wapp.Validate(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
c.JSON(http.StatusInternalServerError, simpleError(err))
|
c.JSON(http.StatusInternalServerError, simpleError(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err = store.StoreApp(app)
|
app, err := store.StoreApp(wapp.App)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrAppsCreate)
|
log.WithError(err).Debug(models.ErrAppsCreate)
|
||||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
||||||
|
|||||||
@@ -40,5 +40,5 @@ func handleAppGet(c *gin.Context) {
|
|||||||
|
|
||||||
app.Routes = routes
|
app.Routes = routes
|
||||||
|
|
||||||
c.JSON(http.StatusOK, app)
|
c.JSON(http.StatusOK, &models.AppWrapper{app})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ func handleAppList(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, apps)
|
c.JSON(http.StatusOK, &models.AppsWrapper{apps})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,5 @@ func handleAppUpdate(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// app.Name = c.Param("app")
|
c.JSON(http.StatusOK, simpleError(models.ErrAppsNothingToUpdate))
|
||||||
|
|
||||||
// app, err = store.StoreApp(app)
|
|
||||||
// if err != nil {
|
|
||||||
// log.WithError(err).Debug(models.ErrAppsUpdate)
|
|
||||||
// c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsUpdate))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, simpleError(models.ErrAppNothingToUpdate))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func Start(engine *gin.Engine) {
|
|||||||
v1.POST("/apps", handleAppCreate)
|
v1.POST("/apps", handleAppCreate)
|
||||||
|
|
||||||
v1.GET("/apps/:app", handleAppGet)
|
v1.GET("/apps/:app", handleAppGet)
|
||||||
v1.POST("/apps/:app", handleAppUpdate)
|
v1.PUT("/apps/:app", handleAppUpdate)
|
||||||
v1.DELETE("/apps/:app", handleAppDestroy)
|
v1.DELETE("/apps/:app", handleAppDestroy)
|
||||||
|
|
||||||
apps := v1.Group("/apps/:app")
|
apps := v1.Group("/apps/:app")
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ func handleRouteCreate(c *gin.Context) {
|
|||||||
|
|
||||||
err := c.BindJSON(route)
|
err := c.BindJSON(route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrInvalidJSON)
|
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if route == nil {
|
||||||
|
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||||
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrRoutesMissingNew))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
route.AppName = c.Param("app")
|
route.AppName = c.Param("app")
|
||||||
|
|
||||||
if err := route.Validate(); err != nil {
|
if err := route.Validate(); err != nil {
|
||||||
@@ -46,7 +52,7 @@ func handleRouteCreate(c *gin.Context) {
|
|||||||
|
|
||||||
route, err = store.StoreRoute(route)
|
route, err = store.StoreRoute(route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrRoutesCreate)
|
log.WithError(err).Error(models.ErrRoutesCreate)
|
||||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesCreate))
|
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesCreate))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ func handleRouteGet(c *gin.Context) {
|
|||||||
|
|
||||||
log.WithFields(logrus.Fields{"route": route}).Debug("Got route")
|
log.WithFields(logrus.Fields{"route": route}).Debug("Got route")
|
||||||
|
|
||||||
c.JSON(http.StatusOK, route)
|
c.JSON(http.StatusOK, &models.RouteWrapper{route})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,5 +27,5 @@ func handleRouteList(c *gin.Context) {
|
|||||||
|
|
||||||
log.WithFields(logrus.Fields{"routes": routes}).Debug("Got routes")
|
log.WithFields(logrus.Fields{"routes": routes}).Debug("Got routes")
|
||||||
|
|
||||||
c.JSON(http.StatusOK, routes)
|
c.JSON(http.StatusOK, &models.RoutesWrapper{Routes: routes})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ func handleRouteUpdate(c *gin.Context) {
|
|||||||
store := c.MustGet("store").(models.Datastore)
|
store := c.MustGet("store").(models.Datastore)
|
||||||
log := c.MustGet("log").(logrus.FieldLogger)
|
log := c.MustGet("log").(logrus.FieldLogger)
|
||||||
|
|
||||||
route := &models.Route{}
|
wroute := &models.RouteWrapper{}
|
||||||
appName := c.Param("app")
|
appName := c.Param("app")
|
||||||
|
|
||||||
err := c.BindJSON(route)
|
err := c.BindJSON(wroute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrInvalidJSON)
|
log.WithError(err).Debug(models.ErrInvalidJSON)
|
||||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
route.AppName = appName
|
wroute.Route.AppName = appName
|
||||||
|
|
||||||
route, err = store.StoreRoute(route)
|
route, err := store.StoreRoute(wroute.Route)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug(models.ErrAppsCreate)
|
log.WithError(err).Debug(models.ErrAppsCreate)
|
||||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
||||||
|
|||||||
324
api/swagger.yml
Normal file
324
api/swagger.yml
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# This is the IronFunctions API spec
|
||||||
|
# If you make changes here, remember to run `go generate` in routeserver/ and
|
||||||
|
# tasker/ to make sure you use the changes.
|
||||||
|
|
||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: IronFunctions
|
||||||
|
description:
|
||||||
|
version: "0.0.1"
|
||||||
|
# the domain of the service
|
||||||
|
host: "localhost:8080"
|
||||||
|
# array of all schemes that your API supports
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
- http
|
||||||
|
# will be prefixed to all paths
|
||||||
|
basePath: /v1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
paths:
|
||||||
|
/apps:
|
||||||
|
get:
|
||||||
|
summary: "Get all app names."
|
||||||
|
description: "Get a list of all the apps in the system."
|
||||||
|
tags:
|
||||||
|
- Apps
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: List of apps.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppsWrapper'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
post:
|
||||||
|
summary: "Post new app"
|
||||||
|
description: "Insert a new app"
|
||||||
|
tags:
|
||||||
|
- Apps
|
||||||
|
parameters:
|
||||||
|
- name: body
|
||||||
|
in: body
|
||||||
|
description: App to post.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppWrapper'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: App details and stats.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppWrapper'
|
||||||
|
400:
|
||||||
|
description: Parameters are missing or invalid.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Could not accept app due to internal error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
|
||||||
|
/apps/{app}:
|
||||||
|
get:
|
||||||
|
summary: "Get information for a app."
|
||||||
|
description: "This gives more details about a app, such as statistics."
|
||||||
|
tags:
|
||||||
|
- Apps
|
||||||
|
parameters:
|
||||||
|
- name: name
|
||||||
|
in: path
|
||||||
|
description: name of the app.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: App details and stats.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppWrapper'
|
||||||
|
404:
|
||||||
|
description: App does not exist.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
put:
|
||||||
|
summary: "Create/update a app."
|
||||||
|
description: "You can set app level settings here. "
|
||||||
|
tags:
|
||||||
|
- Apps
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: name of the app.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: body
|
||||||
|
in: body
|
||||||
|
description: App to post.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppWrapper'
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: App details and stats.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/AppWrapper'
|
||||||
|
400:
|
||||||
|
description: Parameters are missing or invalid.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Could not accept app due to internal error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
/apps/{app}/routes:
|
||||||
|
post:
|
||||||
|
summary: Enqueue Route
|
||||||
|
description: |
|
||||||
|
Enqueues route(s). If any of the routes is invalid, none of the routes are enqueued.
|
||||||
|
tags:
|
||||||
|
- Routes
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: name of the app.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: body
|
||||||
|
in: body
|
||||||
|
description: Array of routes to post.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/NewRoutesWrapper'
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: Route created
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RoutesWrapper'
|
||||||
|
400:
|
||||||
|
description: One or more of the routes were invalid due to parameters being missing or invalid.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Could not accept routes due to internal error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
get:
|
||||||
|
summary: Get route list by app name.
|
||||||
|
description: This will list routes for a particular app.
|
||||||
|
tags:
|
||||||
|
- Routes
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: Name of app for this set of routes.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Route information
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RoutesWrapper'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
/apps/{app}/routes/{route}:
|
||||||
|
get:
|
||||||
|
summary: Gets route by name
|
||||||
|
description: Gets a route by name.
|
||||||
|
tags:
|
||||||
|
- Routes
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: Name of app for this set of routes.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: route
|
||||||
|
in: path
|
||||||
|
description: Route name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Route information
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/RouteWrapper'
|
||||||
|
404:
|
||||||
|
description: Route does not exist.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
default:
|
||||||
|
description: Unexpected error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
delete:
|
||||||
|
summary: Deletes the route
|
||||||
|
tags:
|
||||||
|
- Routes
|
||||||
|
description: Deletes the route.
|
||||||
|
parameters:
|
||||||
|
- name: app
|
||||||
|
in: path
|
||||||
|
description: Name of app for this set of routes.
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: route
|
||||||
|
in: path
|
||||||
|
description: Route name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Route successfully deleted. Deletion succeeds even on routes that do not exist.
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
Route:
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: "Route name"
|
||||||
|
readOnly: true
|
||||||
|
app_name:
|
||||||
|
type: string
|
||||||
|
description: "App this route belongs to."
|
||||||
|
readOnly: true
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: URL path that will be matched to this route
|
||||||
|
image:
|
||||||
|
description: Name of Docker image to use in this route. You should include the image tag, which should be a version number, to be more accurate. Can be overridden on a per route basis with route.image.
|
||||||
|
type: string
|
||||||
|
headers:
|
||||||
|
type: string
|
||||||
|
description: Map of http headers that will be sent with the response
|
||||||
|
|
||||||
|
App:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: "Name of this app. Must be different than the image name. Can ony contain alphanumeric, -, and _."
|
||||||
|
readOnly: true
|
||||||
|
|
||||||
|
RoutesWrapper:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- routes
|
||||||
|
properties:
|
||||||
|
routes:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Route'
|
||||||
|
cursor:
|
||||||
|
type: string
|
||||||
|
description: Used to paginate results. If this is returned, pass it into the same query again to get more results.
|
||||||
|
error:
|
||||||
|
$ref: '#/definitions/ErrorBody'
|
||||||
|
|
||||||
|
RouteWrapper:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- route
|
||||||
|
properties:
|
||||||
|
route:
|
||||||
|
$ref: '#/definitions/Route'
|
||||||
|
|
||||||
|
AppsWrapper:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- apps
|
||||||
|
properties:
|
||||||
|
apps:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/App'
|
||||||
|
|
||||||
|
AppWrapper:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- app
|
||||||
|
properties:
|
||||||
|
app:
|
||||||
|
$ref: '#/definitions/App'
|
||||||
|
|
||||||
|
ErrorBody:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
|
fields:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
|
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
$ref: '#/definitions/ErrorBody'
|
||||||
0
docs/api.md
Normal file
0
docs/api.md
Normal file
11
docs/database/boltdb.md
Normal file
11
docs/database/boltdb.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# IronFunctions using BoltDB
|
||||||
|
|
||||||
|
BoltDB is the default database, you just need to run the API.
|
||||||
|
|
||||||
|
## Persistent
|
||||||
|
|
||||||
|
To keep it persistent you add a volume flag to the command:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -it -v $PWD/bold.db:/app/bolt.db -p 8080:8080 iron/functions
|
||||||
|
```
|
||||||
34
docs/database/postgres.md
Normal file
34
docs/database/postgres.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# IronFunctions using Postgres
|
||||||
|
|
||||||
|
Let's presuppose you don't have even a postgres DB ready.
|
||||||
|
|
||||||
|
### 1. Let's start a postgres instance:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --name iron-postgres \
|
||||||
|
-e POSTGRES_PASSWORD=ironfunctions -d postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Now let's create a new database to IronFunctions
|
||||||
|
|
||||||
|
Creating database:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm --link iron-postgres:postgres postgres \
|
||||||
|
psql -h postgres -U postgres -c "CREATE DATABASE funcs;"
|
||||||
|
```
|
||||||
|
|
||||||
|
Granting access to postgres user
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm --link iron-postgres:postgres postgres \
|
||||||
|
psql -h postgres -U postgres -c 'GRANT ALL PRIVILEGES ON DATABASE funcs TO postgres;'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Now let's start IronFunctions connecting to our new postgres instance
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm --link "iron-postgres:postgres" \
|
||||||
|
-e "DB=postgres://postgres:ironfunctions@postgres/funcs?sslmode=disable" \
|
||||||
|
-it -p 8080:8080 iron/functions
|
||||||
|
```
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# For IronCache
|
|
||||||
IRON_TOKEN=X
|
|
||||||
IRON_PROJECT_ID=X
|
|
||||||
|
|
||||||
# For CloudFlare dns support
|
|
||||||
CLOUDFLARE_EMAIL=you@example.com
|
|
||||||
CLOUDFLARE_API_KEY=X
|
|
||||||
# See comments here to get zone id https://blog.cloudflare.com/cloudflare-tips-frequently-used-cloudflare-ap/#comment-2412200222
|
|
||||||
CLOUDFLARE_ZONE_ID=y
|
|
||||||
23
gateway.pem
23
gateway.pem
@@ -1,23 +0,0 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpQIBAAKCAQEAhof/Rgb5YUGUCynxYAkbkSRQqIaaZem7g6r/lyePBk6IKzjia6QA+Ut+1KZt
|
|
||||||
uGrNXFR0THtfEWCFjIPBJfYLGmkG1gaYocWJUu/b3be3rKGlLWEuSpfHsYyLh4803QNU79Uu0ft8
|
|
||||||
ODB4QJl54WImD1JKzAZyarDalyb+GKbnU5NAULBbTccbGFbNSwPwebvoK9G6Z8qWChPqsYAZxfyC
|
|
||||||
D2LBt0PANwB+haC6Rj0t99R6mtLRz/iKYaHz26d6UxSzNsXArJlhSCABHdQ71rbPkO0M9PvJrhfg
|
|
||||||
y+bLA4sMrHvOSjEDWGY+j1qqEYXSc/Rwe5SMd8kV7i902ks7PjcCZQIDAQABAoIBAHjvlHk9F71o
|
|
||||||
GE+Y2tV8Gn31aVS1++IVpW2NsMoO07HVsu836cLd4co5JcDAA+4+hHG1sf53AVU7sZJJdr5LWlvZ
|
|
||||||
gj2wHFGApBwcZ0f/OWxEu5n5vIVtwCRJtbyc7eaochhPShGVw2s3l0JrNXd4pcIsNfUG7qAeb8Jl
|
|
||||||
WRKMJ3OmoEMOz5M3scRypQKOulRjO6RMJCtbl4AntMYNF7cdWeuIJ3eaMD8HaYbkr1USrwGk65QC
|
|
||||||
mKdUcNl9k++Txuf7UtbRB0apFsMnAKRPUTU+9TPGwMsZSzszk8TClMNO1ALYKKY5mE+cPqrAl5gC
|
|
||||||
ZpPOf45oT+2lxktqq5u5N8XMFoECgYEA16eGE3inh23EVly5ARnowGgFtorZsI6XIJHkYsETRocf
|
|
||||||
GAvQrEMAuFWZy0n5TlNBfzPhHh9rUXWCKlbldgnDgWKpphPux3UXTqTUak+j6397rtEP85RfYqDm
|
|
||||||
QxtW0uOkKkZSGyXXEYBwTbCsQBH28VZsJEYVe+G3uXPcUu3WEZUCgYEAn7Mwgk/JP5wgYo4E3gga
|
|
||||||
fOi0/de11MV5ad337qfdUC1pf1ju9q2CyHaV3g6eo2OnynGZHYq5qlyLWoi/hTr6A+yMZSnQ15io
|
|
||||||
9ker4uyAX6DdVDmWK9uErwrqLAV+Q6HoVmxoyBbMihQW8TqX/5jZegxqipDW16+qOFxtPbZhmZEC
|
|
||||||
gYEAlD85UBFVOSggHC5Jj5Q8CGh55O62j0S2Z1Fjau/HTGh+24zjukelKxLNUo5br5hUIhmL26VF
|
|
||||||
pQ3emTR7MRWtLDii3uQ89ShtCUcOLrbovG86mwZkrNGGcMqi/+a/XOHYbKdCsh7lJcbhbMbS4oh2
|
|
||||||
9ZivZpA3HJ4iKn6XKvsMebECgYEAhNWXU8zpqG9EwLVAdy5mWd92LG5wYDqhct2ejHQ0MayUQ8jF
|
|
||||||
e4l3bya0IbAnY+BQgKNcqKXrKTkw8G0uYLNdokXvwXW2sJ3abH/RCT+Ox/wWHSiJMJG3G6IIhfVL
|
|
||||||
wRW7G6ewwD22hGORcbU7GO8addo+BGPVUDJdc+PtOZeqNwECgYEAvLJQas3PKLL+qVmO9asvDtCl
|
|
||||||
tvOuvPFAuZBk6hLm9SSrFt5cCW+rulL8Kx1PxUk0C8LiJV8uwZIqQxE+gY5MkCfvt+xG1yurkywd
|
|
||||||
SCHOFr0m4gi8+XHvL4fmGzYPgHRi10kepSta5G8USigbcf5fOmw+Upa3qVnwot3CQdxmSfU=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
118
glide.lock
generated
118
glide.lock
generated
@@ -1,75 +1,71 @@
|
|||||||
hash: a104a522dc2ba982d25101330bbd5d44778565128f0cb09f34061c167921046c
|
hash: 2101b4c83f12c75cbc3cb5be0a7d544dd3a7d21a27ef8a5ee8b014ded8cee034
|
||||||
updated: 2016-07-14T08:40:37.798125603-07:00
|
updated: 2016-07-27T16:56:51.792310167-03:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/amir/raidman
|
- name: github.com/asaskevich/govalidator
|
||||||
version: 91c20f3f475cab75bb40ad7951d9bbdde357ade7
|
version: 593d64559f7600f29581a3ee42177f5dbded27a9
|
||||||
|
- name: github.com/boltdb/bolt
|
||||||
|
version: 5cc10bbbc5c141029940133bb33c9e969512a698
|
||||||
|
- name: github.com/gin-gonic/gin
|
||||||
|
version: 4a6bc4aac4607e253bcda67c8c5bcda693d2388e
|
||||||
subpackages:
|
subpackages:
|
||||||
- proto
|
- binding
|
||||||
- name: github.com/BurntSushi/toml
|
- render
|
||||||
version: bec2dacf4b590d26237cfebff4471e21ce543494
|
- name: github.com/go-openapi/analysis
|
||||||
- name: github.com/cactus/go-statsd-client
|
version: abc9a6171f5bf03ada39aead1aa7fd7bbd44d50f
|
||||||
version: 91c326c3f7bd20f0226d3d1c289dd9f8ce28d33d
|
- name: github.com/go-openapi/errors
|
||||||
subpackages:
|
version: d24ebc2075bad502fac3a8ae27aa6dd58e1952dc
|
||||||
- statsd
|
- name: github.com/go-openapi/jsonpointer
|
||||||
- name: github.com/dgrijalva/jwt-go
|
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
|
||||||
version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20
|
- name: github.com/go-openapi/jsonreference
|
||||||
|
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
||||||
|
- name: github.com/go-openapi/loads
|
||||||
|
version: 18441dfa706d924a39a030ee2c3b1d8d81917b38
|
||||||
|
- name: github.com/go-openapi/runtime
|
||||||
|
version: 11e322eeecc1032d5a0a96c566ed53f2b5c26e22
|
||||||
|
- name: github.com/go-openapi/spec
|
||||||
|
version: e9fab754f5629065e6b7a6100301226545d4477e
|
||||||
|
- name: github.com/go-openapi/strfmt
|
||||||
|
version: dfda818c47a4ae5a1dde75ac776f34b2c95de993
|
||||||
|
- name: github.com/go-openapi/swag
|
||||||
|
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
||||||
|
- name: github.com/go-openapi/validate
|
||||||
|
version: deaf2c9013bc1a7f4c774662259a506ba874d80f
|
||||||
- name: github.com/golang/protobuf
|
- name: github.com/golang/protobuf
|
||||||
version: 874264fbbb43f4d91e999fecb4b40143ed611400
|
version: 2402d76f3d41f928c7902a765dfc872356dd3aad
|
||||||
subpackages:
|
subpackages:
|
||||||
- proto
|
- proto
|
||||||
- name: github.com/gorilla/context
|
- name: github.com/iron-io/titan
|
||||||
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
|
version: 697a5466b096fee73202f5ddccf8213a2357e062
|
||||||
- name: github.com/gorilla/mux
|
repo: git@github.com:iron-io/titan.git
|
||||||
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
|
vcs: git
|
||||||
- name: github.com/iron-io/common
|
|
||||||
version: 7c9faec363c808052742b4c4b84876c4c2172308
|
|
||||||
subpackages:
|
subpackages:
|
||||||
- httpshutdown
|
- jobserver/models
|
||||||
- msgpack
|
- name: github.com/lib/pq
|
||||||
- semaphore
|
version: 3cd0097429be7d611bb644ef85b42bfb102ceea4
|
||||||
- serverutil
|
|
||||||
- name: github.com/iron-io/go
|
|
||||||
version: 1ed3de151aa27db91d6df5dc8c8ce164c833b57c
|
|
||||||
subpackages:
|
subpackages:
|
||||||
- common
|
- oid
|
||||||
- common/httpshutdown
|
- name: github.com/mailru/easyjson
|
||||||
- common/msgpack
|
version: 97eee20abef76a0591155412cf0d04f24b05098f
|
||||||
- common/semaphore
|
|
||||||
- common/stats
|
|
||||||
- name: github.com/iron-io/golog
|
|
||||||
version: 5b80d97af5a2a5d386e7609efb82192ae99a7c67
|
|
||||||
- name: github.com/iron-io/iron_go
|
|
||||||
version: 920c950272e820d9e2562fcbf40303f05564f118
|
|
||||||
subpackages:
|
subpackages:
|
||||||
- cache
|
- jlexer
|
||||||
- worker
|
- jwriter
|
||||||
- api
|
- buffer
|
||||||
- config
|
- name: github.com/manucorporat/sse
|
||||||
- name: github.com/mattn/go-colorable
|
version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
|
||||||
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
|
- name: github.com/PuerkitoBio/purell
|
||||||
- name: github.com/mattn/go-isatty
|
version: 1d5d1cfad45d42ec5f81fa8ef23de09cebc6dcc3
|
||||||
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
|
- name: github.com/PuerkitoBio/urlesc
|
||||||
|
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
|
||||||
- name: github.com/Sirupsen/logrus
|
- name: github.com/Sirupsen/logrus
|
||||||
version: 32055c351ea8b00b96d70f28db48d9840feaf0ec
|
version: a283a10442df8dc09befd873fab202bf8a253d6a
|
||||||
- name: github.com/vmihailenco/bufio
|
|
||||||
version: 24e7e48f60fc2d9e99e43c07485d9fff42051e66
|
|
||||||
- name: github.com/vrischmann/envconfig
|
|
||||||
version: 9e6e1c4d3b73427d03118518603bb904d9c55236
|
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: a728288923b47049b2ce791836767ffbe964a5bd
|
version: f315505cf3349909cdf013ea56690da34e96a451
|
||||||
subpackages:
|
subpackages:
|
||||||
- proxy
|
- context
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
version: b518c298ac9dc94b6ac0757394f50d10c5dfa25a
|
version: a646d33e2ee3172a661fc09bca23bb4889a41bc8
|
||||||
subpackages:
|
subpackages:
|
||||||
- unix
|
- unix
|
||||||
- name: gopkg.in/inconshreveable/log15.v2
|
- name: gopkg.in/go-playground/validator.v8
|
||||||
version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f
|
version: c193cecd124b5cc722d7ee5538e945bdb3348435
|
||||||
subpackages:
|
testImports: []
|
||||||
- stack
|
|
||||||
- term
|
|
||||||
- name: gopkg.in/mgo.v2
|
|
||||||
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
|
|
||||||
subpackages:
|
|
||||||
- bson
|
|
||||||
devImports: []
|
|
||||||
|
|||||||
19
glide.yaml
19
glide.yaml
@@ -1,9 +1,14 @@
|
|||||||
package: github.com/iron-io/microgateway
|
package: github.com/iron-io/functions
|
||||||
import:
|
import:
|
||||||
- package: github.com/gorilla/mux
|
- package: github.com/Sirupsen/logrus
|
||||||
- package: github.com/iron-io/common
|
- package: github.com/boltdb/bolt
|
||||||
- package: github.com/iron-io/golog
|
- package: github.com/gin-gonic/gin
|
||||||
- package: github.com/iron-io/iron_go
|
- package: github.com/go-openapi/errors
|
||||||
|
- package: github.com/go-openapi/strfmt
|
||||||
|
- package: github.com/go-openapi/validate
|
||||||
|
- package: github.com/iron-io/titan
|
||||||
|
repo: git@github.com:iron-io/titan.git
|
||||||
|
vcs: git
|
||||||
subpackages:
|
subpackages:
|
||||||
- cache
|
- jobserver/models
|
||||||
- worker
|
- package: github.com/lib/pq
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
/* I wanted to do some stuff to this so had to make a copy. Namely:
|
|
||||||
- change the Host handling for virtual hosts.
|
|
||||||
- get errors if the proxy request fails
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// HTTP reverse proxy handler
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// onExitFlushLoop is a callback set by tests to detect the state of the
|
|
||||||
// flushLoop() goroutine.
|
|
||||||
var onExitFlushLoop func()
|
|
||||||
|
|
||||||
// ReverseProxy is an HTTP Handler that takes an incoming request and
|
|
||||||
// sends it to another server, proxying the response back to the
|
|
||||||
// client.
|
|
||||||
type ReverseProxy struct {
|
|
||||||
// Director must be a function which modifies
|
|
||||||
// the request into a new request to be sent
|
|
||||||
// using Transport. Its response is then copied
|
|
||||||
// back to the original client unmodified.
|
|
||||||
Director func(*http.Request)
|
|
||||||
|
|
||||||
// The transport used to perform proxy requests.
|
|
||||||
// If nil, http.DefaultTransport is used.
|
|
||||||
Transport http.RoundTripper
|
|
||||||
|
|
||||||
// FlushInterval specifies the flush interval
|
|
||||||
// to flush to the client while copying the
|
|
||||||
// response body.
|
|
||||||
// If zero, no periodic flushing is done.
|
|
||||||
FlushInterval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func singleJoiningSlash(a, b string) string {
|
|
||||||
aslash := strings.HasSuffix(a, "/")
|
|
||||||
bslash := strings.HasPrefix(b, "/")
|
|
||||||
switch {
|
|
||||||
case aslash && bslash:
|
|
||||||
return a + b[1:]
|
|
||||||
case !aslash && !bslash:
|
|
||||||
return a + "/" + b
|
|
||||||
}
|
|
||||||
return a + b
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
|
|
||||||
// URLs to the scheme, host, and base path provided in target. If the
|
|
||||||
// target's path is "/base" and the incoming request was for "/dir",
|
|
||||||
// the target request will be for /base/dir.
|
|
||||||
func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
|
|
||||||
targetQuery := target.RawQuery
|
|
||||||
director := func(req *http.Request) {
|
|
||||||
req.URL.Scheme = target.Scheme
|
|
||||||
req.URL.Host = target.Host
|
|
||||||
req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
|
|
||||||
if targetQuery == "" || req.URL.RawQuery == "" {
|
|
||||||
req.URL.RawQuery = targetQuery + req.URL.RawQuery
|
|
||||||
} else {
|
|
||||||
req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &ReverseProxy{Director: director}
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyHeader(dst, src http.Header) {
|
|
||||||
for k, vv := range src {
|
|
||||||
for _, v := range vv {
|
|
||||||
dst.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) error {
|
|
||||||
transport := p.Transport
|
|
||||||
if transport == nil {
|
|
||||||
transport = http.DefaultTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
outreq := new(http.Request)
|
|
||||||
*outreq = *req // includes shallow copies of maps, but okay
|
|
||||||
|
|
||||||
p.Director(outreq)
|
|
||||||
outreq.Proto = "HTTP/1.1"
|
|
||||||
outreq.ProtoMajor = 1
|
|
||||||
outreq.ProtoMinor = 1
|
|
||||||
outreq.Close = false
|
|
||||||
|
|
||||||
// Remove the connection header to the backend. We want a
|
|
||||||
// persistent connection, regardless of what the client sent
|
|
||||||
// to us. This is modifying the same underlying map from req
|
|
||||||
// (shallow copied above) so we only copy it if necessary.
|
|
||||||
if outreq.Header.Get("Connection") != "" {
|
|
||||||
outreq.Header = make(http.Header)
|
|
||||||
copyHeader(outreq.Header, req.Header)
|
|
||||||
outreq.Header.Del("Connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
if clientIp, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
|
||||||
outreq.Header.Set("X-Forwarded-For", clientIp)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := transport.RoundTrip(outreq)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("http: proxy error: %v", err)
|
|
||||||
// rw.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer res.Body.Close()
|
|
||||||
|
|
||||||
copyHeader(rw.Header(), res.Header)
|
|
||||||
|
|
||||||
rw.WriteHeader(res.StatusCode)
|
|
||||||
p.copyResponse(rw, res.Body)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) {
|
|
||||||
if p.FlushInterval != 0 {
|
|
||||||
if wf, ok := dst.(writeFlusher); ok {
|
|
||||||
mlw := &maxLatencyWriter{
|
|
||||||
dst: wf,
|
|
||||||
latency: p.FlushInterval,
|
|
||||||
done: make(chan bool),
|
|
||||||
}
|
|
||||||
go mlw.flushLoop()
|
|
||||||
defer mlw.stop()
|
|
||||||
dst = mlw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
io.Copy(dst, src)
|
|
||||||
}
|
|
||||||
|
|
||||||
type writeFlusher interface {
|
|
||||||
io.Writer
|
|
||||||
http.Flusher
|
|
||||||
}
|
|
||||||
|
|
||||||
type maxLatencyWriter struct {
|
|
||||||
dst writeFlusher
|
|
||||||
latency time.Duration
|
|
||||||
|
|
||||||
lk sync.Mutex // protects Write + Flush
|
|
||||||
done chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *maxLatencyWriter) Write(p []byte) (int, error) {
|
|
||||||
m.lk.Lock()
|
|
||||||
defer m.lk.Unlock()
|
|
||||||
return m.dst.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *maxLatencyWriter) flushLoop() {
|
|
||||||
t := time.NewTicker(m.latency)
|
|
||||||
defer t.Stop()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-m.done:
|
|
||||||
if onExitFlushLoop != nil {
|
|
||||||
onExitFlushLoop()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case <-t.C:
|
|
||||||
m.lk.Lock()
|
|
||||||
m.dst.Flush()
|
|
||||||
m.lk.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("unreached")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *maxLatencyWriter) stop() { m.done <- true }
|
|
||||||
Reference in New Issue
Block a user