Merge pull request #25 from pedronasser/api-fix

Added wrappers; API fixes and swagger
This commit is contained in:
Travis Reeder
2016-07-29 11:41:49 -07:00
committed by GitHub
31 changed files with 694 additions and 409 deletions

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ vendor/
/gateway
/functions
bolt.db
.glide/
private.sh
.env

View File

@@ -1,23 +1,24 @@
## Building
## Building/Testing
Build:
```sh
# one time:
First time or when a dependency changes or when the API changes, run:
```
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
docker run --env-file .env --rm -it --privileged -p 8080:8080 iron/functions
api.sh
```
To build the docker image:
```sh
build.sh
```
## Releasing
```sh
./release.sh
release.sh
```

View File

@@ -1,54 +1,53 @@
Note: currently running at: http://gateway.iron.computer:8080/
# 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
First things first, create an app/service:
TOOD: App or service??
### Create App
### Creating a application
```sh
iron create app APP_NAME
# OR
curl -H "Content-Type: application/json" -X POST -d '{"name":"APP_NAME"}' http://localhost:8080/api/v1/apps
curl -H "Content-Type: application/json" -X POST -d '{
"name":"APP_NAME"
}' 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:
```sh
iron add route myapp /hello iron/hello
# OR
curl -H "Content-Type: application/json" -X POST -d '{"path":"/hello", "image":"iron/hello"}' http://localhost:8080/api/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
curl -H "Content-Type: application/json" -X POST -d '{
"name": "hello",
"path":"/hello",
"image":"iron/hello"
}' http://localhost:8080/v1/apps/myapp/routes
```
### 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.
```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
@@ -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:
```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:
```
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
the route.
### Databases
## Examples
These are the current databases supported by IronFunctions:
TODO: Link to examples in various languages
TODO: Link to slackbots (easiest way to host slackbots?)
- [Running with BoltDB](/iron-io/functions/blob/master/docs/database/boltdb.md)
- [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
Run logspout container on your server.
#### Monitoring
## Logging
TODO
### Scaling
## Monitoring
TODO
## Scaling
TODO

5
api.sh Executable file
View 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

View File

@@ -5,13 +5,14 @@ import "errors"
type Apps []*App
var (
ErrAppsCreate = errors.New("Could not create app")
ErrAppsUpdate = errors.New("Could not update app")
ErrAppsRemoving = errors.New("Could not remove app from datastore")
ErrAppsGet = errors.New("Could not get app from datastore")
ErrAppsList = errors.New("Could not list apps from datastore")
ErrAppsNotFound = errors.New("App not found")
ErrAppNothingToUpdate = errors.New("Nothing to update")
ErrAppsCreate = errors.New("Could not create app")
ErrAppsUpdate = errors.New("Could not update app")
ErrAppsRemoving = errors.New("Could not remove app from datastore")
ErrAppsGet = errors.New("Could not get app from datastore")
ErrAppsList = errors.New("Could not list apps from datastore")
ErrAppsNotFound = errors.New("App not found")
ErrAppsNothingToUpdate = errors.New("Nothing to update")
ErrAppsMissingNew = errors.New("Missing new application")
)
type App struct {

32
api/models/app_wrapper.go Normal file
View 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
}

View 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
}

View File

@@ -17,6 +17,6 @@ func ApplyAppFilter(app *App, filter *AppFilter) bool {
}
func ApplyRouteFilter(route *Route, filter *RouteFilter) bool {
return (filter.Path != "" && route.Path == filter.Path) &&
(filter.AppName != "" && route.AppName == filter.AppName)
return (filter.Path == "" || route.Path == filter.Path) &&
(filter.AppName == "" || route.AppName == filter.AppName)
}

View File

@@ -11,5 +11,5 @@ func (m *Error) Validate() error {
}
var (
ErrInvalidJSON = errors.New("Could not create app")
ErrInvalidJSON = errors.New("Invalid JSON")
)

View File

@@ -3,27 +3,28 @@ package models
import (
"errors"
"net/http"
apiErrors "github.com/go-openapi/errors"
)
var (
ErrRoutesCreate = errors.New("Could not create route")
ErrRoutesUpdate = errors.New("Could not update route")
ErrRoutesRemoving = errors.New("Could not remove route from datastore")
ErrRoutesGet = errors.New("Could not get route from datastore")
ErrRoutesList = errors.New("Could not list routes from datastore")
ErrRoutesNotFound = errors.New("Route not found")
ErrRoutesCreate = errors.New("Could not create route")
ErrRoutesUpdate = errors.New("Could not update route")
ErrRoutesRemoving = errors.New("Could not remove route from datastore")
ErrRoutesGet = errors.New("Could not get route from datastore")
ErrRoutesList = errors.New("Could not list routes from datastore")
ErrRoutesNotFound = errors.New("Route not found")
ErrRoutesMissingNew = errors.New("Missing new route")
)
type Routes []*Route
type Route struct {
Name string `json:"name"`
AppName string `json:"appname"`
Path string `json:"path"`
Image string `json:"image"`
Type string `json:"type,omitempty"`
ContainerPath string `json:"container_path,omitempty"`
Headers http.Header `json:"headers,omitempty"`
Name string `json:"name"`
AppName string `json:"appname"`
Path string `json:"path"`
Image string `json:"image"`
Headers http.Header `json:"headers,omitempty"`
}
var (
@@ -34,20 +35,26 @@ var (
)
func (r *Route) Validate() error {
var res []error
if r.Name == "" {
return ErrRoutesValidationName
res = append(res, ErrRoutesValidationAppName)
}
if r.Image == "" {
return ErrRoutesValidationImage
res = append(res, ErrRoutesValidationImage)
}
if r.AppName == "" {
return ErrRoutesValidationAppName
res = append(res, ErrRoutesValidationAppName)
}
if r.Path == "" {
return ErrRoutesValidationPath
res = append(res, ErrRoutesValidationPath)
}
if len(res) > 0 {
return apiErrors.CompositeValidationError(res...)
}
return nil

View 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
}

View 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
}

View File

@@ -17,8 +17,6 @@ CREATE TABLE IF NOT EXISTS routes (
path text NOT NULL,
app_name 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
);`
@@ -26,7 +24,7 @@ const appsTableCreate = `CREATE TABLE IF NOT EXISTS apps (
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 {
Scan(dest ...interface{}) error
@@ -162,22 +160,18 @@ func (ds *PostgresDatastore) StoreRoute(route *models.Route) (*models.Route, err
_, err = ds.db.Exec(`
INSERT INTO routes (
name, app_name, path, image,
type, container_path, headers
headers
)
VALUES ($1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (name) DO UPDATE SET
path = $3,
image = $4,
type = $5,
container_path = $6,
headers = $7;
headers = $5;
`,
route.Name,
route.AppName,
route.Path,
route.Image,
route.Type,
route.ContainerPath,
headers,
)
@@ -206,8 +200,6 @@ func scanRoute(scanner rowScanner, route *models.Route) error {
&route.Path,
&route.AppName,
&route.Image,
&route.Type,
&route.ContainerPath,
&headerStr,
)

View File

@@ -12,22 +12,28 @@ func handleAppCreate(c *gin.Context) {
store := c.MustGet("store").(models.Datastore)
log := c.MustGet("log").(logrus.FieldLogger)
app := &models.App{}
wapp := &models.AppWrapper{}
err := c.BindJSON(app)
err := c.BindJSON(wapp)
if err != nil {
log.WithError(err).Debug(models.ErrInvalidJSON)
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
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)
c.JSON(http.StatusInternalServerError, simpleError(err))
return
}
app, err = store.StoreApp(app)
app, err := store.StoreApp(wapp.App)
if err != nil {
log.WithError(err).Debug(models.ErrAppsCreate)
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))

View File

@@ -40,5 +40,5 @@ func handleAppGet(c *gin.Context) {
app.Routes = routes
c.JSON(http.StatusOK, app)
c.JSON(http.StatusOK, &models.AppWrapper{app})
}

View File

@@ -21,5 +21,5 @@ func handleAppList(c *gin.Context) {
return
}
c.JSON(http.StatusOK, apps)
c.JSON(http.StatusOK, &models.AppsWrapper{apps})
}

View File

@@ -21,14 +21,5 @@ func handleAppUpdate(c *gin.Context) {
return
}
// app.Name = c.Param("app")
// 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))
c.JSON(http.StatusOK, simpleError(models.ErrAppsNothingToUpdate))
}

View File

@@ -15,7 +15,7 @@ func Start(engine *gin.Engine) {
v1.POST("/apps", handleAppCreate)
v1.GET("/apps/:app", handleAppGet)
v1.POST("/apps/:app", handleAppUpdate)
v1.PUT("/apps/:app", handleAppUpdate)
v1.DELETE("/apps/:app", handleAppDestroy)
apps := v1.Group("/apps/:app")

View File

@@ -16,11 +16,17 @@ func handleRouteCreate(c *gin.Context) {
err := c.BindJSON(route)
if err != nil {
log.WithError(err).Debug(models.ErrInvalidJSON)
log.WithError(err).Error(models.ErrInvalidJSON)
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
return
}
if route == nil {
log.WithError(err).Error(models.ErrInvalidJSON)
c.JSON(http.StatusBadRequest, simpleError(models.ErrRoutesMissingNew))
return
}
route.AppName = c.Param("app")
if err := route.Validate(); err != nil {
@@ -46,7 +52,7 @@ func handleRouteCreate(c *gin.Context) {
route, err = store.StoreRoute(route)
if err != nil {
log.WithError(err).Debug(models.ErrRoutesCreate)
log.WithError(err).Error(models.ErrRoutesCreate)
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesCreate))
return
}

View File

@@ -24,5 +24,5 @@ func handleRouteGet(c *gin.Context) {
log.WithFields(logrus.Fields{"route": route}).Debug("Got route")
c.JSON(http.StatusOK, route)
c.JSON(http.StatusOK, &models.RouteWrapper{route})
}

View File

@@ -27,5 +27,5 @@ func handleRouteList(c *gin.Context) {
log.WithFields(logrus.Fields{"routes": routes}).Debug("Got routes")
c.JSON(http.StatusOK, routes)
c.JSON(http.StatusOK, &models.RoutesWrapper{Routes: routes})
}

View File

@@ -12,19 +12,19 @@ func handleRouteUpdate(c *gin.Context) {
store := c.MustGet("store").(models.Datastore)
log := c.MustGet("log").(logrus.FieldLogger)
route := &models.Route{}
wroute := &models.RouteWrapper{}
appName := c.Param("app")
err := c.BindJSON(route)
err := c.BindJSON(wroute)
if err != nil {
log.WithError(err).Debug(models.ErrInvalidJSON)
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
return
}
route.AppName = appName
wroute.Route.AppName = appName
route, err = store.StoreRoute(route)
route, err := store.StoreRoute(wroute.Route)
if err != nil {
log.WithError(err).Debug(models.ErrAppsCreate)
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))

324
api/swagger.yml Normal file
View 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
View File

11
docs/database/boltdb.md Normal file
View 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
View 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
```

View File

@@ -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

View File

@@ -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
View File

@@ -1,75 +1,71 @@
hash: a104a522dc2ba982d25101330bbd5d44778565128f0cb09f34061c167921046c
updated: 2016-07-14T08:40:37.798125603-07:00
hash: 2101b4c83f12c75cbc3cb5be0a7d544dd3a7d21a27ef8a5ee8b014ded8cee034
updated: 2016-07-27T16:56:51.792310167-03:00
imports:
- name: github.com/amir/raidman
version: 91c20f3f475cab75bb40ad7951d9bbdde357ade7
- name: github.com/asaskevich/govalidator
version: 593d64559f7600f29581a3ee42177f5dbded27a9
- name: github.com/boltdb/bolt
version: 5cc10bbbc5c141029940133bb33c9e969512a698
- name: github.com/gin-gonic/gin
version: 4a6bc4aac4607e253bcda67c8c5bcda693d2388e
subpackages:
- proto
- name: github.com/BurntSushi/toml
version: bec2dacf4b590d26237cfebff4471e21ce543494
- name: github.com/cactus/go-statsd-client
version: 91c326c3f7bd20f0226d3d1c289dd9f8ce28d33d
subpackages:
- statsd
- name: github.com/dgrijalva/jwt-go
version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20
- binding
- render
- name: github.com/go-openapi/analysis
version: abc9a6171f5bf03ada39aead1aa7fd7bbd44d50f
- name: github.com/go-openapi/errors
version: d24ebc2075bad502fac3a8ae27aa6dd58e1952dc
- name: github.com/go-openapi/jsonpointer
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
- 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
version: 874264fbbb43f4d91e999fecb4b40143ed611400
version: 2402d76f3d41f928c7902a765dfc872356dd3aad
subpackages:
- proto
- name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/gorilla/mux
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
- name: github.com/iron-io/common
version: 7c9faec363c808052742b4c4b84876c4c2172308
- name: github.com/iron-io/titan
version: 697a5466b096fee73202f5ddccf8213a2357e062
repo: git@github.com:iron-io/titan.git
vcs: git
subpackages:
- httpshutdown
- msgpack
- semaphore
- serverutil
- name: github.com/iron-io/go
version: 1ed3de151aa27db91d6df5dc8c8ce164c833b57c
- jobserver/models
- name: github.com/lib/pq
version: 3cd0097429be7d611bb644ef85b42bfb102ceea4
subpackages:
- common
- common/httpshutdown
- common/msgpack
- common/semaphore
- common/stats
- name: github.com/iron-io/golog
version: 5b80d97af5a2a5d386e7609efb82192ae99a7c67
- name: github.com/iron-io/iron_go
version: 920c950272e820d9e2562fcbf40303f05564f118
- oid
- name: github.com/mailru/easyjson
version: 97eee20abef76a0591155412cf0d04f24b05098f
subpackages:
- cache
- worker
- api
- config
- name: github.com/mattn/go-colorable
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
- name: github.com/mattn/go-isatty
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
- jlexer
- jwriter
- buffer
- name: github.com/manucorporat/sse
version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
- name: github.com/PuerkitoBio/purell
version: 1d5d1cfad45d42ec5f81fa8ef23de09cebc6dcc3
- name: github.com/PuerkitoBio/urlesc
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
- name: github.com/Sirupsen/logrus
version: 32055c351ea8b00b96d70f28db48d9840feaf0ec
- name: github.com/vmihailenco/bufio
version: 24e7e48f60fc2d9e99e43c07485d9fff42051e66
- name: github.com/vrischmann/envconfig
version: 9e6e1c4d3b73427d03118518603bb904d9c55236
version: a283a10442df8dc09befd873fab202bf8a253d6a
- name: golang.org/x/net
version: a728288923b47049b2ce791836767ffbe964a5bd
version: f315505cf3349909cdf013ea56690da34e96a451
subpackages:
- proxy
- context
- name: golang.org/x/sys
version: b518c298ac9dc94b6ac0757394f50d10c5dfa25a
version: a646d33e2ee3172a661fc09bca23bb4889a41bc8
subpackages:
- unix
- name: gopkg.in/inconshreveable/log15.v2
version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f
subpackages:
- stack
- term
- name: gopkg.in/mgo.v2
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
subpackages:
- bson
devImports: []
- name: gopkg.in/go-playground/validator.v8
version: c193cecd124b5cc722d7ee5538e945bdb3348435
testImports: []

View File

@@ -1,9 +1,14 @@
package: github.com/iron-io/microgateway
package: github.com/iron-io/functions
import:
- package: github.com/gorilla/mux
- package: github.com/iron-io/common
- package: github.com/iron-io/golog
- package: github.com/iron-io/iron_go
- package: github.com/Sirupsen/logrus
- package: github.com/boltdb/bolt
- package: github.com/gin-gonic/gin
- 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:
- cache
- worker
- jobserver/models
- package: github.com/lib/pq

View File

@@ -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 }