-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit for function proxy (#1)
Defines a go server that acts as a proxy for function invocation to language specific servers. Handles timeouts, capturing logs, parallel function calls to separate language servers.
- Loading branch information
Showing
68 changed files
with
12,256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
* text=auto | ||
|
||
vendor/** binary | ||
**/gen/** -diff | ||
|
||
# for Github | ||
**/gen/** linguist-generated | ||
vendor/** linguist-vendored |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Gopkg.toml example | ||
# | ||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | ||
# for detailed Gopkg.toml documentation. | ||
# | ||
# required = ["github.com/user/thing/cmd/thing"] | ||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | ||
# | ||
# [[constraint]] | ||
# name = "github.com/user/project" | ||
# version = "1.0.0" | ||
# | ||
# [[constraint]] | ||
# name = "github.com/user/project2" | ||
# branch = "dev" | ||
# source = "github.com/myfork/project2" | ||
# | ||
# [[override]] | ||
# name = "github.com/x/y" | ||
# version = "2.4.0" | ||
# | ||
# [prune] | ||
# non-go = false | ||
# go-tests = true | ||
# unused-packages = true | ||
|
||
|
||
[[constraint]] | ||
branch = "master" | ||
name = "golang.org/x/sync" | ||
|
||
[prune] | ||
go-tests = true | ||
unused-packages = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Funky - Function Proxy Server | ||
|
||
A simple proxy server written in Go used to forward function invocations to language specific servers. Funky handles capturing stdout and stderr logs, function invocation timeouts and a limited amount of parallel function invocations. | ||
|
||
Funky requires two environment variables: | ||
* SERVERS - a number of language specific servers to initalize to handle function invocations | ||
* SERVER_CMD - the command to run to start a function server e.g. `python3 main.py hello.handle` | ||
|
||
Any request to the function server will try to invoke the function on any free server. The request will block if no server if idle and able to process the request. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/////////////////////////////////////////////////////////////////////// | ||
// Copyright (c) 2018 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
/////////////////////////////////////////////////////////////////////// | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"strconv" | ||
|
||
"github.com/dispatchframework/funky/pkg/funky" | ||
) | ||
|
||
const ( | ||
serversEnvVar = "SERVERS" | ||
serverCmdEnvVar = "SERVER_CMD" | ||
) | ||
|
||
type funkyHandler struct { | ||
router funky.Router | ||
} | ||
|
||
func (f funkyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
var body funky.Message | ||
err := json.NewDecoder(r.Body).Decode(&body) | ||
if err != nil { | ||
resp := funky.Message{ | ||
Context: &funky.Context{ | ||
Error: &funky.Error{ | ||
ErrorType: funky.InputError, | ||
Message: fmt.Sprintf("Invalid Input: %s", err), | ||
}, | ||
}, | ||
} | ||
out, _ := json.Marshal(resp) | ||
fmt.Fprintf(w, string(out)) | ||
return | ||
} | ||
|
||
resp, _ := f.router.Delegate(&body) | ||
|
||
json.NewEncoder(w).Encode(resp) | ||
} | ||
|
||
func healthy(c <-chan struct{}) bool { | ||
select { | ||
case <-c: | ||
return false | ||
default: | ||
return true | ||
} | ||
} | ||
|
||
func main() { | ||
numServers, err := strconv.Atoi(os.Getenv(serversEnvVar)) | ||
if err != nil { | ||
log.Fatalf("Unable to parse %s environment variable", serversEnvVar) | ||
} | ||
if numServers < 1 { | ||
numServers = 1 | ||
} | ||
|
||
serverCmd := os.Getenv(serverCmdEnvVar) | ||
serverFactory, err := funky.NewDefaultServerFactory(serverCmd) | ||
if err != nil { | ||
log.Fatal("Too few arguments to server command.") | ||
} | ||
|
||
router, err := funky.NewRouter(numServers, serverFactory) | ||
if err != nil { | ||
log.Fatalf("Failed creating new router: %+v", err) | ||
} | ||
|
||
handler := funkyHandler{ | ||
router: router, | ||
} | ||
|
||
servMux := http.NewServeMux() | ||
servMux.Handle("/", handler) | ||
servMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { | ||
if !healthy(funky.Healthy) { | ||
w.WriteHeader(500) | ||
} | ||
|
||
w.Write([]byte("{}")) | ||
}) | ||
|
||
server := &http.Server{ | ||
Addr: ":8080", | ||
Handler: servMux, | ||
} | ||
|
||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, os.Interrupt) | ||
go func() { | ||
<-c | ||
server.Shutdown(context.TODO()) | ||
router.Shutdown() | ||
os.Exit(0) | ||
}() | ||
|
||
server.ListenAndServe() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/////////////////////////////////////////////////////////////////////// | ||
// Copyright (c) 2018 VMware, Inc. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
/////////////////////////////////////////////////////////////////////// | ||
package funky | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// IllegalArgumentError An error indicating that an argument to a method is illegal or invalid. | ||
type IllegalArgumentError string | ||
|
||
func (e IllegalArgumentError) Error() string { | ||
return fmt.Sprintf("The argument is illegal or inappropriate: %s", string(e)) | ||
} | ||
|
||
// TimeoutError An error indicating that a timeout has been exceeded | ||
type TimeoutError string | ||
|
||
func (e TimeoutError) Error() string { | ||
return fmt.Sprintf("The invocation exceeded the timeout: %s", string(e)) | ||
} | ||
|
||
// FunctionServerError a generic error indicating that the function server experienced an error | ||
type FunctionServerError struct { | ||
APIError Error | ||
} | ||
|
||
func (e FunctionServerError) Error() string { | ||
return "The function server encountered an error. See APIError field for more detail" | ||
} | ||
|
||
// ConnectionRefusedError An error indicating that the http connection to the function server was refused | ||
type ConnectionRefusedError string | ||
|
||
func (e ConnectionRefusedError) Error() string { | ||
return fmt.Sprintf("The local function server at %s refused the connection", string(e)) | ||
} | ||
|
||
// BadRequestError An error indicating that the body of an http request was invalid | ||
type BadRequestError string | ||
|
||
func (e BadRequestError) Error() string { | ||
return fmt.Sprintf("The request body is invalid: %s", string(e)) | ||
} | ||
|
||
// InvocationError An error indicating that a general error occurred while invoking a function | ||
type InvocationError string | ||
|
||
func (e InvocationError) Error() string { | ||
return fmt.Sprintf("Invocation of the function failed: %s", string(e)) | ||
} | ||
|
||
// InvalidResponsePayloadError error indicating that function return could not be serialized | ||
type InvalidResponsePayloadError string | ||
|
||
func (e InvalidResponsePayloadError) Error() string { | ||
return fmt.Sprintf("Unable to serialize response payload: %s", string(e)) | ||
} | ||
|
||
// UnknownSystemError error for when something unknown happens during function invocation | ||
type UnknownSystemError string | ||
|
||
func (e UnknownSystemError) Error() string { | ||
return fmt.Sprintf("Unknown system error: %s", string(e)) | ||
} |
Oops, something went wrong.