mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Many updates, working ok at this point.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,3 +10,5 @@ app.zip
|
||||
vendor/
|
||||
/microgateway
|
||||
/gateway
|
||||
|
||||
private.sh
|
||||
|
||||
41
README.md
41
README.md
@@ -1,31 +1,39 @@
|
||||
|
||||
Note: currently running at: http://gateway.iron.computer:8080/
|
||||
|
||||
# MicroServices Gateway / API Gateway
|
||||
|
||||
First things first, register an app:
|
||||
First things first, create an app/service:
|
||||
|
||||
TOOD: App or service??
|
||||
|
||||
```sh
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"name":"myapp","password":"xyz"}' http://localhost:8080/test/1/projects/123/apps
|
||||
iron create app
|
||||
# OR
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"name":"myapp"}' http://localhost:8080/api/v1/apps
|
||||
```
|
||||
|
||||
Now add routes to the app. First we'll add a route to the output of a docker container:
|
||||
|
||||
```sh
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/hello.rb","image":"treeder/hello.rb", "type":"run"}' http://localhost:8080/test/1/projects/123/apps/myapp/routes
|
||||
iron add route myapp /hello iron/hello:0.0.1
|
||||
# OR
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/hello", "image":"iron/hello", "type":"run"}' http://localhost:8080/api/v1/apps/myapp/routes
|
||||
```
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/helloiron","image":"iron/hello", "type":"run"}' http://localhost:8080/test/1/projects/123/apps/myapp/routes
|
||||
And how about a [slackbot](https://github.com/treeder/slackbots/tree/master/guppy) endpoint:
|
||||
|
||||
```sh
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/guppy","image":"treeder/guppy:0.0.2"}' http://localhost:8080/api/v1/apps/myapp/routes
|
||||
```
|
||||
|
||||
Test out the route:
|
||||
|
||||
```sh
|
||||
curl -i -X GET http://localhost:8080/hello.rb?app=myapp
|
||||
```
|
||||
Surf to: http://localhost:8080/hello?app=myapp
|
||||
|
||||
Now try mapping an app endpoint:
|
||||
|
||||
```sh
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/sinatra","image":"treeder/hello-sinatra", "type":"app", "cpath":"/"}' http://localhost:8080/test/1/projects/123/apps/myapp/routes
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/sinatra","image":"treeder/hello-sinatra", "type":"app", "cpath":"/"}' http://localhost:8080/api/v1/apps/myapp/routes
|
||||
```
|
||||
|
||||
And test it out:
|
||||
@@ -37,7 +45,7 @@ curl -i -X GET http://localhost:8080/sinatra?app=myapp
|
||||
And another:
|
||||
|
||||
```sh
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/sinatra/ping","image":"treeder/hello-sinatra", "type":"app", "cpath":"/ping"}' http://localhost:8080/test/1/projects/123/apps/myapp/routes
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"path":"/sinatra/ping","image":"treeder/hello-sinatra", "type":"app", "cpath":"/ping"}' http://localhost:8080/api/v1/apps/myapp/routes
|
||||
```
|
||||
|
||||
And test it out:
|
||||
@@ -49,7 +57,7 @@ curl -i -X GET http://localhost:8080/sinatra?app=myapp
|
||||
You'all also get a custom URL like this when in production.
|
||||
|
||||
```
|
||||
appname.projectid.iron.computer
|
||||
appname.iron.computer
|
||||
```
|
||||
|
||||
## Building/Testing
|
||||
@@ -66,7 +74,7 @@ glide install
|
||||
Test it, the iron token and project id are for cache.
|
||||
|
||||
```sh
|
||||
docker run -e "IRON_TOKEN=GP8cqlKSrcpmqeR8x9WKD4qSAss" -e "IRON_PROJECT_ID=4fd2729368a0197d1102056b" --rm -it --privileged -p 8080:8080 iron/gateway
|
||||
docker run -e "IRON_TOKEN=GP8cqlKSrcpmqeR8x9WKD4qSAss" -e "IRON_PROJECT_ID=4fd2729368a0197d1102056b" -e "CLOUDFLARE_EMAIL=treeder@gmail.com" -e "CLOUDFLARE_API_KEY=X" --rm -it --privileged -p 8080:8080 iron/gateway
|
||||
```
|
||||
|
||||
Push it:
|
||||
@@ -82,3 +90,12 @@ After deploying, running it with:
|
||||
```sh
|
||||
docker run -e "IRON_TOKEN=GP8cqlKSrcpmqeR8x9WKD4qSAss" -e "IRON_PROJECT_ID=4fd2729368a0197d1102056b" --name irongateway -it --privileged --net=host -p 8080:8080 -d --name irongateway iron/gateway
|
||||
```
|
||||
|
||||
## TODOS
|
||||
|
||||
* [ ] Check if image exists when registering the endpoint, not at run time
|
||||
* [ ] Put stats into influxdb or something to show to user (requests, errors)
|
||||
* [ ] Store recent logs for user.
|
||||
* [ ] Allow env vars for config on the app and routes (routes override apps).
|
||||
* [ ] Provide a base url for each app, eg: appname.userid.iron.computer
|
||||
* [ ] Allow setting content-type on a route, then use that when responding
|
||||
|
||||
12
dns.go
12
dns.go
@@ -7,8 +7,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/iron-io/go/common"
|
||||
"gopkg.in/inconshreveable/log15.v2"
|
||||
)
|
||||
|
||||
type CloudFlareResult struct {
|
||||
@@ -30,7 +30,7 @@ func registerHost(w http.ResponseWriter, r *http.Request, app *App) bool {
|
||||
// WORKER_NAME.PROJECT_ID.iron.computer.
|
||||
dnsHost := fmt.Sprintf("%v.%v.iron.computer", app.Name, 123)
|
||||
app.Dns = dnsHost
|
||||
log15.Info("registering dns", "dnsname", dnsHost)
|
||||
log.Info("registering dns", "dnsname", dnsHost)
|
||||
|
||||
if app.CloudFlareId == "" {
|
||||
// Tad hacky, but their go lib is pretty confusing.
|
||||
@@ -53,7 +53,7 @@ func registerHost(w http.ResponseWriter, r *http.Request, app *App) bool {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log15.Error("Could not register dns entry.", "err", err)
|
||||
log.Error("Could not register dns entry.", "err", err)
|
||||
common.SendError(w, 500, fmt.Sprint("Could not register dns entry.", err))
|
||||
return false
|
||||
}
|
||||
@@ -61,14 +61,14 @@ func registerHost(w http.ResponseWriter, r *http.Request, app *App) bool {
|
||||
// todo: get error message from body for bad status code
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if resp.StatusCode != 200 {
|
||||
log15.Error("Could not register dns entry 2.", "code", resp.StatusCode, "body", string(body))
|
||||
log.Error("Could not register dns entry 2.", "code", resp.StatusCode, "body", string(body))
|
||||
common.SendError(w, 500, fmt.Sprint("Could not register dns entry 2. ", resp.StatusCode))
|
||||
return false
|
||||
}
|
||||
cfResult := CloudFlareResponse{}
|
||||
err = json.Unmarshal(body, &cfResult)
|
||||
if err != nil {
|
||||
log15.Error("Could not parse DNS response.", "err", err, "code", resp.StatusCode, "body", string(body))
|
||||
log.Error("Could not parse DNS response.", "err", err, "code", resp.StatusCode, "body", string(body))
|
||||
common.SendError(w, 500, fmt.Sprint("Could not parse DNS response. ", resp.StatusCode))
|
||||
return false
|
||||
}
|
||||
@@ -76,6 +76,6 @@ func registerHost(w http.ResponseWriter, r *http.Request, app *App) bool {
|
||||
app.CloudFlareId = cfResult.Result.Id
|
||||
}
|
||||
|
||||
log15.Info("host registered successfully with cloudflare", "app", app)
|
||||
log.Info("host registered successfully with cloudflare", "app", app)
|
||||
return true
|
||||
}
|
||||
|
||||
23
gateway.pem
Normal file
23
gateway.pem
Normal file
@@ -0,0 +1,23 @@
|
||||
-----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-----
|
||||
10
glide.lock
generated
10
glide.lock
generated
@@ -1,5 +1,5 @@
|
||||
hash: a104a522dc2ba982d25101330bbd5d44778565128f0cb09f34061c167921046c
|
||||
updated: 2016-06-15T17:17:05.21953321-07:00
|
||||
updated: 2016-06-17T14:27:42.156301621-07:00
|
||||
imports:
|
||||
- name: github.com/amir/raidman
|
||||
version: 91c20f3f475cab75bb40ad7951d9bbdde357ade7
|
||||
@@ -12,7 +12,7 @@ imports:
|
||||
subpackages:
|
||||
- statsd
|
||||
- name: github.com/dgrijalva/jwt-go
|
||||
version: c04502f106d7c5b3fae17c5da49a1bbdd3006b3c
|
||||
version: f0777076321ab64f6efc15a82d9d23b98539b943
|
||||
- name: github.com/golang/protobuf
|
||||
version: 0c1f6d65b5a189c2250d10e71a5506f06f9fa0a0
|
||||
subpackages:
|
||||
@@ -29,7 +29,7 @@ imports:
|
||||
- semaphore
|
||||
- serverutil
|
||||
- name: github.com/iron-io/go
|
||||
version: 277190779e20ad6bff386a5f7f6a8009d73fa475
|
||||
version: 6b082f50f20cb0e0146bdf0ffbc6c07216eeea77
|
||||
subpackages:
|
||||
- common
|
||||
- common/httpshutdown
|
||||
@@ -50,12 +50,14 @@ imports:
|
||||
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: f3cfb454f4c209e6668c95216c4744b8fddb2356
|
||||
- name: github.com/vmihailenco/bufio
|
||||
version: 24e7e48f60fc2d9e99e43c07485d9fff42051e66
|
||||
- name: github.com/vrischmann/envconfig
|
||||
version: 9e6e1c4d3b73427d03118518603bb904d9c55236
|
||||
- name: golang.org/x/net
|
||||
version: 3f122ce3dbbe488b7e6a8bdb26f41edec852a40b
|
||||
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab
|
||||
subpackages:
|
||||
- proxy
|
||||
- name: golang.org/x/sys
|
||||
|
||||
10
models.go
10
models.go
@@ -17,11 +17,11 @@ type Route2 struct {
|
||||
|
||||
// An app is that base object for api gateway
|
||||
type App struct {
|
||||
Name string `json:"name"`
|
||||
ProjectId string `json:"project_id"`
|
||||
CloudFlareId string `json:"-"`
|
||||
Dns string `json:"dns"`
|
||||
Routes []Route3 `json:"routes"`
|
||||
Name string `json:"name"`
|
||||
ProjectId string `json:"project_id"`
|
||||
CloudFlareId string `json:"-"`
|
||||
Dns string `json:"dns"`
|
||||
Routes map[string]*Route3 `json:"routes"`
|
||||
}
|
||||
|
||||
// this is for the new api gateway
|
||||
|
||||
140
router.go
140
router.go
@@ -11,20 +11,19 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/iron-io/go/common"
|
||||
"github.com/iron-io/golog"
|
||||
"github.com/iron-io/iron_go/cache"
|
||||
"github.com/iron-io/iron_go/worker"
|
||||
"gopkg.in/inconshreveable/log15.v2"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/iron-io/go/common"
|
||||
"github.com/iron-io/iron_go/cache"
|
||||
"github.com/iron-io/iron_go/worker"
|
||||
)
|
||||
|
||||
var config struct {
|
||||
@@ -82,11 +81,12 @@ func main() {
|
||||
common.LoadConfigFile(configFile, &config)
|
||||
// common.SetLogging(common.LoggingConfig{To: config.Logging.To, Level: config.Logging.Level, Prefix: config.Logging.Prefix})
|
||||
|
||||
log.Println("config:", config)
|
||||
golog.Infoln("Starting up router version", version)
|
||||
// TODO: validate inputs, iron tokens, cloudflare stuff, etc
|
||||
config.CloudFlare.Email = os.Getenv("CLOUDFLARE_EMAIL")
|
||||
config.CloudFlare.AuthKey = os.Getenv("CLOUDFLARE_API_KEY")
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
log.Println("Running on", runtime.NumCPU(), "CPUs")
|
||||
log.Println("config:", config)
|
||||
log.Infoln("Starting up router version", version)
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
@@ -95,35 +95,34 @@ func main() {
|
||||
s2.Handle("/", &WorkerHandler{})
|
||||
|
||||
// dev:
|
||||
fmt.Println("Setting /test subrouter")
|
||||
s := r.PathPrefix("/test").Subrouter()
|
||||
s := r.PathPrefix("/api").Subrouter()
|
||||
// production:
|
||||
// s := r.Host("router.irondns.info").Subrouter()
|
||||
s.Handle("/1/projects/{project_id}/register", &Register{})
|
||||
s.Handle("/1/projects/{project_id}/apps", &NewApp{})
|
||||
s.HandleFunc("/1/projects/{project_id}/apps/{app_name}/routes", NewRoute)
|
||||
// s.Handle("/1/projects/{project_id}/register", &Register{})
|
||||
s.Handle("/v1/apps", &NewApp{})
|
||||
s.HandleFunc("/v1/apps/{app_name}/routes", NewRoute)
|
||||
s.HandleFunc("/ping", Ping)
|
||||
s.HandleFunc("/version", Version)
|
||||
s.Handle("/addworker", &WorkerHandler{})
|
||||
// s.Handle("/addworker", &WorkerHandler{})
|
||||
s.HandleFunc("/", Ping)
|
||||
|
||||
r.HandleFunc("/elb-ping-router", Ping) // for ELB health check
|
||||
|
||||
// for testing app responses, pass in app name, can use localhost
|
||||
s4 := r.Queries("app", "").Subrouter()
|
||||
s4.HandleFunc("/appsr", Ping)
|
||||
// s4.HandleFunc("/appsr", Ping)
|
||||
s4.HandleFunc("/{rest:.*}", Run)
|
||||
s4.NotFoundHandler = http.HandlerFunc(Run)
|
||||
|
||||
s3 := r.Queries("rhost", "").Subrouter()
|
||||
s3.HandleFunc("/", ProxyFunc2)
|
||||
// s3 := r.Queries("rhost", "").Subrouter()
|
||||
// s3.HandleFunc("/", ProxyFunc2)
|
||||
|
||||
// This is where all the main incoming traffic goes
|
||||
r.NotFoundHandler = http.HandlerFunc(Run)
|
||||
|
||||
http.Handle("/", r)
|
||||
port := 8080
|
||||
golog.Infoln("Router started, listening and serving on port", port)
|
||||
log.Infoln("Router started, listening and serving on port", port)
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%v", port), nil))
|
||||
}
|
||||
|
||||
@@ -133,10 +132,10 @@ func ProxyFunc2(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
func ProxyFunc(w http.ResponseWriter, req *http.Request) {
|
||||
golog.Infoln("HOST:", req.Host)
|
||||
log.Infoln("HOST:", req.Host)
|
||||
host := strings.Split(req.Host, ":")[0]
|
||||
rhost := req.FormValue("rhost")
|
||||
golog.Infoln("rhost:", rhost)
|
||||
log.Infoln("rhost:", rhost)
|
||||
if rhost != "" {
|
||||
host = rhost
|
||||
}
|
||||
@@ -146,10 +145,10 @@ func ProxyFunc(w http.ResponseWriter, req *http.Request) {
|
||||
// 2) This host has active workers so we do the proxy
|
||||
// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
|
||||
// route := routingTable[host]
|
||||
golog.Infoln("getting route for host:", host, "--")
|
||||
log.Infoln("getting route for host:", host, "--")
|
||||
route, err := getRoute(host)
|
||||
golog.Infoln("route:", route)
|
||||
golog.Infoln("err:", err)
|
||||
log.Infoln("route:", route)
|
||||
log.Infoln("err:", err)
|
||||
if err != nil {
|
||||
common.SendError(w, 400, fmt.Sprintln("Host not registered or error!", err))
|
||||
return
|
||||
@@ -165,13 +164,13 @@ func ProxyFunc(w http.ResponseWriter, req *http.Request) {
|
||||
func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
|
||||
dlen := len(route.Destinations)
|
||||
if dlen == 0 {
|
||||
golog.Infoln("No workers running, starting new task.")
|
||||
log.Infoln("No workers running, starting new task.")
|
||||
startNewWorker(route)
|
||||
common.SendError(w, 500, fmt.Sprintln("No workers running, starting them up..."))
|
||||
return
|
||||
}
|
||||
if dlen < 3 {
|
||||
golog.Infoln("Less than three workers running, starting a new task.")
|
||||
log.Infoln("Less than three workers running, starting a new task.")
|
||||
startNewWorker(route)
|
||||
}
|
||||
destIndex := rand.Intn(dlen)
|
||||
@@ -181,21 +180,21 @@ func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
|
||||
destUrl, err := url.Parse(destUrlString2)
|
||||
if err != nil {
|
||||
removeDestination(route, destIndex, w)
|
||||
golog.Infoln("error!", err)
|
||||
log.Infoln("error!", err)
|
||||
common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
|
||||
return
|
||||
}
|
||||
// todo: check destination runtime and remove it if it's expired so we don't send requests to an endpoint that is about to be killed
|
||||
golog.Infoln("proxying to", destUrl)
|
||||
log.Infoln("proxying to", destUrl)
|
||||
proxy := NewSingleHostReverseProxy(destUrl)
|
||||
err = proxy.ServeHTTP(w, req)
|
||||
if err != nil {
|
||||
golog.Infoln("Error proxying!", err)
|
||||
log.Infoln("Error proxying!", err)
|
||||
etype := reflect.TypeOf(err)
|
||||
golog.Infoln("err type:", etype)
|
||||
log.Infoln("err type:", etype)
|
||||
// can't figure out how to compare types so comparing strings.... lame.
|
||||
if true || strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
|
||||
golog.Infoln("It's a network error so we're going to remove destination.")
|
||||
log.Infoln("It's a network error so we're going to remove destination.")
|
||||
removeDestination(route, destIndex, w)
|
||||
serveEndpoint(w, req, route)
|
||||
return
|
||||
@@ -203,28 +202,28 @@ func serveEndpoint(w http.ResponseWriter, req *http.Request, route *Route) {
|
||||
common.SendError(w, 500, fmt.Sprintln("Internal error occurred:", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("Served!")
|
||||
log.Infoln("Served!")
|
||||
}
|
||||
|
||||
func removeDestination(route *Route, destIndex int, w http.ResponseWriter) {
|
||||
golog.Infoln("Removing destination", destIndex, "from route:", route)
|
||||
log.Infoln("Removing destination", destIndex, "from route:", route)
|
||||
route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex+1:]...)
|
||||
err := putRoute(route)
|
||||
if err != nil {
|
||||
golog.Infoln("Couldn't update routing table:", err)
|
||||
log.Infoln("Couldn't update routing table:", err)
|
||||
common.SendError(w, 500, fmt.Sprintln("couldn't update routing table", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("New route after remove destination:", route)
|
||||
log.Infoln("New route after remove destination:", route)
|
||||
// if len(route.Destinations) < 3 {
|
||||
// golog.Infoln("After network error, there are less than three destinations, so starting a new one. ")
|
||||
// log.Infoln("After network error, there are less than three destinations, so starting a new one. ")
|
||||
// // always want at least three running
|
||||
// startNewWorker(route)
|
||||
// }
|
||||
}
|
||||
|
||||
func startNewWorker(route *Route) error {
|
||||
golog.Infoln("Starting a new worker")
|
||||
log.Infoln("Starting a new worker")
|
||||
// start new worker
|
||||
payload := map[string]interface{}{
|
||||
"token": config.Iron.SuperToken,
|
||||
@@ -238,7 +237,7 @@ func startNewWorker(route *Route) error {
|
||||
workerapi.Settings.UseConfigMap(payload)
|
||||
jsonPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
golog.Infoln("Couldn't marshal json!", err)
|
||||
log.Infoln("Couldn't marshal json!", err)
|
||||
return err
|
||||
}
|
||||
timeout := time.Second * time.Duration(1800+rand.Intn(600)) // a little random factor in here to spread out worker deaths
|
||||
@@ -250,9 +249,9 @@ func startNewWorker(route *Route) error {
|
||||
tasks := make([]worker.Task, 1)
|
||||
tasks[0] = task
|
||||
taskIds, err := workerapi.TaskQueue(tasks...)
|
||||
golog.Infoln("Tasks queued.", taskIds)
|
||||
log.Infoln("Tasks queued.", taskIds)
|
||||
if err != nil {
|
||||
golog.Infoln("Couldn't queue up worker!", err)
|
||||
log.Infoln("Couldn't queue up worker!", err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
@@ -267,13 +266,13 @@ func (r *Register) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
projectId := vars["project_id"]
|
||||
token := common.GetToken(req)
|
||||
golog.Infoln("project_id:", projectId, "token:", token.Token)
|
||||
log.Infoln("project_id:", projectId, "token:", token.Token)
|
||||
|
||||
route := Route{}
|
||||
if !common.ReadJSON(w, req, &route) {
|
||||
return
|
||||
}
|
||||
golog.Infoln("body read into route:", route)
|
||||
log.Infoln("body read into route:", route)
|
||||
route.ProjectId = projectId
|
||||
route.Token = token.Token
|
||||
|
||||
@@ -287,11 +286,11 @@ func (r *Register) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// todo: do we need to close body?
|
||||
err = putRoute(&route)
|
||||
if err != nil {
|
||||
golog.Infoln("couldn't register host:", err)
|
||||
log.Infoln("couldn't register host:", err)
|
||||
common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("registered route:", route)
|
||||
log.Infoln("registered route:", route)
|
||||
fmt.Fprintln(w, "Host registered successfully.")
|
||||
}
|
||||
|
||||
@@ -304,13 +303,13 @@ func (r *NewApp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
projectId := vars["project_id"]
|
||||
// token := common.GetToken(req)
|
||||
golog.Infoln("project_id:", projectId)
|
||||
log.Infoln("project_id:", projectId)
|
||||
|
||||
app := App{}
|
||||
if !common.ReadJSON(w, req, &app) {
|
||||
return
|
||||
}
|
||||
golog.Infoln("body read into app:", app)
|
||||
log.Infoln("body read into app:", app)
|
||||
app.ProjectId = projectId
|
||||
|
||||
_, err := getApp(app.Name)
|
||||
@@ -319,9 +318,11 @@ func (r *NewApp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
app.Routes = make(map[string]*Route3)
|
||||
|
||||
// create dns entry
|
||||
// TODO: Add project id to this. eg: appname.projectid.iron.computer
|
||||
log15.Debug("Creating dns entry.")
|
||||
log.Debug("Creating dns entry.")
|
||||
regOk := registerHost(w, req, &app)
|
||||
if !regOk {
|
||||
return
|
||||
@@ -330,11 +331,11 @@ func (r *NewApp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// todo: do we need to close body?
|
||||
err = putApp(&app)
|
||||
if err != nil {
|
||||
golog.Infoln("couldn't create app:", err)
|
||||
log.Infoln("couldn't create app:", err)
|
||||
common.SendError(w, 400, fmt.Sprintln("Could not create app!", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("registered app:", app)
|
||||
log.Infoln("registered app:", app)
|
||||
v := map[string]interface{}{"app": app}
|
||||
common.SendSuccess(w, "App created successfully.", v)
|
||||
}
|
||||
@@ -344,13 +345,13 @@ func NewRoute(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
projectId := vars["project_id"]
|
||||
appName := vars["app_name"]
|
||||
golog.Infoln("project_id:", projectId, "app_name", appName)
|
||||
log.Infoln("project_id:", projectId, "app_name", appName)
|
||||
|
||||
route := Route3{}
|
||||
route := &Route3{}
|
||||
if !common.ReadJSON(w, req, &route) {
|
||||
return
|
||||
}
|
||||
golog.Infoln("body read into route:", route)
|
||||
log.Infoln("body read into route:", route)
|
||||
|
||||
// TODO: validate route
|
||||
|
||||
@@ -360,14 +361,19 @@ func NewRoute(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
app.Routes = append(app.Routes, route)
|
||||
if route.Type == "" {
|
||||
route.Type = "run"
|
||||
}
|
||||
|
||||
// app.Routes = append(app.Routes, route)
|
||||
app.Routes[route.Path] = route
|
||||
err = putApp(app)
|
||||
if err != nil {
|
||||
golog.Errorln("Couldn't create route!:", err)
|
||||
log.Errorln("Couldn't create route!:", err)
|
||||
common.SendError(w, 400, fmt.Sprintln("Could not create route!", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("Route created:", route)
|
||||
log.Infoln("Route created:", route)
|
||||
fmt.Fprintln(w, "Route created successfully.")
|
||||
}
|
||||
|
||||
@@ -376,13 +382,13 @@ type WorkerHandler struct {
|
||||
|
||||
// When a worker starts up, it calls this
|
||||
func (wh *WorkerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
golog.Infoln("AddWorker called!")
|
||||
log.Infoln("AddWorker called!")
|
||||
|
||||
// get project id and token
|
||||
projectId := req.FormValue("project_id")
|
||||
token := req.FormValue("token")
|
||||
// codeName := req.FormValue("code_name")
|
||||
golog.Infoln("project_id:", projectId, "token:", token)
|
||||
log.Infoln("project_id:", projectId, "token:", token)
|
||||
|
||||
// check header for what operation to perform
|
||||
routerHeader := req.Header.Get("Iron-Router")
|
||||
@@ -394,28 +400,28 @@ func (wh *WorkerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
// todo: do we need to close body?
|
||||
golog.Infoln("Incoming body from worker:", r2)
|
||||
log.Infoln("Incoming body from worker:", r2)
|
||||
route, err := getRoute(r2.Host)
|
||||
if err != nil {
|
||||
common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("ROUTE:", route)
|
||||
log.Infoln("ROUTE:", route)
|
||||
route.Destinations = append(route.Destinations, r2.Dest)
|
||||
golog.Infoln("ROUTE new:", route)
|
||||
log.Infoln("ROUTE new:", route)
|
||||
err = putRoute(route)
|
||||
if err != nil {
|
||||
golog.Infoln("couldn't register host:", err)
|
||||
log.Infoln("couldn't register host:", err)
|
||||
common.SendError(w, 400, fmt.Sprintln("Could not register host!", err))
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(w, "Worker added")
|
||||
golog.Infoln("Worked added.")
|
||||
log.Infoln("Worked added.")
|
||||
}
|
||||
}
|
||||
|
||||
func getRoute(host string) (*Route, error) {
|
||||
golog.Infoln("getRoute for host:", host)
|
||||
log.Infoln("getRoute for host:", host)
|
||||
rx, err := icache.Get(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -441,7 +447,7 @@ func putRoute(route *Route) error {
|
||||
}
|
||||
|
||||
func getApp(name string) (*App, error) {
|
||||
golog.Infoln("getapp:", name)
|
||||
log.Infoln("getapp:", name)
|
||||
rx, err := icache.Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
48
runner.go
48
runner.go
@@ -4,8 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/iron-io/go/common"
|
||||
"github.com/iron-io/golog"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
@@ -13,7 +11,9 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/iron-io/go/common"
|
||||
)
|
||||
|
||||
type RunningApp struct {
|
||||
@@ -31,15 +31,15 @@ func init() {
|
||||
|
||||
func Run(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Println("RUN!!!!")
|
||||
golog.Infoln("HOST:", req.Host)
|
||||
log.Infoln("HOST:", req.Host)
|
||||
appName := req.FormValue("app")
|
||||
golog.Infoln("app_name", appName, "path:", req.URL.Path)
|
||||
log.Infoln("app_name", appName, "path:", req.URL.Path)
|
||||
if appName != "" {
|
||||
// passed in the name
|
||||
} else {
|
||||
host := strings.Split(req.Host, ":")[0]
|
||||
appName = strings.Split(host, ".")[0]
|
||||
golog.Infoln("app_name from host", appName)
|
||||
log.Infoln("app_name from host", appName)
|
||||
}
|
||||
|
||||
app, err := getApp(appName)
|
||||
@@ -47,7 +47,7 @@ func Run(w http.ResponseWriter, req *http.Request) {
|
||||
common.SendError(w, 400, fmt.Sprintln("This app does not exist. Please create app first.", err))
|
||||
return
|
||||
}
|
||||
golog.Infoln("app", app)
|
||||
log.Infoln("app", app)
|
||||
|
||||
// find route
|
||||
for _, el := range app.Routes {
|
||||
@@ -60,11 +60,11 @@ func Run(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
if el.Type == "app" {
|
||||
DockerHost(&el, w)
|
||||
DockerHost(el, w)
|
||||
return
|
||||
} else { // "run"
|
||||
// TODO: timeout 59 seconds
|
||||
DockerRun(el.Image, w)
|
||||
DockerRun(el.Image, w, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -73,8 +73,18 @@ func Run(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
// TODO: use Docker utils from docker-job for this and a few others in here
|
||||
func DockerRun(image string, w http.ResponseWriter) {
|
||||
cmd := exec.Command("docker", "run", "--rm", "-i", image)
|
||||
func DockerRun(image string, w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
payload, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
log.WithError(err).Errorln("Error reading request body")
|
||||
return
|
||||
}
|
||||
log.WithField("payload", "---"+string(payload)+"---").Infoln("incoming request")
|
||||
log.WithField("image", image).Infoln("About to run using this image")
|
||||
|
||||
// TODO: swap all this out with Titan's running via API
|
||||
cmd := exec.Command("docker", "run", "--rm", "-i", "-e", fmt.Sprintf("PAYLOAD=%v", string(payload)), image)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -94,14 +104,18 @@ func DockerRun(image string, w http.ResponseWriter) {
|
||||
|
||||
log.Printf("Waiting for command to finish...")
|
||||
if err = cmd.Wait(); err != nil {
|
||||
// this probably shouldn't be fatal? test with iron/error image
|
||||
log.Fatal(err)
|
||||
// job failed
|
||||
log.Infoln("job finished with err:", err)
|
||||
log.WithFields(log.Fields{"metric": "run.errors", "value": 1, "type": "count"}).Infoln("failed run")
|
||||
// TODO: wrap error in json "error": buff
|
||||
} else {
|
||||
log.Infoln("Docker ran successfully:", b.String())
|
||||
// print
|
||||
log.WithFields(log.Fields{"metric": "run.success", "value": 1, "type": "count"}).Infoln("successful run")
|
||||
}
|
||||
log.WithFields(log.Fields{"metric": "ran", "value": 1, "type": "count"}).Infoln("")
|
||||
log.Printf("Command finished with error: %v", err)
|
||||
log.WithFields(log.Fields{"metric": "run", "value": 1, "type": "count"}).Infoln("job ran")
|
||||
buff.Flush()
|
||||
golog.Infoln("Docker ran successfully:", b.String())
|
||||
fmt.Fprintln(w, b.String())
|
||||
fmt.Fprintln(w, string(bytes.Trim(b.Bytes(), "\x00")))
|
||||
}
|
||||
|
||||
func DockerHost(el *Route3, w http.ResponseWriter) {
|
||||
@@ -120,8 +134,8 @@ func DockerHost(el *Route3, w http.ResponseWriter) {
|
||||
// TODO: Need to catch interrupt and stop all containers that are started, see devo/dj for how to do this
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
// TODO: What if the app fails to start? Don't want to keep starting the container
|
||||
}
|
||||
// TODO: What if the app fails to start? Don't want to keep starting the container
|
||||
} else {
|
||||
// TODO: check if it's still running?
|
||||
// TODO: if ra.terminated, then start new container?
|
||||
|
||||
Reference in New Issue
Block a user