Skip to content
Snippets Groups Projects
Commit 9656b530 authored by Alex Ellis's avatar Alex Ellis
Browse files

Enable routing to Alexa functions

Add first Alexa-compatible function HostnameIntent
parent 2b9a1c10
No related branches found
No related tags found
No related merge requests found
faas @ d94cfeb6
Subproject commit d94cfeb660705028b6c101412a03519a8164712d
gateway
sample.txt
from golang:1.7.3
RUN go get -d github.com/docker/docker/api/types \
&& go get -d github.com/docker/docker/api/types/filters \
&& go get -d github.com/docker/docker/api/types/swarm \
&& go get -d github.com/docker/docker/client
RUN go get github.com/gorilla/mux \
&& go get github.com/prometheus/client_golang/prometheus
&& go get -d github.com/docker/docker/api/types/filters \
&& go get -d github.com/docker/docker/api/types/swarm \
&& go get -d github.com/docker/docker/client \
&& go get github.com/gorilla/mux \
&& go get github.com/prometheus/client_golang/prometheus
WORKDIR /go/src/github.com/alexellis/faas/gateway
......
......@@ -3,13 +3,16 @@ package main
import (
"bytes"
"context"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"
"io/ioutil"
"strconv"
"encoding/json"
"github.com/alexellis/faas/gateway/metrics"
"github.com/docker/docker/api/types"
......@@ -19,6 +22,28 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
type AlexaSessionApplication struct {
ApplicationId string `json:"applicationId"`
}
type AlexaSession struct {
SessionId string `json:"sessionId"`
Application AlexaSessionApplication `json:"application"`
}
type AlexaIntent struct {
Name string `json:"name"`
}
type AlexaRequest struct {
Intent AlexaIntent `json:"intent"`
}
type AlexaRequestBody struct {
Session AlexaSession `json:"session"`
Request AlexaRequest `json:"request"`
}
func lookupSwarmService(serviceName string) (bool, error) {
var c *client.Client
var err error
......@@ -33,38 +58,69 @@ func lookupSwarmService(serviceName string) (bool, error) {
return len(services) > 0, err
}
func isAlexa(requestBody []byte) AlexaRequestBody {
body := AlexaRequestBody{}
buf := bytes.NewBuffer(requestBody)
fmt.Println(buf)
str := buf.String()
parts := strings.Split(str, "sessionId")
if len(parts) > 0 {
json.Unmarshal(requestBody, &body)
fmt.Println("Alexa SDK request found")
fmt.Printf("Session=%s, Intent=%s, App=%s\n", body.Session.SessionId, body.Request.Intent, body.Session.Application.ApplicationId)
}
return body
}
func invokeService(w http.ResponseWriter, r *http.Request, metrics metrics.MetricOptions, service string, requestBody []byte) {
start := time.Now()
buf := bytes.NewBuffer(requestBody)
url := "http://" + service + ":" + strconv.Itoa(8080) + "/"
fmt.Println("Forwarding request to ", url)
response, err := http.Post(url, "text/plain", buf)
if err != nil {
log.Fatalln(err)
}
responseBody, _ := ioutil.ReadAll(response.Body)
w.Write(responseBody)
seconds := time.Since(start).Seconds()
fmt.Printf("Took %f\n", seconds)
metrics.GatewayServerlessServedTotal.Inc()
metrics.GatewayFunctions.Observe(seconds)
}
func makeProxy(metrics metrics.MetricOptions) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
metrics.GatewayRequestsTotal.Inc()
start := time.Now()
if r.Method == "POST" {
log.Println(r.Header)
header := r.Header["X-Function"]
log.Println(header)
exists, err := lookupSwarmService(header[0])
if err != nil {
log.Fatalln(err)
}
if exists == true {
requestBody, _ := ioutil.ReadAll(r.Body)
buf := bytes.NewBuffer(requestBody)
response, err := http.Post("http://"+header[0]+":"+strconv.Itoa(8080)+"/", "text/plain", buf)
if len(header) > 0 {
exists, err := lookupSwarmService(header[0])
if err != nil {
log.Fatalln(err)
}
responseBody, _ := ioutil.ReadAll(response.Body)
w.Write(responseBody)
metrics.GatewayServerlessServedTotal.Inc()
metrics.GatewayFunctions.Observe(time.Since(start).Seconds())
if exists == true {
requestBody, _ := ioutil.ReadAll(r.Body)
invokeService(w, r, metrics, header[0], requestBody)
}
} else {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Provide an x-function header."))
requestBody, _ := ioutil.ReadAll(r.Body)
alexaService := isAlexa(requestBody)
if len(alexaService.Session.SessionId) > 0 &&
len(alexaService.Session.Application.ApplicationId) > 0 &&
len(alexaService.Request.Intent.Name) > 0 {
fmt.Println("Alexa skill detected")
invokeService(w, r, metrics, alexaService.Request.Intent.Name, requestBody)
} else {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Provide an x-function header."))
}
}
}
}
......
#!/bin/bash
docker network create --driver overlay --attachable functions
git clone https://github.com/alexellis/faas && cd faas
cd watchdog
./build.sh
docker build -t catservice .
docker service rm catservice ; docker service create --network=functions --name catservice catservice
cd ..
cd gateway
docker build -t server . ;docker rm -f server; docker run -v /var/run/docker.sock:/var/run/docker.sock --name server -p 8080:8080 --network=functions server
FROM alpine:latest
RUN apk --update add nodejs
COPY ./fwatchdog /usr/bin/
COPY package.json .
COPY handler.js .
COPY sample.json .
RUN npm i
ENV fprocess="node handler.js"
CMD ["fwatchdog"]
"use strict"
let fs = require('fs');
let sample = require("./sample.json");
var content = '';
process.stdin.resume();
process.stdin.on('data', function(buf) { content += buf.toString(); });
process.stdin.on('end', function() {
fs.readFile("/etc/hostname", "utf8", (err, data) => {
if(err) {
return console.log(err);
}
// console.log(content);
sample.response.outputSpeech.text = "Your hostname is: " + data;
sample.response.card.content = "Your hostname is: "+ data
sample.response.card.title = "Your hostname";
console.log(JSON.stringify(sample));
process.exit(0);
});
});
{
"name": "HostnameIntent",
"version": "1.0.0",
"description": "",
"main": "handler.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "PlainText",
"text": "There's currently 6 people in space"
},
"card": {
"content": "There's currently 6 people in space",
"title": "People in space",
"type": "Simple"
},
"shouldEndSession": true
},
"sessionAttributes": {}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment