Merge pull request #16 from alexellis/re_document

Adjust documentation for clarity
Allow content-type for UI
Filter non-functions by container label "function"
This commit is contained in:
Alex Ellis 2017-01-31 17:33:00 +00:00 committed by GitHub
commit a26043cb90
9 changed files with 456 additions and 378 deletions

96
DEV.md Normal file
View File

@ -0,0 +1,96 @@
## Develop your own function
### TestDrive
Before you start development, you may want to take FaaS for a test drive which sets up a stack of sample functions from docker-compose.yml. You can then build your own functions and add them to the stack.
> You can test-drive FaaS with a set of sample functions as defined in docker-compose.yml on play-with-docker.com for free, or on your own laptop.
* [Begin the TestDrive instructions](https://github.com/alexellis/faas/blob/master/TestDrive.md)
### Working on the API Gateway or Watchdog
To work on either of the FaaS Golang components checkout the "./build.sh" scripts and acompanying Dockerfiles.
* ![Roadmap and Contributing](https://github.com/alexellis/faas/blob/master/ROADMAP.md)
### Creating a function
Functions run as Docker containers with the Watchdog component embedded to handle communication with the API Gateway.
**Markdown Parser**
This is the basis of a function which generates HTML from MarkDown:
```
FROM golang:1.7.3
RUN mkdir -p /go/src/app
COPY handler.go /go/src/app
WORKDIR /go/src/app
RUN go get github.com/microcosm-cc/bluemonday && \
go get github.com/russross/blackfriday
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
ADD https://github.com/alexellis/faas/releases/download/v0.3-alpha/fwatchdog /usr/bin
RUN chmod +x /usr/bin/fwatchdog
ENV fprocess="/go/src/app/app"
CMD ["/usr/bin/watchdog"]
```
The base Docker container is not important, you just need to add the watchdog component and then set the fprocess to execute your binary at runtime.
Update the Docker stack with this:
```
markdown:
image: alexellis2/faas-markdownrender:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
```
**Word counter with busybox**
```
FROM alpine:latest
ADD https://github.com/alexellis/faas/releases/download/v0.3-alpha/fwatchdog /usr/bin
RUN chmod +x /usr/bin/fwatchdog
ENV fprocess="wc"
CMD ["fwatchdog"]
```
Update your Docker stack with this definition:
```
wordcount:
image: alexellis2/faas-alpinefunction:latest
labels:
function: "true"
depends_on:
- gateway
networks:
- functions
environment:
fprocess: "wc"
```
### Testing your function
You can test your function through a webbrowser against the UI portal on port 8080.
http://localhost:8080/
You can also invoke a function by name with curl:
```
curl --data-binary @README.md http://localhost:8080/function/func_wordcount
```

231
README.md
View File

@ -1,4 +1,4 @@
# faas - Functions As A Service
## faas - Functions As A Service
FaaS is a platform for building serverless functions on Docker Swarm Mode with first class metrics. Any UNIX process can be packaged as a function in FaaS enabling you to consume a range of web events without repetitive boiler-plate coding.
@ -11,227 +11,38 @@ FaaS is a platform for building serverless functions on Docker Swarm Mode with f
Status](https://travis-ci.org/alexellis/faas.svg?branch=master)](https://travis-ci.org/alexellis/faas)
## Minimum requirements:
* Docker 1.13-RC (to support attachable overlay networks)
* Docker 1.13 (to support attachable overlay networks)
* At least a single host in Swarm Mode. (run `docker swarm init`)
> For more information on Swarm mode and configuration please read the [Swarm Mode tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).
## TestDrive
Check your `docker version` and upgrade to one of the latest 1.13-RCs from the [Docker Releases page](https://github.com/docker/docker/releases). This is already available through the Beta channel in Docker for Mac.
You can test-drive FaaS with a set of sample functions as defined in docker-compose.yml on play-with-docker.com for free, or on your own laptop.
## Quickstart with `docker stack deploy`
* [Begin the TestDrive instructions](https://github.com/alexellis/faas/blob/master/TestDrive.md)
For a complete stack of Prometheus, the gateway and the DockerHubStats function:
## Roadmap and contributing
* [Read the Roadmap]((https://github.com/alexellis/faas/blob/master/ROADMAP.md))
* Simply run `./deploy_stack.sh` - following that you can find out information about the services like this:
## Develop your own functions
* [Read the Roadmap]((https://github.com/alexellis/faas/blob/master/DEV.md))
```
# docker stack ls
NAME SERVICES
func 3
#### Would you prefer a video overview?
# docker stack ps func
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
rhzej73haufd func_gateway.1 alexellis2/faas-gateway:latest moby Running Running 26 minutes ago
fssz6unq3e74 func_hubstats.1 alexellis2/faas-dockerhubstats:latest moby Running Running 27 minutes ago
nnlzo6u3pilg func_prometheus.1 quay.io/prometheus/prometheus:latest moby Running Running 27 minutes ago
```
See how to deploy FaaS onto play-with-docker.com and Docker Swarm in 1-2 minutes. See the sample functions in action and watch the graphs in Prometheus as we ramp up the amount of requests.
* Then head over to http://localhost:9090 for your Prometheus metrics
* [Deep Dive into Functions as a Service (FaaS) on Docker](https://www.youtube.com/watch?v=sp1B7l5mEzc)
* Your function can be accessed via the gateway like this:
#### Prometheus metrics are built-in
**Sample function: Docker Hub Stats (hubstats)**
Prometheus is built into FaaS and the sample stack, so you can check throughput for each function individually with a rate function in Prometheus like this:
```
# curl -X POST http://localhost:8080/function/func_hubstats -d "alexellis2"
The organisation or user alexellis2 has 99 repositories on the Docker hub.
![](https://pbs.twimg.com/media/C2d9IkbXAAI58fz.jpg)
# curl -X POST http://localhost:8080/function/func_hubstats -d "library"
The organisation or user library has 128 repositories on the Docker hub.
```
### More resources:
The `-d` value passes in the argument for your function. This is read via STDIN and used to query the Docker Hub to see how many images you've created/pushed.
FaaS is still expanding and growing, check out the developments around:
**Sample function: webhook stasher (webhookstash)**
Another cool sample function is the Webhook Stasher which saves the body of any data posted to the service to the container's filesystem. Each file is written with the filename of the UNIX time.
```
# curl -X POST http://localhost:8080/function/func_webhookstash -d '{"event": "fork", "repo": "alexellis2/faas"}'
Webhook stashed
# docker ps|grep stash
d769ca70729d alexellis2/faas-webhookstash@sha256:b378f1a144202baa8fb008f2c8896f5a8
# docker exec d769ca70729d find .
.
./1483999546817280727.txt
./1483999702130689245.txt
./1483999702533419188.txt
./1483999702978454621.txt
./1483999703284879767.txt
./1483999719981227578.txt
./1483999720296180414.txt
./1483999720666705381.txt
./1483999720961054638.txt
```
**Sample function: Node OS Info (nodeinfo)**
Grab OS, CPU and other info via a Node.js container using the `os` module.
```
# curl -X POST http://localhost:8080/function/func_nodeinfo -d ''
linux x64 [ { model: 'Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz',
speed: 2500,
times:
{ user: 3754430800,
nice: 2450200,
sys: 885352200,
idle: 25599742200,
irq: 0 } },
...
```
> Why not start the code on play-with-docker.com and then configure a Github repository to send webhook to the function?
If you're looking for a UI checkout the [Postman plugin for Chrome](https://www.getpostman.com) where you can send POSTs without needing `curl`.
## Manual quickstart
#### Create an attachable network for the gateway and functions to join
```
# docker network create --driver overlay --attachable functions
```
#### Start the gateway
```
# docker pull alexellisio/faas-gateway:latest
# docker rm -f gateway;
# docker run -d -v /var/run/docker.sock:/var/run/docker.sock --name gateway -p 8080:8080 \
--network=functions alexellisio/faas-gateway:latest
```
#### Start at least one of the serverless functions:
Here we start an echo service using the `cat` command found in a shell.
```
# docker service rm catservice
# docker service create --network=functions --name catservice alexellisio/faas-catservice:latest
```
#### Now send an event to the API gateway
* Method 1 - use the service name as a URL:
```
# curl -X POST --data-binary @$HOME/.ssh/known_hosts -v http://localhost:8080/function/catservice
```
* Method 2 - use the X-Function header:
```
# curl -X POST -H 'x-function: catservice' --data-binary @$HOME/.ssh/known_hosts -v http://localhost:8080/
```
#### Build your own function
Visit the accompanying blog post to find out how to build your own function in whatever programming language you prefer.
[FaaS blog post](http://blog.alexellis.io/functions-as-a-service/)
#### Setup a Prometheus instance
Please review the following quickstart example and edit the configuration in `monitor/prometheus.yml`.
* [Quickstart-Prometheus](https://github.com/alexellis/quickstart-prometheus)
# Overview
## the gateway
This container acts in a similar way to the API Gateway on AWS. Requests can be made to this endpoint with a JSON body.
**Incoming requests and routing**
There are three options for routing:
* 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
* Routing is enabled through a `X-Function` header which matches a service name (function) directly.
Features:
* [todo] auto-scaling of replicas as load increases
* [todo] backing off of replicas as load reduces
* [todo] unique URL routes for serverless functions
* instrumentation via Prometheus metrics at GET /metrics
## the watchdog
This binary fwatchdog acts as a watchdog for your function. Features:
* Static binary in Go
* Listens to HTTP requests over swarm overlay network
* Spawns process set in `fprocess` ENV variable for each HTTP connection
* [todo] Only lets processes run for set duration i.e. 500ms, 2s, 3s.
* Language/binding independent
### Additional technical debt:
* Must switch over to timeouts for HTTP.post via HttpClient.
* Coverage with unit tests
* Update quick-start to use `docker stack deploy`
* Have `docker stack deploy` include a pre-configured Prometheus instance.
## Development
For development of the FaaS framework / library read on. If you would like to consume the project with your own functions then you can use the public images and the supplied `docker stack` file as a template (docker-compose.yml)
### License
This project is licensed until the MIT License.
## Contributing
Here are a few guidelines for contributing:
* If you have found a bug please raise an issue.
* If the documentation can be improved / translated etc please raise an issue to discuss.
* If you would like to contribute to the codebase please raise an issue to propose the change.
> Please provide a summary of what you changed, how you did it and how it can be tested.
### Building a development environment:
To use multiple hosts you should push your services (functions) to the Docker Hub or a registry accessible to all nodes.
```
# docker network create --driver overlay --attachable functions
# git clone https://github.com/alexellis/faas && cd faas
# cd watchdog
# ./build.sh
# cd ../sample-functions/catservice/
# cp ../../watchdog/fwatchdog ./
# 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
```
Accessing the `cat` (read echo) service:
```
# curl -X POST -H 'x-function: catservice' --data-binary @$HOME/.ssh/known_hosts -v http://localhost:8080/
# curl -X POST -H 'x-function: catservice' --data-binary @/etc/hostname -v http://localhost:8080/
```
## Prometheus metrics / instrumentation
Standard go metrics and function invocation count / duration are available at http://localhost:8080/metrics/
* [Auto-scaling](https://twitter.com/alexellisuk/status/823262200236277762)
* Prometheus alerts
* More sample functions
* [Brand new UI](https://twitter.com/alexellisuk/status/823262200236277762)

59
ROADMAP.md Normal file
View File

@ -0,0 +1,59 @@
# Roadmap
## 1. Current items
### The API Gateway
This container acts in a similar way to the API Gateway on AWS. Requests can be made to this endpoint with a JSON body.
Features:
* UI for viewing and testing functions deployed through stack
* Auto-scaling of replicas as load increases
* Backing off of replicas as load reduces
* Unique URL routes for serverless functions
* Instrumentation via Prometheus metrics at GET /metrics
* Bundled Prometheus stack with AlertManager
**Incoming requests and routing**
There are three options for routing:
* Functions created on the overlay network can be invoked by: http://localhost:8080/function/{servicename}
* Automatic routing is also enabled through the `/` endpoint via a `X-Function` header which matches a service name (function) directly.
### The watchdog
This binary fwatchdog acts as a watchdog for your function. Features:
* Static binary in Go
* Listens to HTTP requests over swarm overlay network
* Spawns process set in `fprocess` ENV variable for each HTTP connection
* Only lets processes run for set duration i.e. 500ms, 2s, 3s.
* Language/binding independent - can invoke any UNIX process, including built-ins such as `wc` or `cat`
## 2. Future items
* UI enhancements to create new function through a form
* Asynchronous / long-running tasks
* Built-in TLS termination
* Deeper tests coverage and integration tests
* Documentation about Alexa sample function
## 3. Development and Contributing
For development of the FaaS framework / library read on. If you would like to consume the project with your own functions then you can use the public images and the supplied `docker stack` file as a template (docker-compose.yml)
### License
This project is licensed until the MIT License.
## Contributing
Here are a few guidelines for contributing:
* If you have found a bug please raise an issue.
* If the documentation can be improved / translated etc please raise an issue to discuss.
* If you would like to contribute to the codebase please raise an issue to propose the change.
> Please provide a summary of what you changed, how you did it and how it can be tested.

View File

@ -1,4 +1,4 @@
## faas - Functions As A Service
## Functions As A Service - TestDrive
FaaS is a platform for building serverless functions on Docker Swarm Mode with first class metrics. Any UNIX process can be packaged as a function in FaaS enabling you to consume a range of web events without repetitive boiler-plate coding.
@ -28,9 +28,7 @@ This one-shot script clones the code, initialises Docker swarm mode and then dep
![](https://pbs.twimg.com/media/C1wDi_tXUAIphu-.jpg)
* Head over to the README to see how to invoke the sample function for Docker Hub Stats via the `curl` commands.
#### The sample functions are:
#### Some of the sample functions are:
* Webhook stasher function (webhookstash) - saves webhook body into container's filesystem (Golang)
* Docker Hub Stats function (hubstats) - queries the count of images for a user on the Docker Hub (Golang)
@ -38,33 +36,82 @@ This one-shot script clones the code, initialises Docker swarm mode and then dep
#### Invoke the sample functions with curl or Postman:
Head over to the Github repo now for the quick-start to test out the sample functions:
Head over to the [Github repo to fork the code](https://github.com/alexellis/faas), or read on to see the input/output from the sample functions.
[Quickstart documentation with `docker-stack deploy`](https://github.com/alexellis/faas/tree/stack_1#quickstart-with-docker-stack-deploy)
#### Working with the sample functions
Once you're up and running checkout the `gateway_functions_count` metrics on your Prometheus endpoint on *port 9090*.
> If you're looking for a UI head over to http://localhost:8080/ for a list of functions deployed on your swarm.
### More resources:
You can find out which services are deployed like this:
FaaS is still expanding and growing, check out the development branch for:
```
# docker stack ls
NAME SERVICES
func 3
* [Auto-scaling](https://twitter.com/alexellisuk/status/823262200236277762)
* Prometheus alerts
* More sample functions
* [Brand new UI](https://twitter.com/alexellisuk/status/823262200236277762)
# docker stack ps func
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
rhzej73haufd func_gateway.1 alexellis2/faas-gateway:latest moby Running Running 26 minutes ago
fssz6unq3e74 func_hubstats.1 alexellis2/faas-dockerhubstats:latest moby Running Running 27 minutes ago
nnlzo6u3pilg func_prometheus.1 quay.io/prometheus/prometheus:latest moby Running Running 27 minutes ago
```
[Development branch](https://github.com/alexellis/faas/commits/labels_metrics)
* Head over to http://localhost:9090 for your Prometheus metrics
#### Would you prefer a video overview?
* Your function can be accessed via the gateway like this:
See how to deploy FaaS onto play-with-docker.com and Docker Swarm in 1-2 minutes. See the sample functions in action and watch the graphs in Prometheus as we ramp up the amount of requests.
**Sample function: Docker Hub Stats (hubstats)**
* [Deep Dive into Functions as a Service (FaaS) on Docker](https://www.youtube.com/watch?v=sp1B7l5mEzc)
```
# curl -X POST http://localhost:8080/function/func_hubstats -d "alexellis2"
The organisation or user alexellis2 has 99 repositories on the Docker hub.
#### Prometheus metrics are built-in
# curl -X POST http://localhost:8080/function/func_hubstats -d "library"
The organisation or user library has 128 repositories on the Docker hub.
```
Prometheus is built into FaaS and the sample stack, so you can check throughput for each function individually with a rate function in Prometheus like this:
The `-d` value passes in the argument for your function. This is read via STDIN and used to query the Docker Hub to see how many images you've created/pushed.
![](https://pbs.twimg.com/media/C2d9IkbXAAI58fz.jpg)
**Sample function: webhook stasher (webhookstash)**
Another cool sample function is the Webhook Stasher which saves the body of any data posted to the service to the container's filesystem. Each file is written with the filename of the UNIX time.
```
# curl -X POST http://localhost:8080/function/func_webhookstash -d '{"event": "fork", "repo": "alexellis2/faas"}'
Webhook stashed
# docker ps|grep stash
d769ca70729d alexellis2/faas-webhookstash@sha256:b378f1a144202baa8fb008f2c8896f5a8
# docker exec d769ca70729d find .
.
./1483999546817280727.txt
./1483999702130689245.txt
./1483999702533419188.txt
./1483999702978454621.txt
./1483999703284879767.txt
./1483999719981227578.txt
./1483999720296180414.txt
./1483999720666705381.txt
./1483999720961054638.txt
```
**Sample function: Node OS Info (nodeinfo)**
Grab OS, CPU and other info via a Node.js container using the `os` module.
```
# curl -X POST http://localhost:8080/function/func_nodeinfo -d ''
linux x64 [ { model: 'Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz',
speed: 2500,
times:
{ user: 3754430800,
nice: 2450200,
sys: 885352200,
idle: 25599742200,
irq: 0 } },
...
```
> Why not start the code on play-with-docker.com and then configure a Github repository to send webhook to the function?

View File

@ -53,8 +53,11 @@ services:
# Sample functions go here.
# Service label of "function" allows functions to show up in UI on http://gateway:8080/
webhookstash:
image: alexellis2/faas-webhookstash:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -65,6 +68,8 @@ services:
hubstats:
image: alexellis2/faas-dockerhubstats:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -72,8 +77,11 @@ services:
environment:
no_proxy: "gateway"
https_proxy: $https_proxy
nodeinfo:
image: alexellis2/faas-nodeinfo:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -84,6 +92,8 @@ services:
echoit:
image: alexellis2/faas-alpinefunction:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -95,6 +105,8 @@ services:
wordcount:
image: alexellis2/faas-alpinefunction:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -106,6 +118,8 @@ services:
markdown:
image: alexellis2/faas-markdownrender:latest
labels:
function: "true"
depends_on:
- gateway
networks:
@ -114,15 +128,17 @@ services:
no_proxy: "gateway"
https_proxy: $https_proxy
# alexacolorchange:
# image: alexellis2/faas-alexachangecolorintent
# depends_on:
# - gateway
# networks:
# - functions
# environment:
# no_proxy: "gateway"
# https_proxy: $https_proxy
alexacolorchange:
labels:
function: "true"
image: alexellis2/faas-alexachangecolorintent
depends_on:
- gateway
networks:
- functions
environment:
no_proxy: "gateway"
https_proxy: $https_proxy

View File

@ -1,4 +1,5 @@
<html ng-app="faasGateway" >
<html ng-app="faasGateway">
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic" />
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.css">
@ -7,117 +8,123 @@
<link rel="stylesheet" href="style/bootstrap.css">
</head>
</head>
<body ng-controller="home">
<div layout="column" style="height:100%;" ng-cloak>
<body ng-controller="home">
<div layout="column" style="height:100%;" ng-cloak>
<section id="popupContainer" layout="row" flex>
<md-sidenav
class="md-sidenav-left"
md-component-id="left"
md-is-locked-open="$mdMedia('gt-sm')"
md-whiteframe="4" layout="column">
<section id="popupContainer" layout="row" flex>
<md-sidenav class="md-sidenav-left" md-component-id="left" md-is-locked-open="$mdMedia('gt-sm')" md-whiteframe="4" layout="column">
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">FaaS Gateway</h1>
</md-toolbar>
<md-content layout-padding>
<md-button ng-click="newFunction()" ng-disabled="isFunctionBeingCreated" class="md-primary">New function</md-button>
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">FaaS Gateway</h1>
</md-toolbar>
<md-list>
<md-list-item ng-switch class="md-3-line" ng-click="showFunction(function)" ng-repeat="function in functions | orderBy: '-invocationCount'" ng-class="function.name == selectedFunction.name ? 'selected' : false">
<md-icon ng-switch-when="true" style="color: blue" md-svg-icon="person"></md-icon>
<md-icon ng-switch-when="false" md-svg-icon="person-outline"></md-icon>
<p>{{function.name}}</p>
<md-divider ng-if="!$last"></md-divider>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>
<md-content layout-padding>
<md-button ng-click="newFunction()" ng-disabled="isFunctionBeingCreated" class="md-primary">New function</md-button>
<md-content flex layout-padding ng-if="!selectedFunction" ng-show="functions.length">
<div layout="column" layout-align="top center">
<p>Select a function.</p>
</div>
<div flex></div>
</md-content>
<md-list>
<md-list-item ng-switch class="md-3-line" ng-click="showFunction(function)" ng-repeat="function in functions | orderBy: '-invocationCount'" ng-class="function.name == selectedFunction.name ? 'selected' : false">
<md-icon ng-switch-when="true" style="color: blue" md-svg-icon="person"></md-icon>
<md-icon ng-switch-when="false" md-svg-icon="person-outline"></md-icon>
<p>{{function.name}}</p>
<md-divider ng-if="!$last"></md-divider>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>
<md-content flex layout-padding ng-if="!functions.length">
<div layout="column" layout-align="top center">
<p>No functions found in FaaS.</p>
</div>
<div flex></div>
</md-content>
<md-content flex layout-padding ng-if="!selectedFunction" ng-show="functions.length">
<div layout="column" layout-align="top center">
<p>Select a function.</p>
</div>
<div flex></div>
</md-content>
<md-content flex layout="column" ng-repeat="function in functions" ng-show="function.name == selectedFunction.name">
<md-content flex layout-padding ng-if="!functions.length">
<div layout="column" layout-align="top center">
<p>No functions found in FaaS.</p>
</div>
<div flex></div>
</md-content>
<md-card md-theme="default" md-theme-watch>
<md-card-title>
<md-card-title-text>
<md-content flex layout="column" ng-repeat="function in functions" ng-show="function.name == selectedFunction.name">
<span class="md-headline">
<md-card md-theme="default" md-theme-watch>
<md-card-title>
<md-card-title-text>
<span class="md-headline">
{{function.name}}
</span>
<div layout-gt-sm="row">
<md-input-container class="md-icon-float md-block">
<label>Replicas</label>
<input ng-model="function.replicas" type="text" readonly="readonly">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>Invocation count</label>
<input ng-model="function.invocationCount" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-icon-float md-block">
<label>Replicas</label>
<input ng-model="function.replicas" type="text" readonly="readonly">
</md-input-container>
<md-input-container class="md-block" flex-gt-sm>
<label>Invocation count</label>
<input ng-model="function.invocationCount" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Image</label>
<input ng-model="function.image" type="text" readonly="readonly">
</md-input-container>
</div>
<md-card-title-text>
</md-card-title>
</md-card>
<md-card>
<md-card-title>
<md-card-title-text>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Image</label>
<input ng-model="function.image" type="text" readonly="readonly">
</md-input-container>
</div>
<md-card-title-text>
</md-card-title>
</md-card>
<md-card>
<md-card-title>
<md-card-title-text>
<span class="md-headline">
<span class="md-headline">
Invoke function
</span>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<button ng-click="fireRequest()" class="md-raised md-button md-ink-ripple" type="button">
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<button ng-click="fireRequest()" class="md-raised md-button md-ink-ripple" type="button">
<span class="ng-scope">Invoke</span><div class="md-ripple-container"></div>
</button>
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Request body</label>
<textarea ng-model="invocation.request" cols="80" rows="4"></textarea>
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Response status</label>
<input ng-model="invocationStatus" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Response body</label>
<textarea ng-model="invocationResponse" cols="80" rows=10></textarea>
</md-input-container>
</div>
<md-card-title-text>
</md-card-title>
</md-card>
</md-content>
</section>
</div>
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<md-radio-group ng-model="invocation.contentType">
<md-radio-button value="text" class="md-primary"> Text </md-radio-button>
<md-radio-button value="json"> JSON </md-radio-button>
</md-radio-group>
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Request body</label>
<textarea ng-model="invocation.request" cols="80" rows="4"></textarea>
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Response status</label>
<input ng-model="invocationStatus" type="text" readonly="readonly">
</md-input-container>
</div>
<div layout-gt-sm="row">
<md-input-container class="md-block" flex-gt-sm>
<label>Response body</label>
<textarea ng-model="invocationResponse" cols="80" rows=10></textarea>
</md-input-container>
</div>
<md-card-title-text>
</md-card-title>
</md-card>
</md-content>
</section>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
@ -126,6 +133,6 @@
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.0/angular-material.min.js"></script>
<script src="script/bootstrap.js"></script>
</body>
</body>
</html>
</html>

View File

@ -1,13 +1,13 @@
"use strict"
var app = angular.module('faasGateway', ['ngMaterial']);
app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', function($scope, $log, $http, $location, $timeout) {
app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', '$mdDialog', function($scope, $log, $http, $location, $timeout, $mdDialog) {
$scope.functions = [];
$scope.invocationRequest = "";
$scope.invocationResponse = "";
$scope.invocationStatus = "";
$scope.invocation = {
contentType: "text"
};
$scope.invocation.request = ""
setInterval(function() {
@ -15,33 +15,42 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', func
}, 1000);
$scope.fireRequest = function() {
$http({url:"/function/"+$scope.selectedFunction.name, data: $scope.invocation.request, method: "POST", headers: {"Content-Type": "text/plain"}, responseType: "text"}).
then(function(response) {
$scope.invocationResponse = response.data;
$scope.invocationStatus = response.status;
}).catch(function(error1) {
$scope.invocationResponse = error1;
$scope.invocationStatus = null;
});
var options = {
url: "/function/" + $scope.selectedFunction.name,
data: $scope.invocation.request,
method: "POST",
headers: { "Content-Type": $scope.invocation.contentType == "json" ? "application/json" : "text/plain" },
responseType: $scope.invocation.contentType
};
$http(options)
.then(function(response) {
$scope.invocationResponse = response.data;
$scope.invocationStatus = response.status;
}).catch(function(error1) {
$scope.invocationResponse = error1;
$scope.invocationStatus = null;
});
// console.log("POST /function/"+ $scope.selectedFunction.name);
// console.log("Body: " + $scope.invocation.request);
};
var refreshData = function () {
var refreshData = function() {
var previous = $scope.functions;
var cl = function(previousItems) {
$http.get("/system/functions").then(function(response) {
if(response && response.data) {
if(previousItems.length !=response.data.length) {
if (response && response.data) {
if (previousItems.length != response.data.length) {
$scope.functions = response.data;
} else {
for(var i =0;i<$scope.functions.length;i++) {
for(var j =0;j<response.data.length;j++) {
if($scope.functions[i].name == response.data[j].name) {
$scope.functions[i].replicas=response.data[j].replicas;
$scope.functions[i].invocationCount=response.data[j].invocationCount;
for (var i = 0; i < $scope.functions.length; i++) {
for (var j = 0; j < response.data.length; j++) {
if ($scope.functions[i].name == response.data[j].name) {
$scope.functions[i].replicas = response.data[j].replicas;
$scope.functions[i].invocationCount = response.data[j].invocationCount;
}
}
}
@ -59,22 +68,38 @@ app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', func
};
$scope.showFunction = function(fn) {
if($scope.selectedFunction!=fn) {
if ($scope.selectedFunction != fn) {
$scope.selectedFunction = fn;
$scope.invocation.request = "";
$scope.invocationResponse = "";
$scope.invocationStatus = "";
$scope.invocation.contentType = "";
}
};
// TODO: popup + form to create new Docker service.
$scope.newFunction = function() {
$scope.functions.push({
name: "f" +($scope.functions.length+2),
replicas: 0,
invokedCount: 0
var showDialog = function() {
var alert = $mdDialog.alert({
title: 'New function',
textContent: 'New functions are not supported yet, but will be defined here.',
ok: 'Close'
});
$mdDialog
.show(alert)
.finally(function() {
alert = undefined;
});
};
// TODO: popup + form to create new Docker service.
// More to follow @ https://material.angularjs.org/latest/demo/dialog
$scope.newFunction = function() {
// $scope.functions.push({
// name: "f" + ($scope.functions.length + 2),
// replicas: 0,
// invokedCount: 0
// });
showDialog()
};
fetch();
}]);
}]);

View File

@ -30,22 +30,27 @@ func MakeFunctionReader(metricsOptions metrics.MetricOptions, c *client.Client)
}
// TODO: Filter only "faas" functions (via metadata?)
functions := make([]requests.Function, 0)
var functions []requests.Function
for _, service := range services {
counter, _ := metricsOptions.GatewayFunctionInvocation.GetMetricWithLabelValues(service.Spec.Name)
// Get the metric's value from ProtoBuf interface (idea via Julius Volz)
var protoMetric io_prometheus_client.Metric
counter.Write(&protoMetric)
invocations := protoMetric.GetCounter().GetValue()
if len(service.Spec.TaskTemplate.ContainerSpec.Labels["function"]) > 0 {
f := requests.Function{
Name: service.Spec.Name,
Image: service.Spec.TaskTemplate.ContainerSpec.Image,
InvocationCount: invocations,
Replicas: *service.Spec.Mode.Replicated.Replicas,
counter, _ := metricsOptions.GatewayFunctionInvocation.GetMetricWithLabelValues(service.Spec.Name)
// Get the metric's value from ProtoBuf interface (idea via Julius Volz)
var protoMetric io_prometheus_client.Metric
counter.Write(&protoMetric)
invocations := protoMetric.GetCounter().GetValue()
f := requests.Function{
Name: service.Spec.Name,
Image: service.Spec.TaskTemplate.ContainerSpec.Image,
InvocationCount: invocations,
Replicas: *service.Spec.Mode.Replicated.Replicas,
}
functions = append(functions, f)
}
functions = append(functions, f)
}
functionBytes, _ := json.Marshal(functions)

View File

@ -1,7 +1,19 @@
'use strict'
let os = require('os');
let fs = require('fs');
const getStdin = require('get-stdin');
getStdin().then(content => {
console.log(os.platform(), os.arch(), os.cpus(), os.uptime(), os.userInfo());
});
getStdin().then((content) => {
fs.readFile("/etc/hostname", "utf8", (err, data) => {
console.log("Hostname: " + data);
console.log("Platform: " + os.platform());
console.log("Arch: " + os.arch());
console.log("CPU count: " + os.cpus().length);
console.log("Uptime: " + os.uptime())
console.log("User info: " + os.userInfo());
if (content && content.length && content.indexOf("verbose") > -1) {
console.log(os.cpus());
}
});
});