Golang runtime (#125)
Minimal golang runtime. Functions are built as Go plugins, and the plugin is uploaded to fission. See `environments/go/README.md` for instructions and `examples/go` for a usage example. For now, we're re-using the 'code' field of the function to store the binary plugin. With v2 environments, this will be stored separately as a binary package.
This commit is contained in:
committed by
Soam Vasani
parent
a3f8016442
commit
1665235c14
+2
-2
@@ -16,5 +16,5 @@ script:
|
|||||||
- ./fission-bundle/build.sh
|
- ./fission-bundle/build.sh
|
||||||
- hack/verify-gofmt.sh
|
- hack/verify-gofmt.sh
|
||||||
- /tmp/test-etcd/etcd &
|
- /tmp/test-etcd/etcd &
|
||||||
- go test -v -i $(go list ./... | grep -v '/vendor/')
|
- go test -v -i $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
||||||
- go test -v $(go list ./... | grep -v '/vendor/')
|
- go test -v $(go list ./... | grep -v '/vendor/' | grep -v 'examples/go')
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM golang:1.8
|
||||||
|
|
||||||
|
ENV GOPATH /usr
|
||||||
|
ENV APP ${GOPATH}/src/github.com/fission/fission/environments/go
|
||||||
|
|
||||||
|
ADD context ${APP}/context
|
||||||
|
ADD server.go ${APP}
|
||||||
|
|
||||||
|
WORKDIR ${APP}
|
||||||
|
RUN go get
|
||||||
|
RUN go build -o /server server.go
|
||||||
|
|
||||||
|
ENTRYPOINT ["/server"]
|
||||||
|
EXPOSE 8888
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Fission: Go Environment
|
||||||
|
|
||||||
|
This is the Go environment for Fission.
|
||||||
|
|
||||||
|
It's a Docker image containing a Go 1.8rc3 runtime, along with a dynamic loader.
|
||||||
|
|
||||||
|
## Build this image
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t USER/go-runtime . && docker push USER/go-runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using the image in fission
|
||||||
|
|
||||||
|
You can add this customized image to fission with "fission env
|
||||||
|
create":
|
||||||
|
|
||||||
|
```
|
||||||
|
fission env create --name go-runtime --image USER/go-runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you already have an environment, you can update its image:
|
||||||
|
|
||||||
|
```
|
||||||
|
fission env update --name go-runtime --image USER/go-runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, fission functions that have the env parameter set to the
|
||||||
|
same environment name as this command will use this environment.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
type (
|
||||||
|
Context map[string]interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() Context {
|
||||||
|
ctx := make(map[string]interface{})
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"plugin"
|
||||||
|
|
||||||
|
"github.com/fission/fission/environments/go/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CODE_PATH = "/userfunc/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
var userFunc http.HandlerFunc
|
||||||
|
|
||||||
|
func loadPlugin() http.HandlerFunc {
|
||||||
|
p, err := plugin.Open(CODE_PATH)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sym, err := p.Lookup("Handler")
|
||||||
|
if err != nil {
|
||||||
|
panic("Entry point not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch h := sym.(type) {
|
||||||
|
case *http.Handler:
|
||||||
|
return (*h).ServeHTTP
|
||||||
|
case *http.HandlerFunc:
|
||||||
|
return *h
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
return h
|
||||||
|
case func(context.Context, http.ResponseWriter, *http.Request):
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c := context.New()
|
||||||
|
h(c, w, r)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("Entry point not found: bad type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func specializeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if userFunc != nil {
|
||||||
|
w.WriteHeader(400)
|
||||||
|
w.Write([]byte("Not a generic container"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(CODE_PATH)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte(CODE_PATH + ": not found"))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Specializing ...")
|
||||||
|
userFunc = loadPlugin()
|
||||||
|
fmt.Println("Done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/specialize", specializeHandler)
|
||||||
|
|
||||||
|
// Generic route -- all http requests go to the user function.
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if userFunc == nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("Generic container: no requests supported"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userFunc(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println("Listening on 8888 ...")
|
||||||
|
http.ListenAndServe(":8888", nil)
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Go examples
|
||||||
|
|
||||||
|
The `go` runtime uses the [`plugin` package](https://golang.org/pkg/plugin/) to dynamically load an HTTP handler.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- go 1.8
|
||||||
|
- A [go-runtime fission environment](environments/go/README.md)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### hello.go
|
||||||
|
|
||||||
|
`hello.go` is an very basic HTTP handler returning `"Hello, World!"`.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
# Build the function as a plugin
|
||||||
|
$ ./build.sh
|
||||||
|
|
||||||
|
# Upload the function to fission
|
||||||
|
$ fission function create --name hello --env go-runtime --package hello.so
|
||||||
|
|
||||||
|
# Map /hello to the hello function
|
||||||
|
$ fission route create --method GET --url /hello --function hello
|
||||||
|
|
||||||
|
# Run the function
|
||||||
|
$ curl http://$FISSION_ROUTER/hello
|
||||||
|
Hello, World!
|
||||||
|
```
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
FISSION_PATH="github.com/fission/fission"
|
||||||
|
|
||||||
|
docker run --rm -v $GOPATH/src/$FISSION_PATH:/usr/src/$FISSION_PATH \
|
||||||
|
-e GOPATH=/usr/ \
|
||||||
|
-w /usr/src/$FISSION_PATH/examples/go \
|
||||||
|
golang:1.8 go build -buildmode=plugin -o hello.so hello.go
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
msg := "Hello, World!"
|
||||||
|
w.Write([]byte(msg))
|
||||||
|
}
|
||||||
+4
-1
@@ -74,7 +74,10 @@ func fnCreate(c *cli.Context) error {
|
|||||||
|
|
||||||
fileName := c.String("code")
|
fileName := c.String("code")
|
||||||
if len(fileName) == 0 {
|
if len(fileName) == 0 {
|
||||||
fatal("Need --code argument.")
|
fileName = c.String("package")
|
||||||
|
if len(fileName) == 0 {
|
||||||
|
fatal("Need --code or --package argument.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code := fnFetchCode(fileName)
|
code := fnFetchCode(fileName)
|
||||||
|
|||||||
+3
-2
@@ -39,13 +39,14 @@ func main() {
|
|||||||
fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
|
fnNameFlag := cli.StringFlag{Name: "name", Usage: "function name"}
|
||||||
fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
|
fnEnvNameFlag := cli.StringFlag{Name: "env", Usage: "environment name for function"}
|
||||||
fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
|
fnCodeFlag := cli.StringFlag{Name: "code", Usage: "local path or URL for source code"}
|
||||||
|
fnPackageFlag := cli.StringFlag{Name: "package", Usage: "local path or URL for binary package"}
|
||||||
fnUidFlag := cli.StringFlag{Name: "uid", Usage: "function uid, optional (use latest if unspecified)"}
|
fnUidFlag := cli.StringFlag{Name: "uid", Usage: "function uid, optional (use latest if unspecified)"}
|
||||||
fnSubcommands := []cli.Command{
|
fnSubcommands := []cli.Command{
|
||||||
{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, htUrlFlag, htMethodFlag}, Action: fnCreate},
|
{Name: "create", Usage: "Create new function (and optionally, an HTTP route to it)", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag, htUrlFlag, htMethodFlag}, Action: fnCreate},
|
||||||
{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGet},
|
{Name: "get", Usage: "Get function source code", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGet},
|
||||||
{Name: "edit", Usage: "Edit function source code in $EDITOR", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnEdit},
|
{Name: "edit", Usage: "Edit function source code in $EDITOR", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnEdit},
|
||||||
{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGetMeta},
|
{Name: "getmeta", Usage: "Get function metadata", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnGetMeta},
|
||||||
{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag}, Action: fnUpdate},
|
{Name: "update", Usage: "Update function source code", Flags: []cli.Flag{fnNameFlag, fnEnvNameFlag, fnCodeFlag, fnPackageFlag}, Action: fnUpdate},
|
||||||
{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnDelete},
|
{Name: "delete", Usage: "Delete function", Flags: []cli.Flag{fnNameFlag, fnUidFlag}, Action: fnDelete},
|
||||||
{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
|
{Name: "list", Usage: "List all functions", Flags: []cli.Flag{}, Action: fnList},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user