Move packages to root dir from src/

This is supposed to work slightly better with go import paths. I think.
This commit is contained in:
Soam Vasani
2016-09-07 14:07:26 -07:00
parent 3cd3dc37e1
commit 2aa4c58556
15 changed files with 0 additions and 0 deletions
-7
View File
@@ -1,7 +0,0 @@
# A docker image for the func container.
FROM node:4-onbuild
ADD server.js /usr/src/app/server.js
EXPOSE 8888
-20
View File
@@ -1,20 +0,0 @@
{
"name": "fission-nodejs-runtime",
"version": "0.0.0",
"author": "Soam Vasani",
"contributors": [
{
"name": "Soam Vasani",
"email": "soamvasani@platform9.com"
}
],
"description": "NodeJS run container for the fission framework",
"engines": {
"node": ">=4.2.2"
},
"dependencies": {
"express": "",
"minimist": "",
"body-parser": ""
}
}
-74
View File
@@ -1,74 +0,0 @@
'use strict';
const fs = require('fs');
const process = require('process');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
// Command line opts
const argv = require('minimist')(process.argv.slice(1));
if (!argv.codepath || !argv.port) {
console.error("Need --codepath and --port");
process.exit(1);
}
// User function. Starts out undefined.
let userFunction;
//
// Specialize this server to a given user function. The user function
// is read from argv.codepath; it's expected to be placed there by the
// fission runtime.
//
function specialize(req, res) {
// Make sure we're a generic container. (No reuse of containers.
// Once specialized, the container remains specialized.)
if (userFunction) {
res.status(400).send("Not a generic container");
return;
}
// Read and load the code. It's placed there securely by the fission runtime.
try {
var startTime = process.hrtime();
userFunction = require(argv.codepath);
var elapsed = process.hrtime(startTime);
console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]/1000000}ms`);
} catch(e) {
console.error(`user code load error: ${e}`);
res.status(500).send(JSON.stringify(e));
return;
}
res.status(202).send();
}
app.use(bodyParser.json());
app.post('/specialize', specialize);
// Generic route -- all http requests go to the user function.
app.all('/', function (req, res) {
if (!userFunction) {
res.status(500).send("Generic container: no requests supported");
return;
}
const context = {
request: req,
response: res
// TODO: context should also have: URL template params, query string, ...anything else?
};
function callback(status, body, headers) {
if (!status)
return;
if (headers) {
for (let name of Object.keys(headers)) {
res.set(name, headers[name]);
}
}
res.status(status).send(body);
}
userFunction(context, callback);
});
app.listen(argv.port);
-5
View File
@@ -1,5 +0,0 @@
module.exports = function (context, callback) {
console.log("Test function entered");
callback(200, "Hello, world!\n");
console.log("Test function exit");
}
-31
View File
@@ -1,31 +0,0 @@
#!/bin/sh
# TODO placeholder until we have better tests :)
set +x
set -e
DIR=$(dirname $0)
echo "-- Starting server"
node $DIR/../server.js --codepath $DIR/test.js --port 8888 &
function cleanup() {
echo "-- Cleanup"
kill %1
}
trap cleanup EXIT
sleep 2
echo "-- Specializing"
curl -f -X POST http://localhost:8888/specialize
echo "-- Running user function"
curl -f -X GET http://localhost:8888
curl -f -X POST http://localhost:8888
curl -f -X PUT http://localhost:8888
curl -f -X DELETE http://localhost:8888
curl -f -X TRACE http://localhost:8888
curl -f -X OPTIONS http://localhost:8888
# -I causes curl to make a HEAD request.
curl -f -I http://localhost:8888
-73
View File
@@ -1,73 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"errors"
"log"
"net/http"
"net/http/httputil"
"net/url"
)
type functionHandler struct {
fmap *functionServiceMap
poolManagerUrl string
function
}
func (*functionHandler) getServiceForFunction() (*url.URL, error) {
return nil, errors.New("not implemented")
}
func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
serviceUrl, err := fh.fmap.lookup(&fh.function)
if err != nil {
// Cache miss: request the Pool Manager to make a new service.
serviceUrl, poolErr := fh.getServiceForFunction()
if poolErr != nil {
// now we're really screwed
log.Printf("Failed to get service for function (%v,%v): %v",
fh.function.name, fh.function.uid, poolErr)
responseWriter.WriteHeader(500) // TODO: make this smarter based on the actual error
return
}
// add it to the map
fh.fmap.assign(&fh.function, serviceUrl)
}
// Proxy off our request to the serviceUrl, and send the response back.
// TODO: As an optimization we may want to cache proxies too -- this would get us
// connection reuse and possibly better performance
director := func(req *http.Request) {
// send this request to serviceurl
req.URL.Scheme = serviceUrl.Scheme
req.URL.Host = serviceUrl.Host
req.URL.Path = serviceUrl.Path
// leave the query string intact (req.URL.RawQuery)
if _, ok := req.Header["User-Agent"]; !ok {
// explicitly disable User-Agent so it's not set to default value
req.Header.Set("User-Agent", "")
}
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(responseWriter, request)
// TODO: handle failures and possibly retry here.
}
-60
View File
@@ -1,60 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"log"
"net/http"
"testing"
// "net/http/httputil"
"net/http/httptest"
"net/url"
)
func createBackendService(testResponseString string) *url.URL {
backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(testResponseString))
}))
backendURL, err := url.Parse(backendServer.URL)
if err != nil {
panic("error parsing url")
}
return backendURL
}
/*
1. Create a service at some URL
2. Add it to the function service map
3. Create a http server with some trigger url pointed at function handler
4. Send a request to that server, ensure it reaches the first service.
*/
func TestFunctionProxying(t *testing.T) {
testResponseString := "hi"
backendURL := createBackendService(testResponseString)
log.Printf("Created backend svc at %v", backendURL)
fn := &function{name: "foo", uid: "xxx"}
fmap := makeFunctionServiceMap()
fmap.assign(fn, backendURL)
fh := &functionHandler{fmap: fmap, function: *fn}
functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler))
fhURL := functionHandlerServer.URL
testRequest(fhURL, testResponseString)
}
-111
View File
@@ -1,111 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"errors"
"log"
"net/url"
)
type requestType int
const (
LOOKUP requestType = iota // lookup the map
ASSIGN // assign function
NEXT_GEN // increment current generation
SWEEP // delete all but the current generation
)
type functionServiceMapResponse struct {
serviceUrl url.URL
error
}
type functionServiceMapRequest struct {
function
serviceUrl url.URL
requestType
responseChannel chan<- functionServiceMapResponse
}
type functionServiceMapEntry struct {
serviceUrl url.URL
generation uint64
}
type functionServiceMap struct {
// map (funcname, uid) -> url
svc map[function]functionServiceMapEntry
currentGeneration uint64
requestChannel chan *functionServiceMapRequest
}
func makeFunctionServiceMap() *functionServiceMap {
fmap := &functionServiceMap{}
fmap.requestChannel = make(chan *functionServiceMapRequest)
fmap.svc = make(map[function]functionServiceMapEntry)
go fmap.functionServiceMapWork()
return fmap
}
func (fmap *functionServiceMap) functionServiceMapWork() {
for {
req := <-fmap.requestChannel
switch req.requestType {
case LOOKUP:
e, present := fmap.svc[req.function]
if present {
req.responseChannel <- functionServiceMapResponse{serviceUrl: e.serviceUrl}
} else {
req.responseChannel <- functionServiceMapResponse{error: errors.New("not found")}
}
case ASSIGN:
fmap.svc[req.function] =
functionServiceMapEntry{serviceUrl: req.serviceUrl, generation: fmap.currentGeneration}
// no response
case NEXT_GEN:
fmap.currentGeneration++
// no response
case SWEEP:
log.Panic("not implemented")
default:
log.Panic("bad request")
}
}
}
func (fmap *functionServiceMap) lookup(f *function) (*url.URL, error) {
respChannel := make(chan functionServiceMapResponse)
fmap.requestChannel <- &functionServiceMapRequest{function: *f, requestType: LOOKUP, responseChannel: respChannel}
resp := <-respChannel
if resp.error != nil {
return nil, resp.error
} else {
return &resp.serviceUrl, nil
}
}
func (fmap *functionServiceMap) assign(f *function, serviceUrl *url.URL) {
fmap.requestChannel <- &functionServiceMapRequest{function: *f, serviceUrl: *serviceUrl, requestType: ASSIGN}
}
func (fmap *functionServiceMap) nextGen() {
fmap.requestChannel <- &functionServiceMapRequest{requestType: NEXT_GEN}
}
func (fmap *functionServiceMap) sweep() {
fmap.requestChannel <- &functionServiceMapRequest{requestType: SWEEP}
}
-47
View File
@@ -1,47 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"net/url"
"testing"
)
func TestFunctionServiceMap(t *testing.T) {
m := makeFunctionServiceMap()
fn := &function{name: "foo", uid: "012"}
u, err := url.Parse("/foo012")
if err != nil {
t.Errorf("can't parse url")
}
m.assign(fn, u)
v, err := m.lookup(fn)
if err != nil {
t.Errorf("Lookup error: %v", err)
}
if *v != *u {
t.Errorf("Expected %#v, got %#v", u, v)
}
fn.name = "bar"
_, err2 := m.lookup(fn)
if err2 == nil {
t.Errorf("No error on missing entry")
}
}
-62
View File
@@ -1,62 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"github.com/gorilla/mux"
)
type HTTPTriggerSet struct {
*functionServiceMap
*mutableRouter
controllerUrl string
poolManagerUrl string
triggers []httptrigger
}
func makeHTTPTriggerSet(fmap *functionServiceMap, controllerUrl string, poolManagerUrl string) *HTTPTriggerSet {
triggers := make([]httptrigger, 1)
return &HTTPTriggerSet{
functionServiceMap: fmap,
triggers: triggers,
controllerUrl: controllerUrl,
poolManagerUrl: poolManagerUrl,
}
}
func (triggers *HTTPTriggerSet) subscribeRouter(mr *mutableRouter) {
triggers.mutableRouter = mr
mr.updateRouter(triggers.getRouterFromTriggers())
go triggers.watchTriggers()
}
func (triggers *HTTPTriggerSet) getRouterFromTriggers() *mux.Router {
muxRouter := mux.NewRouter()
for _, trigger := range triggers.triggers {
fh := &functionHandler{
fmap: triggers.functionServiceMap,
function: trigger.function,
poolManagerUrl: triggers.poolManagerUrl,
}
muxRouter.HandleFunc(trigger.urlPattern, fh.handler)
}
return muxRouter
}
func (triggers *HTTPTriggerSet) watchTriggers() {
// watch controller for updates to triggers and update the router accordingly
}
-54
View File
@@ -1,54 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"github.com/gorilla/mux"
"log"
"net/http"
"sync/atomic"
)
//
// mutableRouter wraps the mux router, and allows the router to be
// atomically changed.
//
type mutableRouter struct {
router atomic.Value // mux.Router
}
func NewMutableRouter(handler *mux.Router) *mutableRouter {
mr := mutableRouter{}
mr.router.Store(handler)
return &mr
}
func (mr *mutableRouter) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
// Atomically grab the underlying mux router and call it.
routerValue := mr.router.Load()
router, ok := routerValue.(*mux.Router)
if !ok {
log.Panic("Invalid router type")
}
router.ServeHTTP(responseWriter, request)
}
func (mr *mutableRouter) updateRouter(newHandler *mux.Router) {
log.Print("Updating router")
mr.router.Store(newHandler)
}
-94
View File
@@ -1,94 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"github.com/gorilla/mux"
"log"
"net/http"
"testing"
"time"
)
func OldHandler(responseWriter http.ResponseWriter, request *http.Request) {
responseWriter.Write([]byte("old handler"))
}
func NewHandler(responseWriter http.ResponseWriter, request *http.Request) {
responseWriter.Write([]byte("new handler"))
}
func verifyRequest(expectedResponse string) {
targetUrl := "http://localhost:3333"
testRequest(targetUrl, expectedResponse)
}
func startServer(mr *mutableRouter) {
http.ListenAndServe(":3333", mr)
}
func spamServer(quit chan bool) {
i := 0
for {
select {
case <-quit:
break
default:
i = i + 1
resp, err := http.Get("http://localhost:3333")
if err != nil {
log.Panicf("failed to make get request %v: %v", i, err)
}
resp.Body.Close()
}
}
}
func TestMutableMux(t *testing.T) {
// make a simple mutable router
log.Print("Create mutable router")
muxRouter := mux.NewRouter()
muxRouter.HandleFunc("/", OldHandler)
mr := NewMutableRouter(muxRouter)
// start http server
log.Print("Start http server")
go startServer(mr)
// continuously make requests, panic if any fails
time.Sleep(100 * time.Millisecond)
q := make(chan bool)
go spamServer(q)
time.Sleep(5 * time.Millisecond)
// connect and verify old handler
log.Print("Verify old handler")
verifyRequest("old handler")
// change the muxer
log.Print("Change mux router")
newMuxRouter := mux.NewRouter()
newMuxRouter.HandleFunc("/", NewHandler)
mr.updateRouter(newMuxRouter)
// connect and verify the new handler
log.Print("Verify new handler")
verifyRequest("new handler")
q <- true
time.Sleep(100 * time.Millisecond)
}
-102
View File
@@ -1,102 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This is the Fission Router package.
Its job is to:
1. Keep track of HTTP triggers and their mappings to functions
Use the controller API to get and watch this state.
2. Given a function, get a reference to a routable function run service
Use the ContainerPoolManager API to get a service backed by one
or more function run containers. The container(s) backing the
service may be newly created, or they might be reused. The only
requirement is that one or more containers backs the service.
3. Forward the request to the service, and send the response back.
Plain ol HTTP.
*/
package router
import (
"fmt"
"github.com/gorilla/mux"
flag "github.com/ogier/pflag"
"net/http"
)
type (
function struct {
name string
uid string
}
httptrigger struct {
urlPattern string
function
}
options struct {
port int
poolManagerUrl string
controllerUrl string
//...
}
)
// request url ---[mux]---> function(name,uid) ----[fmap]----> k8s service url
// request url ---[trigger]---> function(name, deployment) ----[deployment]----> function(name, uid) ----[pool mgr]---> k8s service url
func router(httpTriggerSet *HTTPTriggerSet) *mutableRouter {
muxRouter := mux.NewRouter()
mr := NewMutableRouter(muxRouter)
httpTriggerSet.subscribeRouter(mr)
return mr
}
func server(port int, httpTriggerSet *HTTPTriggerSet) {
mr := router(httpTriggerSet)
url := fmt.Sprintf(":%v", port)
http.ListenAndServe(url, mr)
}
func getOptions() *options {
options := &options{}
flag.IntVar(&options.port, "port", 80, "Port to listen on")
// default to using dns service discovery
flag.StringVar(&options.poolManagerUrl, "poolmanager_url", "http://poolmanager/", "URL for the PoolManager service")
flag.StringVar(&options.controllerUrl, "controller_url", "http://controller/", "URL for the controller service")
return options
}
func main() {
options := getOptions()
fmap := makeFunctionServiceMap()
triggers := makeHTTPTriggerSet(fmap, options.controllerUrl, options.poolManagerUrl)
server(options.port, triggers)
}
-44
View File
@@ -1,44 +0,0 @@
/*
Copyright 2016 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package router
import (
"fmt"
"testing"
"time"
)
func TestRouter(t *testing.T) {
fmap := makeFunctionServiceMap()
fn := &function{name: "foo", uid: "xxx"}
testResponseString := "hi"
testServiceUrl := createBackendService(testResponseString)
fmap.assign(fn, testServiceUrl)
triggers := makeHTTPTriggerSet(fmap, "", "")
triggerUrl := "/foo"
triggers.triggers = append(triggers.triggers, httptrigger{triggerUrl, *fn})
port := 4242
go server(port, triggers)
time.Sleep(100 * time.Millisecond)
testUrl := fmt.Sprintf("http://localhost:%v%v", port, triggerUrl)
testRequest(testUrl, testResponseString)
}
-30
View File
@@ -1,30 +0,0 @@
package router
import (
"io/ioutil"
"log"
"net/http"
)
func testRequest(targetUrl string, expectedResponse string) {
resp, err := http.Get(targetUrl)
if err != nil {
log.Panicf("failed to make get request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Panicf("response status: %v", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Panic("failed to read response")
}
bodyStr := string(body)
log.Printf("Server responded with %v", bodyStr)
if bodyStr != expectedResponse {
log.Panic("Unexpected response")
}
}