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