From 84d1c0eaef36ac8fd3e2363d373fd6901210e323 Mon Sep 17 00:00:00 2001 From: Alex <alexellis2@gmail.com> Date: Wed, 4 Jan 2017 09:14:21 +0000 Subject: [PATCH] Enable routing via /functions/ endpoint --- README.md | 4 +- gateway/build.sh | 5 +-- gateway/server.go | 48 +++++++++++++++++------- sample-functions/WebhookStash/Dockerfile | 11 ++++++ sample-functions/WebhookStash/README.md | 18 +++++++++ sample-functions/WebhookStash/handler.go | 18 +++++++++ 6 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 sample-functions/WebhookStash/Dockerfile create mode 100644 sample-functions/WebhookStash/README.md create mode 100644 sample-functions/WebhookStash/handler.go diff --git a/README.md b/README.md index c8156c94..b638dad3 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/gateway/build.sh b/gateway/build.sh index 7787c530..3b05335b 100755 --- a/gateway/build.sh +++ b/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 . diff --git a/gateway/server.go b/gateway/server.go index da9d4c06..6b00b139 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -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}) @@ -95,6 +96,7 @@ 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) @@ -102,7 +104,22 @@ func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.Metri 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() @@ -110,16 +127,15 @@ func makeProxy(metrics metrics.MetricOptions) http.HandlerFunc { 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) @@ -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) diff --git a/sample-functions/WebhookStash/Dockerfile b/sample-functions/WebhookStash/Dockerfile new file mode 100644 index 00000000..fda40f3e --- /dev/null +++ b/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"] diff --git a/sample-functions/WebhookStash/README.md b/sample-functions/WebhookStash/README.md new file mode 100644 index 00000000..2d26b5b6 --- /dev/null +++ b/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 +``` diff --git a/sample-functions/WebhookStash/handler.go b/sample-functions/WebhookStash/handler.go new file mode 100644 index 00000000..5e41b6b1 --- /dev/null +++ b/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) +} -- GitLab