Pool Manager -- manage generic containers and their specialization
GenericPool is a pool of generic containers for an environment. GenericPoolManager keeps track of all GenericPools, creating them on-demand. The pool manager API is simply a "lookup" for the service URL of a function. If one exists it is returned immediately; otherwise, a generic pool is created, and then a pod is specialized from that pool. poolmgr is designed to run from within the cluster, since it connects to pod IP addresses directly. This is a first cut with many pieces missing. TODO: * Use versioned kubernetes clients instead of the unversioned one * Unit tests for GenericPoolMgr; improve unit test for GenericPool; test for API. * Handle cases where a service exists but pod backing it has failed. * On start up, use existing deployments/pods/services if they exist; in other words don't orphan resources on restart. * Kill idle resources (services, pods, even generic pools) * Autoscale generic pool (for example, by watching num ready pods)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package poolmgr
|
||||
|
||||
import (
|
||||
"github.com/platform9/fission"
|
||||
|
||||
"fmt"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getKubeClient() *unversioned.Client {
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
configOverrides := &clientcmd.ConfigOverrides{}
|
||||
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
|
||||
config, err := kubeConfig.ClientConfig()
|
||||
if err != nil {
|
||||
panic("failed loading client config")
|
||||
}
|
||||
client := unversioned.NewOrDie(config)
|
||||
return client
|
||||
}
|
||||
|
||||
type staticHandler struct {
|
||||
resp string
|
||||
}
|
||||
|
||||
func (s *staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(s.resp))
|
||||
}
|
||||
|
||||
// staticHttpServer starts an http server at port and responds to any
|
||||
// request with the given response. Use this to mock the controller
|
||||
// raw function fetch HTTP endpoint.
|
||||
func staticHttpServer(port int, response string) {
|
||||
s := &staticHandler{resp: response}
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), s))
|
||||
}
|
||||
|
||||
func TestGenericPool(t *testing.T) {
|
||||
namespace := "fission-test"
|
||||
|
||||
client := getKubeClient()
|
||||
|
||||
_, err := client.Namespaces().Create(&api.Namespace{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: namespace,
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Panicf("failed to create namespace: %v", err)
|
||||
}
|
||||
|
||||
// destroys everything in the namespace
|
||||
defer client.Namespaces().Delete(namespace)
|
||||
|
||||
env := &fission.Environment{
|
||||
Metadata: fission.Metadata{
|
||||
Name: "test-env",
|
||||
Uid: "",
|
||||
},
|
||||
RunContainerImageUrl: "fission/testing",
|
||||
}
|
||||
|
||||
gp, err := MakeGenericPool(client, env, 3, namespace)
|
||||
if err != nil {
|
||||
log.Panicf("failed to make generic pool: %v", err)
|
||||
}
|
||||
log.Printf("Pool created")
|
||||
|
||||
// test specialization
|
||||
|
||||
testFunc := `
|
||||
module.exports = function (context, callback) {
|
||||
callback(200, "Hello, world!");
|
||||
}
|
||||
`
|
||||
go staticHttpServer(2222, testFunc)
|
||||
|
||||
m := fission.Metadata{
|
||||
Name: "foo",
|
||||
Uid: "xxx-yyy",
|
||||
}
|
||||
fsvc, err := gp.GetFuncSvc(&m)
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting function svc: %v", err)
|
||||
}
|
||||
log.Printf("fsvc: %v", fsvc)
|
||||
}
|
||||
Reference in New Issue
Block a user