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:
Valentin Tjoncke
2017-03-21 17:13:29 -07:00
committed by Soam Vasani
parent a3f8016442
commit 1665235c14
10 changed files with 193 additions and 5 deletions
+14
View File
@@ -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
+29
View File
@@ -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.
+10
View File
@@ -0,0 +1,10 @@
package context
type (
Context map[string]interface{}
)
func New() Context {
ctx := make(map[string]interface{})
return ctx
}
+83
View File
@@ -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)
}