mirror of
https://github.com/openfaas/faas.git
synced 2025-06-08 16:26:47 +00:00
Enable routing to Alexa functions
Add first Alexa-compatible function HostnameIntent
This commit is contained in:
parent
2b9a1c10e4
commit
9656b530c4
1
faas
Submodule
1
faas
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d94cfeb660705028b6c101412a03519a8164712d
|
1
gateway/.gitignore
vendored
1
gateway/.gitignore
vendored
@ -1 +1,2 @@
|
||||
gateway
|
||||
sample.txt
|
||||
|
@ -3,8 +3,8 @@ 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 -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,41 +58,72 @@ 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)
|
||||
|
||||
if len(header) > 0 {
|
||||
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 err != nil {
|
||||
log.Fatalln(err)
|
||||
invokeService(w, r, metrics, header[0], requestBody)
|
||||
}
|
||||
responseBody, _ := ioutil.ReadAll(response.Body)
|
||||
w.Write(responseBody)
|
||||
metrics.GatewayServerlessServedTotal.Inc()
|
||||
} else {
|
||||
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)
|
||||
|
||||
metrics.GatewayFunctions.Observe(time.Since(start).Seconds())
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Provide an x-function header."))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
12
oneshot.sh
Executable file
12
oneshot.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/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
|
||||
|
11
sample-functions/HostnameIntent/Dockerfile
Normal file
11
sample-functions/HostnameIntent/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
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"]
|
22
sample-functions/HostnameIntent/handler.js
Normal file
22
sample-functions/HostnameIntent/handler.js
Normal file
@ -0,0 +1,22 @@
|
||||
"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);
|
||||
});
|
||||
});
|
||||
|
12
sample-functions/HostnameIntent/package.json
Normal file
12
sample-functions/HostnameIntent/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "HostnameIntent",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "handler.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
16
sample-functions/HostnameIntent/sample.json
Normal file
16
sample-functions/HostnameIntent/sample.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user