Skip to content

Commit

Permalink
Enable routing via /functions/ endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
alexellis committed Jan 4, 2017
1 parent f2fdfde commit 84d1c0e
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 19 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -19,9 +19,9 @@ This container acts in a similar way to the API Gateway on AWS. Requests can be

There are three options for routing:

* Routing is enabled through a `X-Function` header which matches a service name (function) directly.
* Functions created on the overlay network can be invoked by: http://localhost:8080/function/{servicename}
* Routing automatically detects Alexa SDK requests and forwards to a service name (function) that matches the Intent name
* [todo] individual routes can be set up mapping to a specific service name (function).
* Routing is enabled through a `X-Function` header which matches a service name (function) directly.

Features:

Expand Down
5 changes: 2 additions & 3 deletions gateway/build.sh
@@ -1,5 +1,4 @@
#!/bin/sh
echo Building catservice:latest

docker build -t catservice .
echo Building server:latest

docker build -t server .
48 changes: 34 additions & 14 deletions gateway/server.go
Expand Up @@ -51,6 +51,7 @@ func lookupSwarmService(serviceName string) (bool, error) {
if err != nil {
log.Fatal("Error with Docker client.")
}
fmt.Printf("Resolving: '%s'\n", serviceName)
serviceFilter := filters.NewArgs()
serviceFilter.Add("name", serviceName)
services, err := c.ServiceList(context.Background(), types.ServiceListOptions{Filters: serviceFilter})
Expand Down Expand Up @@ -95,31 +96,46 @@ func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.Metri
return
}

w.WriteHeader(http.StatusOK)
w.Write(responseBody)
seconds := time.Since(start).Seconds()
fmt.Printf("[%s] took %f seconds\n", stamp, seconds)
metrics.GatewayServerlessServedTotal.Inc()
metrics.GatewayFunctions.Observe(seconds)
}

func makeProxy(metrics metrics.MetricOptions) http.HandlerFunc {
func lookupInvoke(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, name string) {
exists, err := lookupSwarmService(name)
if err != nil || exists == false {
if err != nil {
log.Fatalln(err)
}
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Error resolving service."))
}
if exists == true {
requestBody, _ := ioutil.ReadAll(r.Body)
invokeService(w, r, metrics, name, requestBody)
}
}

func makeProxy(metrics metrics.MetricOptions, wildcard bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
metrics.GatewayRequestsTotal.Inc()

if r.Method == "POST" {
log.Println(r.Header)
header := r.Header["X-Function"]
log.Println(header)

if len(header) > 0 {
exists, err := lookupSwarmService(header[0])
if err != nil {
log.Fatalln(err)
}
if exists == true {
requestBody, _ := ioutil.ReadAll(r.Body)
invokeService(w, r, metrics, header[0], requestBody)
}
fmt.Println(wildcard)

if wildcard == true {
vars := mux.Vars(r)
name := vars["name"]
fmt.Println("invoke by name")
lookupInvoke(w, r, metrics, name)
} else if len(header) > 0 {
lookupInvoke(w, r, metrics, header[0])
} else {
requestBody, _ := ioutil.ReadAll(r.Body)
alexaService := isAlexa(requestBody)
Expand Down Expand Up @@ -160,12 +176,16 @@ func main() {
prometheus.Register(GatewayServerlessServedTotal)
prometheus.Register(GatewayFunctions)

r := mux.NewRouter()
r.HandleFunc("/", makeProxy(metrics.MetricOptions{
metricsOptions := metrics.MetricOptions{
GatewayRequestsTotal: GatewayRequestsTotal,
GatewayServerlessServedTotal: GatewayServerlessServedTotal,
GatewayFunctions: GatewayFunctions,
}))
}

r := mux.NewRouter()
r.HandleFunc("/", makeProxy(metricsOptions, false))

r.HandleFunc("/function/{name:[a-zA-Z]+}", makeProxy(metricsOptions, true))

metricsHandler := metrics.PrometheusHandler()
r.Handle("/metrics", metricsHandler)
Expand Down
11 changes: 11 additions & 0 deletions sample-functions/WebhookStash/Dockerfile
@@ -0,0 +1,11 @@
FROM golang:1.7.3
RUN mkdir -p /go/src/app
COPY handler.go /go/src/app
WORKDIR /go/src/app
RUN go get -d -v
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

COPY fwatchdog /usr/bin/

ENV fprocess="/go/src/app/app"
CMD ["fwatchdog"]
18 changes: 18 additions & 0 deletions sample-functions/WebhookStash/README.md
@@ -0,0 +1,18 @@
WebhookStash
============

Example serverless function shows how to stash way contents of webhooks called via API gateway.

Each file is saved with the UNIX timestamp in nano seconds plus an extension of .txt

Example:

```
# curl -X POST -v -d @$HOME/.ssh/id_rsa.pub localhost:8080/function/webhookstash
```

Then if you find the replica you can check the disk:

```
# docker exec webhookstash.1.z054csrh70tgk9s5k4bb8uefq find
```
18 changes: 18 additions & 0 deletions sample-functions/WebhookStash/handler.go
@@ -0,0 +1,18 @@
package main

import (
"fmt"
"io/ioutil"
"os"
"strconv"
"time"
)

func main() {
input, _ := ioutil.ReadAll(os.Stdin)
fmt.Println("Stashing request")
now := time.Now()
stamp := strconv.FormatInt(now.UnixNano(), 10)

ioutil.WriteFile(stamp+".txt", input, 0644)
}

0 comments on commit 84d1c0e

Please sign in to comment.