From ff429ce4931d1600ab5d5d689606a375cf83e4a9 Mon Sep 17 00:00:00 2001 From: Alex Ellis Date: Sun, 22 Jan 2017 10:45:42 +0000 Subject: [PATCH] Add static front end + functions endpoint. --- docker-compose.yml | 3 +- gateway/Dockerfile | 3 + gateway/Dockerfile.build | 2 +- gateway/assets/index.html | 102 +++++++++++++++++++++++++++++ gateway/assets/script/bootstrap.js | 21 ++++++ gateway/assets/style/bootstrap.css | 50 ++++++++++++++ gateway/build.sh | 2 +- gateway/server.go | 68 +++++++++++++++++-- prometheus/alertmanager.yml | 2 +- prometheus/test_alert.json | 47 +++++++++++++ 10 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 gateway/assets/index.html create mode 100644 gateway/assets/script/bootstrap.js create mode 100644 gateway/assets/style/bootstrap.css create mode 100644 prometheus/test_alert.json diff --git a/docker-compose.yml b/docker-compose.yml index 8ad8e1cf..d8255824 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: - "/var/run/docker.sock:/var/run/docker.sock" ports: - 8080:8080 - image: alexellis2/faas-gateway:latest-dev + image: alexellis2/faas-gateway:latest-dev2 networks: - functions deploy: @@ -94,6 +94,7 @@ services: fprocess: "wc" no_proxy: "gateway" https_proxy: $https_proxy + networks: functions: driver: overlay diff --git a/gateway/Dockerfile b/gateway/Dockerfile index 6084f1ba..ad71154d 100644 --- a/gateway/Dockerfile +++ b/gateway/Dockerfile @@ -1,6 +1,9 @@ FROM alpine:latest +WORKDIR /root/ + COPY gateway . +COPY assets assets EXPOSE 8080 ENV http_proxy "" diff --git a/gateway/Dockerfile.build b/gateway/Dockerfile.build index fc9d3d4f..1f9c1102 100644 --- a/gateway/Dockerfile.build +++ b/gateway/Dockerfile.build @@ -6,6 +6,7 @@ RUN go get -d github.com/docker/docker/api/types \ && go get -d github.com/docker/docker/client \ && go get github.com/gorilla/mux \ && go get github.com/prometheus/client_golang/prometheus +# RUN go get -d github.com/prometheus/client_model/go WORKDIR /go/src/github.com/alexellis/faas/gateway @@ -14,5 +15,4 @@ COPY requests requests COPY server.go . COPY proxy.go . -RUN find /go/src/github.com/alexellis/faas/gateway/ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . diff --git a/gateway/assets/index.html b/gateway/assets/index.html new file mode 100644 index 00000000..d3cd755f --- /dev/null +++ b/gateway/assets/index.html @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + +
+ +
+ + + +

FaaS Gateway

+
+ + + New function + + + + + +

{{function.name}}

+ +
+
+
+
+ + +
+

Select a function.

+
+
+
+ + +
+

No functions found in FaaS.

+
+
+
+ + + + + + + + + {{function.name}} + +
+ + + + + + + + +
+ +
+ + + + +
+ + + +
+
+ +
+
+
+ + + + + + + + + + + diff --git a/gateway/assets/script/bootstrap.js b/gateway/assets/script/bootstrap.js new file mode 100644 index 00000000..05b2285b --- /dev/null +++ b/gateway/assets/script/bootstrap.js @@ -0,0 +1,21 @@ +"use strict" +var app = angular.module('faasGateway', ['ngMaterial']); +app.controller("home", ['$scope', '$log', '$http', '$location', '$timeout', function($scope, $log, $http, $location, $timeout) { + $scope.functions = []; + $http.get("/system/functions").then(function(response) { + $scope.functions = response.data; + }); + + $scope.showFunction = function(fn) { + $scope.selectedFunction = fn; + }; + + // TODO: popup + form to create new Docker service. + $scope.newFunction = function() { + $scope.functions.push({ + name: "f" +($scope.functions.length+2), + replicas: 0, + invokedCount: 0 + }); + }; +}]); diff --git a/gateway/assets/style/bootstrap.css b/gateway/assets/style/bootstrap.css new file mode 100644 index 00000000..9180312c --- /dev/null +++ b/gateway/assets/style/bootstrap.css @@ -0,0 +1,50 @@ +@import url('https://fonts.googleapis.com/css?family=Rationale'); + + +/*Taken from PWD, remove styles not used.*/ + +.selected button { + background-color: rgba(158,158,158,0.2); +} + +md-card-content.terminal-container { + background-color: #000; + padding: 0; +} + +.clock { + font-family: 'Rationale', sans-serif; + font-size: 3.0em; + color: #1da4eb; + text-align: center; +} + +.welcome { + background-color: #e7e7e7; +} + +.welcome > div { + text-align: center; +} + +.welcome > div > img { + max-width: 100%; +} + +.g-recaptcha div { + margin-left: auto; + margin-right: auto; + margin-bottom: auto; + margin-top: 50px; +} + +.disconnected { + background-color: #FDF4B6; +} +md-input-container { + margin-bottom: 0; +} +md-input-container .md-errors-spacer { + height: 0; + min-height: 0; +} diff --git a/gateway/build.sh b/gateway/build.sh index 0e5585b6..ee340c32 100755 --- a/gateway/build.sh +++ b/gateway/build.sh @@ -10,4 +10,4 @@ docker rm -f gateway_extract echo Building alexellis2/faas-gateway:latest -docker build -t alexellis2/faas-gateway:latest-dev . +docker build -t alexellis2/faas-gateway:latest-dev2 . diff --git a/gateway/server.go b/gateway/server.go index 071a388b..44e96169 100644 --- a/gateway/server.go +++ b/gateway/server.go @@ -1,22 +1,78 @@ package main import ( + "context" + "encoding/json" "fmt" + "io/ioutil" "log" "net/http" "time" "github.com/alexellis/faas/gateway/metrics" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" "github.com/gorilla/mux" + io_prometheus_client "github.com/prometheus/client_model/go" ) -func makeAlertHandler(c *client.Client) http.HandlerFunc { +func makeAlertHandler() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - fmt.Println(c) + fmt.Println("Alert received.") + body, _ := ioutil.ReadAll(r.Body) + fmt.Println(string(body)) // Todo: parse alert, validate alert and scale up or down function - fmt.Println("Alert received.") + w.WriteHeader(http.StatusOK) + } +} + +// Function exported for system/functions endpoint +type Function struct { + Name string `json:"name"` + Image string `json:"image"` + InvocationCount float64 `json:"invocationCount"` + Replicas uint64 `json:"replicas"` +} + +func makeFunctionReader(metricsOptions metrics.MetricOptions, c *client.Client) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + serviceFilter := filters.NewArgs() + + options := types.ServiceListOptions{ + Filters: serviceFilter, + } + + services, err := c.ServiceList(context.Background(), options) + if err != nil { + fmt.Println(err) + } + + // TODO: Filter only "faas" functions (via metadata?) + + functions := make([]Function, 0) + for _, service := range services { + counter, _ := metricsOptions.GatewayFunctionInvocation.GetMetricWithLabelValues(service.Spec.Name) + + var pbmetric io_prometheus_client.Metric + counter.Write(&pbmetric) + invocations := pbmetric.GetCounter().GetValue() + + f := Function{ + Name: service.Spec.Name, + Image: service.Spec.TaskTemplate.ContainerSpec.Image, + InvocationCount: invocations, + Replicas: *service.Spec.Mode.Replicated.Replicas, + } + functions = append(functions, f) + } + + functionBytes, _ := json.Marshal(functions) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + w.Write(functionBytes) } } @@ -33,12 +89,14 @@ func main() { r := mux.NewRouter() r.HandleFunc("/function/{name:[a-zA-Z_]+}", MakeProxy(metricsOptions, true, dockerClient)) - r.HandleFunc("/system/alert", makeAlertHandler(dockerClient)) - r.HandleFunc("/", MakeProxy(metricsOptions, false, dockerClient)) + r.HandleFunc("/system/alert", makeAlertHandler()) + r.HandleFunc("/system/functions", makeFunctionReader(metricsOptions, dockerClient)).Methods("GET") + r.HandleFunc("/", MakeProxy(metricsOptions, false, dockerClient)).Methods("POST") metricsHandler := metrics.PrometheusHandler() r.Handle("/metrics", metricsHandler) + r.PathPrefix("/").Handler(http.FileServer(http.Dir("./assets/"))).Methods("GET") s := &http.Server{ Addr: ":8080", ReadTimeout: 8 * time.Second, diff --git a/prometheus/alertmanager.yml b/prometheus/alertmanager.yml index 95d2f7a3..8f647258 100644 --- a/prometheus/alertmanager.yml +++ b/prometheus/alertmanager.yml @@ -64,5 +64,5 @@ inhibit_rules: receivers: - name: 'scale-up' webhook_configs: - - url: http://gateway:8080/function/func_webhookstash + - url: http://gateway:8080/system/alert send_resolved: true diff --git a/prometheus/test_alert.json b/prometheus/test_alert.json new file mode 100644 index 00000000..13f5aa52 --- /dev/null +++ b/prometheus/test_alert.json @@ -0,0 +1,47 @@ +{ + "receiver":"scale-up", + "status":"firing", + "alerts":[ + { + "status":"firing", + "labels":{ + "alertname":"APIHighInvocationRate", + "function_name":"func_echoit", + "instance":"gateway:8080", + "job":"gateway", + "monitor":"faas-monitor", + "service":"gateway", + "severity":"major", + "value":"8" + }, + "annotations":{ + "description":"High invocation total on gateway:8080", + "summary":"High invocation total on gateway:8080" + }, + "startsAt":"2017-01-22T10:40:52.804Z", + "endsAt":"0001-01-01T00:00:00Z", + "generatorURL":"http://bb1b23e87070:9090/graph?g0.expr=rate%28gateway_function_invocation_total%5B10s%5D%29+%3E+5\u0026g0.tab=0" + } + ], + "groupLabels":{ + "alertname":"APIHighInvocationRate", + "service":"gateway" + }, + "commonLabels":{ + "alertname":"APIHighInvocationRate", + "function_name":"func_echoit", + "instance":"gateway:8080", + "job":"gateway", + "monitor":"faas-monitor", + "service":"gateway", + "severity":"major", + "value":"8" + }, + "commonAnnotations":{ + "description":"High invocation total on gateway:8080", + "summary":"High invocation total on gateway:8080" + }, + "externalURL":"http://c052c835bcee:9093", + "version":"3", + "groupKey":18195285354214864953 +} \ No newline at end of file