V2 types and TPR (#266)

This changes the core fission function, environment and trigger types. It also changes Fission's storage to use ThirdPartyResources.

 - Functions are now specified by packages. Functions can also have both source and deployment packages. A package can be specified by a literal, or by a URL.
 - Environments have a build and runtime component.
 - Triggers reference functions by a FunctionReference. This is a layer of indirection between triggers and functions, and will allow things like incremental function upgrades in future releases.

See Documentation/wip/env-v2.md for design discussion about points 1 and 2.

Changes:

* V2 Types

All types now have a spec, following the pattern of K8s objects.

Functions now have source and deployment packages. A Package can be
specified by literal, or by URL.

Environments now have a builder and runtime component.

All triggers use a new FunctionReference to specify the function. This
for now only uses a function name, but in the future can be extended
to be more flexible.

A new FunctionLoadRequest type is added for specialization requests to
the environment runtime.

* TPR types, TPR init code, and a "fission client"

Implements TPR types using the spec types in fission/types.go.

Adds code for adding creating TPR types, and convenient types for crud
operations on each of our resource types.

Adds code for connecting to K8s API and configuring a REST client with
fission types set up.

* Change old stateful controller into a thin apiserver

This apiserver is now simply a stateless api layer on top of the TPR
types. At the moment it doesn't do anything that couldn't be done by
simply talking to the TPR types. In the future we can have better
validation and potentially some higher level APIs (like versioning for
example) in here.

* Split controller client into files and update for v2 types.

* Update CLI for v2 types.

As far as possible we keep the CLI flags the same. We'll have to add
flags for source/deploy packages and builder/runtime
environments. That will come in the next change.

* Update poolmgr and fetcher for v2 types.

Also adds a poolmgr_test.

* Update router for new types.

Also adds a function reference resolver, which separates out the job
of resolving a FunctionReference to a function.

* Update kubewatcher and timer for v2 types.

* Update Message Queue trigger type for v2 types.

* Minor odds and ends.

* Fission bundle CLI updates

Remove controllerUrl flag, since we don't need it any more.

* Remove etcd deployment (replaced by storing state in TPR)

Also update the poolmgr commandline, and use an env var for the
fetcher image URL.

* Explicit ChecksumType and consts

* Clarify separation of environment interface types
This commit is contained in:
Soam Vasani
2017-08-05 01:18:30 -07:00
committed by GitHub
parent 37aa266a4d
commit e238776bf7
81 changed files with 4649 additions and 3588 deletions
+43 -46
View File
@@ -26,19 +26,16 @@ import (
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
kerrors "k8s.io/client-go/1.5/pkg/api/errors"
"github.com/fission/fission"
"github.com/fission/fission/fission/logdb"
"github.com/fission/fission/tpr"
)
type (
API struct {
FunctionStore
HTTPTriggerStore
TimeTriggerStore
MessageQueueTriggerStore
EnvironmentStore
WatchStore
fissionClient *tpr.FissionClient
}
logDBConfig struct {
@@ -48,16 +45,8 @@ type (
}
)
func MakeAPI(rs *ResourceStore) *API {
api := &API{
FunctionStore: FunctionStore{ResourceStore: *rs},
HTTPTriggerStore: HTTPTriggerStore{ResourceStore: *rs},
TimeTriggerStore: TimeTriggerStore{ResourceStore: *rs},
MessageQueueTriggerStore: MessageQueueTriggerStore{ResourceStore: *rs},
EnvironmentStore: EnvironmentStore{ResourceStore: *rs},
WatchStore: WatchStore{ResourceStore: *rs},
}
return api
func MakeAPI() (*API, error) {
return makeTPRBackedAPI()
}
func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
@@ -71,6 +60,14 @@ func (api *API) respondWithSuccess(w http.ResponseWriter, resp []byte) {
func (api *API) respondWithError(w http.ResponseWriter, err error) {
debug.PrintStack()
// this error type comes with an HTTP code, so just use that
se, ok := err.(*kerrors.StatusError)
if ok {
http.Error(w, string(se.ErrStatus.Reason), int(se.ErrStatus.Code))
return
}
code, msg := fission.GetHTTPError(err)
log.Errorf("Error: %v: %v", code, msg)
http.Error(w, msg, code)
@@ -102,41 +99,41 @@ func (api *API) Serve(port int) {
r := mux.NewRouter()
r.HandleFunc("/", api.HomeHandler)
r.HandleFunc("/v1/functions", api.FunctionApiList).Methods("GET")
r.HandleFunc("/v1/functions", api.FunctionApiCreate).Methods("POST")
r.HandleFunc("/v1/functions/{function}", api.FunctionApiGet).Methods("GET")
r.HandleFunc("/v1/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
r.HandleFunc("/v1/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
r.HandleFunc("/v2/functions", api.FunctionApiList).Methods("GET")
r.HandleFunc("/v2/functions", api.FunctionApiCreate).Methods("POST")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiGet).Methods("GET")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiUpdate).Methods("PUT")
r.HandleFunc("/v2/functions/{function}", api.FunctionApiDelete).Methods("DELETE")
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiList).Methods("GET")
r.HandleFunc("/v1/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v1/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/http", api.HTTPTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v1/environments", api.EnvironmentApiList).Methods("GET")
r.HandleFunc("/v1/environments", api.EnvironmentApiCreate).Methods("POST")
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
r.HandleFunc("/v1/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
r.HandleFunc("/v2/environments", api.EnvironmentApiList).Methods("GET")
r.HandleFunc("/v2/environments", api.EnvironmentApiCreate).Methods("POST")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiGet).Methods("GET")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT")
r.HandleFunc("/v2/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE")
r.HandleFunc("/v1/watches", api.WatchApiList).Methods("GET")
r.HandleFunc("/v1/watches", api.WatchApiCreate).Methods("POST")
r.HandleFunc("/v1/watches/{watch}", api.WatchApiGet).Methods("GET")
r.HandleFunc("/v1/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
r.HandleFunc("/v1/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
r.HandleFunc("/v2/watches", api.WatchApiList).Methods("GET")
r.HandleFunc("/v2/watches", api.WatchApiCreate).Methods("POST")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiGet).Methods("GET")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiUpdate).Methods("PUT")
r.HandleFunc("/v2/watches/{watch}", api.WatchApiDelete).Methods("DELETE")
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiList).Methods("GET")
r.HandleFunc("/v1/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v1/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/time", api.TimeTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/time/{timeTrigger}", api.TimeTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/v1/triggers/messagequeue", api.MessageQueueTriggerApiList).Methods("GET")
r.HandleFunc("/v1/triggers/messagequeue", api.MessageQueueApiCreate).Methods("POST")
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiGet).Methods("GET")
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiUpdate).Methods("PUT")
r.HandleFunc("/v1/triggers/messagequeue/{mqTrigger}", api.MessageQueueApiDelete).Methods("DELETE")
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiList).Methods("GET")
r.HandleFunc("/v2/triggers/messagequeue", api.MessageQueueTriggerApiCreate).Methods("POST")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiGet).Methods("GET")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiUpdate).Methods("PUT")
r.HandleFunc("/v2/triggers/messagequeue/{mqTrigger}", api.MessageQueueTriggerApiDelete).Methods("DELETE")
r.HandleFunc("/proxy/{dbType}", api.FunctionLogsApiPost).Methods("POST")
+137 -210
View File
@@ -19,35 +19,50 @@ package controller
import (
"flag"
"io/ioutil"
"log"
"net/http"
"os"
"testing"
"time"
etcdClient "github.com/coreos/etcd/client"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"k8s.io/client-go/1.5/pkg/api"
"bytes"
"github.com/fission/fission"
"github.com/fission/fission/controller/client"
"github.com/fission/fission/tpr"
)
var g struct {
client *client.Client
}
func assertNameReuseFails(err error, name string) {
func panicIf(err error) {
if err != nil {
log.Panicf("err: %v", err)
}
}
func assert(c bool, msg string) {
if !c {
log.Fatalf("assert failed: %v", msg)
}
}
func assertNameReuseFailure(err error, name string) {
assert(err != nil, "recreating "+name+" with same name must fail")
fe, ok := err.(fission.Error)
assert(ok, "error must be a fission Error")
assert(fe.Code == fission.ErrorNameExists, "error must be a name exists error")
}
func assertNotFoundFails(err error, name string) {
func assertNotFoundFailure(err error, name string) {
assert(err != nil, "requesting a non-existent "+name+" must fail")
fe, ok := err.(fission.Error)
assert(ok, "error must be a fission Error")
assert(fe.Code == fission.ErrorNotFound, "error must be a not found error")
if fe.Code != fission.ErrorNotFound {
log.Fatalf("error must be a not found error: %v", fe)
}
}
func assertCronSpecFails(err error) {
@@ -58,67 +73,42 @@ func assertCronSpecFails(err error) {
}
func TestFunctionApi(t *testing.T) {
log.SetFormatter(&log.TextFormatter{DisableColors: true})
name1 := "foo"
name2 := "bar"
testFunc := &fission.Function{
Metadata: fission.Metadata{
Name: "foo",
Uid: "",
testFunc := &tpr.Function{
Metadata: api.ObjectMeta{
Name: name1,
Namespace: api.NamespaceDefault,
},
Environment: fission.Metadata{
Name: "nodejs",
Uid: "xxx",
Spec: fission.FunctionSpec{
EnvironmentName: "nodejs",
Deployment: fission.Package{
Literal: []byte("code1"),
},
},
Code: "code1",
}
_, err := g.client.FunctionGet(&fission.Metadata{Name: "foo"})
assertNotFoundFails(err, "function")
_, err := g.client.FunctionGet(&api.ObjectMeta{
Name: testFunc.Metadata.Name,
Namespace: api.NamespaceDefault,
})
assertNotFoundFailure(err, "function")
m, err := g.client.FunctionCreate(testFunc)
panicIf(err)
uid1 := m.Uid
//log.Printf("Created function %v: %v", m.Name, m.Uid)
_, err = g.client.FunctionCreate(testFunc)
assertNameReuseFails(err, "function")
assertNameReuseFailure(err, "function")
code, err := g.client.FunctionGetRaw(m)
code, err := g.client.FunctionGetRawDeployment(m)
panicIf(err)
assert(string(code) == testFunc.Code, "code from FunctionGetRaw must match created function")
assert(bytes.Compare(code, testFunc.Spec.Deployment.Literal) == 0, "code from FunctionGetRawDeployment must match created function")
testFunc.Code = "code2"
m, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
uid2 := m.Uid
//log.Printf("Updated function %v: %v", m.Name, m.Uid)
m.Uid = uid1
testFunc.Code = "code1"
f, err := g.client.FunctionGet(m)
testFunc.Spec.Deployment.Literal = []byte("code2")
_, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
testFunc.Metadata.Uid = m.Uid
//log.Printf("f = %#v", f)
//log.Printf("testFunc = %#v", testFunc)
assert(*f == *testFunc, "first version should match when read by uid")
m.Uid = uid2
testFunc.Metadata.Uid = m.Uid
testFunc.Code = "code2"
f, err = g.client.FunctionGet(m)
panicIf(err)
assert(*f == *testFunc, "second version should match when read by uid")
m.Uid = ""
testFunc.Metadata.Uid = uid2
testFunc.Code = "code2"
f, err = g.client.FunctionGet(m)
panicIf(err)
assert(*f == *testFunc, "second version should match when read as latest")
testFunc.Metadata.Name = "bar"
testFunc.Metadata.Name = name2
m, err = g.client.FunctionCreate(testFunc)
panicIf(err)
@@ -127,7 +117,7 @@ func TestFunctionApi(t *testing.T) {
assert(len(funcs) == 2,
"created two functions, but didn't find them")
funcs_url := g.client.Url + "/v1/functions"
funcs_url := g.client.Url + "/v2/functions"
resp, err := http.Get(funcs_url)
panicIf(err)
defer resp.Body.Close()
@@ -141,112 +131,55 @@ func TestFunctionApi(t *testing.T) {
}
assert(found, "incorrect response content type")
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo"})
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name1, Namespace: api.NamespaceDefault})
panicIf(err)
err = g.client.FunctionDelete(&fission.Metadata{Name: "bar"})
err = g.client.FunctionDelete(&api.ObjectMeta{Name: name2, Namespace: api.NamespaceDefault})
panicIf(err)
}
func TestFunctionVersionApi(t *testing.T) {
testFunc := &fission.Function{
Metadata: fission.Metadata{
Name: "foo",
Uid: "",
},
Environment: fission.Metadata{
Name: "nodejs",
Uid: "xxx",
},
Code: "code1",
}
testFunc.Code = "code1"
m, err := g.client.FunctionCreate(testFunc)
panicIf(err)
uid1 := m.Uid
testFunc.Code = "code2"
m, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
uid2 := m.Uid
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo", Uid: uid1})
panicIf(err)
f, err := g.client.FunctionGet(&fission.Metadata{Name: "foo"})
panicIf(err)
assert(f.Metadata.Uid == uid2, "deleted version1, but version2 does not exist")
testFunc.Code = "code3"
m, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
uid3 := m.Uid
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo", Uid: uid3})
panicIf(err)
f, err = g.client.FunctionGet(&fission.Metadata{Name: "foo"})
panicIf(err)
assert(f.Metadata.Uid == uid2, "deleted version3, but version2 does not exist")
testFunc.Code = "code4"
m, err = g.client.FunctionUpdate(testFunc)
panicIf(err)
err = g.client.FunctionDelete(&fission.Metadata{Name: "foo"})
panicIf(err)
funcs, err := g.client.FunctionList()
panicIf(err)
assert(len(funcs) == 0,
"created one function with two versions(2 and 4), delete without uid but cannot delete them all")
}
func TestHTTPTriggerApi(t *testing.T) {
testTrigger := &fission.HTTPTrigger{
Metadata: fission.Metadata{
Name: "xxx",
Uid: "yyy",
testTrigger := &tpr.Httptrigger{
Metadata: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
UrlPattern: "/hello",
Function: fission.Metadata{
Name: "foo",
Uid: "",
Spec: fission.HTTPTriggerSpec{
RelativeURL: "/hello",
FunctionReference: fission.FunctionReference{
Type: fission.FunctionReferenceTypeFunctionName,
Name: "foo",
},
},
}
_, err := g.client.HTTPTriggerGet(&fission.Metadata{Name: "foo"})
assertNotFoundFails(err, "trigger")
_, err := g.client.HTTPTriggerGet(&api.ObjectMeta{
Name: testTrigger.Metadata.Name,
Namespace: api.NamespaceDefault,
})
assertNotFoundFailure(err, "httptrigger")
m, err := g.client.HTTPTriggerCreate(testTrigger)
panicIf(err)
defer g.client.HTTPTriggerDelete(m)
_, err = g.client.HTTPTriggerCreate(testTrigger)
assertNameReuseFails(err, "trigger")
assertNameReuseFailure(err, "httptrigger")
tr, err := g.client.HTTPTriggerGet(m)
panicIf(err)
testTrigger.Metadata.Uid = m.Uid
assert(*testTrigger == *tr, "trigger should match after reading")
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
testTrigger.UrlPattern = "/hi"
m2, err := g.client.HTTPTriggerUpdate(testTrigger)
testTrigger.Spec.RelativeURL = "/hi"
_, err = g.client.HTTPTriggerUpdate(testTrigger)
panicIf(err)
m.Uid = m2.Uid
tr, err = g.client.HTTPTriggerGet(m)
panicIf(err)
testTrigger.Metadata.Uid = m.Uid
assert(*testTrigger == *tr, "trigger should match after reading")
testTrigger.Metadata.Name = "yyy"
m, err = g.client.HTTPTriggerCreate(testTrigger)
_, err = g.client.HTTPTriggerCreate(testTrigger)
assert(err != nil, "duplicate trigger should not be allowed")
testTrigger.UrlPattern = "/hi2"
m, err = g.client.HTTPTriggerCreate(testTrigger)
testTrigger.Spec.RelativeURL = "/hi2"
m2, err := g.client.HTTPTriggerCreate(testTrigger)
panicIf(err)
defer g.client.HTTPTriggerDelete(m)
defer g.client.HTTPTriggerDelete(m2)
ts, err := g.client.HTTPTriggerList()
panicIf(err)
@@ -254,42 +187,42 @@ func TestHTTPTriggerApi(t *testing.T) {
}
func TestEnvironmentApi(t *testing.T) {
testEnv := &fission.Environment{
Metadata: fission.Metadata{
Name: "xxx",
Uid: "yyy",
testEnv := &tpr.Environment{
Metadata: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: fission.EnvironmentSpec{
Runtime: fission.Runtime{
Image: "gcr.io/xyz",
},
},
RunContainerImageUrl: "gcr.io/xyz",
}
_, err := g.client.EnvironmentGet(&fission.Metadata{Name: "foo"})
assertNotFoundFails(err, "environment")
_, err := g.client.EnvironmentGet(&api.ObjectMeta{
Name: testEnv.Metadata.Name,
Namespace: api.NamespaceDefault,
})
assertNotFoundFailure(err, "environment")
m, err := g.client.EnvironmentCreate(testEnv)
panicIf(err)
defer g.client.EnvironmentDelete(m)
_, err = g.client.EnvironmentCreate(testEnv)
assertNameReuseFails(err, "environment")
assertNameReuseFailure(err, "environment")
tr, err := g.client.EnvironmentGet(m)
e, err := g.client.EnvironmentGet(m)
panicIf(err)
testEnv.Metadata.Uid = m.Uid
assert(*testEnv == *tr, "env should match after reading")
assert(testEnv.Spec == e.Spec, "env should match after reading")
testEnv.RunContainerImageUrl = "/hi"
m2, err := g.client.EnvironmentUpdate(testEnv)
testEnv.Spec.Runtime.Image = "another-img"
_, err = g.client.EnvironmentUpdate(testEnv)
panicIf(err)
m.Uid = m2.Uid
tr, err = g.client.EnvironmentGet(m)
testEnv.Metadata.Name = "bar"
m2, err := g.client.EnvironmentCreate(testEnv)
panicIf(err)
testEnv.Metadata.Uid = m.Uid
assert(*testEnv == *tr, "env should match after reading")
testEnv.Metadata.Name = "yyy"
m, err = g.client.EnvironmentCreate(testEnv)
panicIf(err)
defer g.client.EnvironmentDelete(m)
defer g.client.EnvironmentDelete(m2)
ts, err := g.client.EnvironmentList()
panicIf(err)
@@ -297,36 +230,38 @@ func TestEnvironmentApi(t *testing.T) {
}
func TestWatchApi(t *testing.T) {
testWatch := &fission.Watch{
Metadata: fission.Metadata{
Name: "xxx",
Uid: "yyy",
testWatch := &tpr.Kuberneteswatchtrigger{
Metadata: api.ObjectMeta{
Name: "xxx",
Namespace: api.NamespaceDefault,
},
Namespace: "default",
ObjType: "pod",
LabelSelector: "",
FieldSelector: "",
Function: fission.Metadata{
Name: "foo",
Uid: "",
Spec: fission.KubernetesWatchTriggerSpec{
Namespace: "default",
Type: "pod",
FunctionReference: fission.FunctionReference{
Type: fission.FunctionReferenceTypeFunctionName,
Name: "foo",
},
},
Target: "",
}
_, err := g.client.WatchGet(&fission.Metadata{Name: "foo"})
assertNotFoundFails(err, "watch")
_, err := g.client.WatchGet(&api.ObjectMeta{
Name: testWatch.Metadata.Name,
Namespace: api.NamespaceDefault,
})
assertNotFoundFailure(err, "watch")
m, err := g.client.WatchCreate(testWatch)
panicIf(err)
defer g.client.WatchDelete(m)
_, err = g.client.WatchCreate(testWatch)
assertNameReuseFails(err, "watch")
assertNameReuseFailure(err, "watch")
w, err := g.client.WatchGet(m)
panicIf(err)
testWatch.Metadata.Uid = m.Uid
w.Target = ""
assert(*testWatch == *w, "watch should match after reading")
assert((testWatch.Spec.Namespace == w.Spec.Namespace &&
testWatch.Spec.Type == w.Spec.Type &&
testWatch.Spec.FunctionReference == w.Spec.FunctionReference), "watch should match after reading")
testWatch.Metadata.Name = "yyy"
m2, err := g.client.WatchCreate(testWatch)
@@ -339,45 +274,40 @@ func TestWatchApi(t *testing.T) {
}
func TestTimeTriggerApi(t *testing.T) {
testTrigger := &fission.TimeTrigger{
Metadata: fission.Metadata{
Name: "xxx",
Uid: "yyy",
testTrigger := &tpr.Timetrigger{
Metadata: api.ObjectMeta{
Name: "xxx",
Namespace: api.NamespaceDefault,
},
Cron: "0 30 * * * *",
Function: fission.Metadata{
Name: "foo",
Uid: "",
Spec: fission.TimeTriggerSpec{
Cron: "0 30 * * * *",
FunctionReference: fission.FunctionReference{
Type: fission.FunctionReferenceTypeFunctionName,
Name: "asdf",
},
},
}
_, err := g.client.TimeTriggerGet(&fission.Metadata{Name: "foo"})
assertNotFoundFails(err, "trigger")
_, err := g.client.TimeTriggerGet(&api.ObjectMeta{Name: testTrigger.Metadata.Name})
assertNotFoundFailure(err, "trigger")
m, err := g.client.TimeTriggerCreate(testTrigger)
panicIf(err)
defer g.client.TimeTriggerDelete(m)
_, err = g.client.TimeTriggerCreate(testTrigger)
assertNameReuseFails(err, "trigger")
assertNameReuseFailure(err, "trigger")
tr, err := g.client.TimeTriggerGet(m)
panicIf(err)
testTrigger.Metadata.Uid = m.Uid
assert(*testTrigger == *tr, "trigger should match after reading")
assert(testTrigger.Spec == tr.Spec, "trigger should match after reading")
testTrigger.Cron = "@hourly"
m2, err := g.client.TimeTriggerUpdate(testTrigger)
testTrigger.Spec.Cron = "@hourly"
_, err = g.client.TimeTriggerUpdate(testTrigger)
panicIf(err)
m.Uid = m2.Uid
tr, err = g.client.TimeTriggerGet(m)
panicIf(err)
testTrigger.Metadata.Uid = m.Uid
assert(*testTrigger == *tr, "trigger should match after reading")
testTrigger.Metadata.Name = "yyy"
testTrigger.Cron = "Not valid cron spec"
m, err = g.client.TimeTriggerCreate(testTrigger)
testTrigger.Spec.Cron = "Not valid cron spec"
_, err = g.client.TimeTriggerCreate(testTrigger)
assertCronSpecFails(err)
ts, err := g.client.TimeTriggerList()
@@ -388,20 +318,17 @@ func TestTimeTriggerApi(t *testing.T) {
func TestMain(m *testing.M) {
flag.Parse()
fileStore, ks, rs := getTestResourceStore()
defer os.RemoveAll(fileStore.root)
// skip test if no cluster available for testing
kubeconfig := os.Getenv("KUBECONFIG")
if len(kubeconfig) == 0 {
log.Println("Skipping test, no kubernetes cluster")
return
}
api := MakeAPI(rs)
g.client = client.MakeClient("http://localhost:8888")
go Start(8888)
ks.Delete(context.Background(), "Function", &etcdClient.DeleteOptions{Recursive: true})
ks.Delete(context.Background(), "HTTPTrigger", &etcdClient.DeleteOptions{Recursive: true})
ks.Delete(context.Background(), "TimeTrigger", &etcdClient.DeleteOptions{Recursive: true})
ks.Delete(context.Background(), "Environment", &etcdClient.DeleteOptions{Recursive: true})
ks.Delete(context.Background(), "Watch", &etcdClient.DeleteOptions{Recursive: true})
go api.Serve(8888)
time.Sleep(500 * time.Millisecond)
g.client = client.MakeClient("http://localhost:8888")
resp, err := http.Get("http://localhost:8888/")
panicIf(err)
+1 -666
View File
@@ -18,10 +18,7 @@ package client
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
@@ -73,7 +70,7 @@ func (c *Client) put(relativeUrl string, contentType string, body []byte) (*http
}
func (c *Client) url(relativeUrl string) string {
return c.Url + "/v1/" + relativeUrl
return c.Url + "/v2/" + relativeUrl
}
func (c *Client) handleResponse(resp *http.Response) ([]byte, error) {
@@ -91,665 +88,3 @@ func (c *Client) handleCreateResponse(resp *http.Response) ([]byte, error) {
body, err := ioutil.ReadAll(resp.Body)
return body, err
}
func (c *Client) FunctionCreate(f *fission.Function) (*fission.Metadata, error) {
orig := f.Code
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
defer func() { f.Code = orig }()
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("functions"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionGet(m *fission.Metadata) (*fission.Function, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var f fission.Function
err = json.Unmarshal(body, &f)
if err != nil {
return nil, err
}
dec, err := base64.StdEncoding.DecodeString(f.Code)
if err != nil {
return nil, err
}
f.Code = string(dec)
return &f, nil
}
func (c *Client) FunctionGetRaw(m *fission.Metadata) ([]byte, error) {
relativeUrl := fmt.Sprintf("functions/%v?raw=1", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("&uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return c.handleResponse(resp)
}
func (c *Client) FunctionUpdate(f *fission.Function) (*fission.Metadata, error) {
orig := f.Code
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
defer func() { f.Code = orig }()
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("functions/%v", f.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) FunctionList() ([]fission.Function, error) {
resp, err := http.Get(c.url("functions"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
funcs := make([]fission.Function, 0)
err = json.Unmarshal(body, &funcs)
if err != nil {
return nil, err
}
return funcs, nil
}
func (c *Client) HTTPTriggerCreate(t *fission.HTTPTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/http"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerGet(m *fission.Metadata) (*fission.HTTPTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fission.HTTPTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) HTTPTriggerUpdate(t *fission.HTTPTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/http/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) HTTPTriggerList() ([]fission.HTTPTrigger, error) {
resp, err := http.Get(c.url("triggers/http"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fission.HTTPTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
func (c *Client) EnvironmentCreate(env *fission.Environment) (*fission.Metadata, error) {
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentGet(m *fission.Metadata) (*fission.Environment, error) {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var env fission.Environment
err = json.Unmarshal(body, &env)
if err != nil {
return nil, err
}
return &env, nil
}
func (c *Client) EnvironmentUpdate(env *fission.Environment) (*fission.Metadata, error) {
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) EnvironmentList() ([]fission.Environment, error) {
resp, err := http.Get(c.url("environments"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
envs := make([]fission.Environment, 0)
err = json.Unmarshal(body, &envs)
if err != nil {
return nil, err
}
return envs, nil
}
func (c *Client) WatchCreate(w *fission.Watch) (*fission.Metadata, error) {
reqbody, err := json.Marshal(w)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("watches"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) WatchGet(m *fission.Metadata) (*fission.Watch, error) {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var w fission.Watch
err = json.Unmarshal(body, &w)
if err != nil {
return nil, err
}
return &w, nil
}
func (c *Client) WatchUpdate(w *fission.Watch) (*fission.Metadata, error) {
return nil, fission.MakeError(fission.ErrorNotImplmented,
"watch update not implemented")
}
func (c *Client) WatchDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) WatchList() ([]fission.Watch, error) {
resp, err := http.Get(c.url("watches"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
watches := make([]fission.Watch, 0)
err = json.Unmarshal(body, &watches)
if err != nil {
return nil, err
}
return watches, err
}
func (c *Client) TimeTriggerCreate(t *fission.TimeTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/time"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerGet(m *fission.Metadata) (*fission.TimeTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fission.TimeTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) TimeTriggerUpdate(t *fission.TimeTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/time/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) TimeTriggerList() ([]fission.TimeTrigger, error) {
resp, err := http.Get(c.url("triggers/time"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fission.TimeTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
func (c *Client) MessageQueueTriggerCreate(t *fission.MessageQueueTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/messagequeue"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerGet(m *fission.Metadata) (*fission.MessageQueueTrigger, error) {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t fission.MessageQueueTrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *fission.MessageQueueTrigger) (*fission.Metadata, error) {
reqbody, err := json.Marshal(mqTrigger)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", mqTrigger.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m fission.Metadata
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerDelete(m *fission.Metadata) error {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
if len(m.Uid) > 0 {
relativeUrl += fmt.Sprintf("?uid=%v", m.Uid)
}
err := c.delete(relativeUrl)
return err
}
func (c *Client) MessageQueueTriggerList(mqType string) ([]fission.MessageQueueTrigger, error) {
relativeUrl := "triggers/messagequeue"
if len(mqType) > 0 {
relativeUrl += fmt.Sprintf("?mqtype=%v", mqType)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]fission.MessageQueueTrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
+132
View File
@@ -0,0 +1,132 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission/tpr"
)
func (c *Client) EnvironmentCreate(env *tpr.Environment) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentGet(m *api.ObjectMeta) (*tpr.Environment, error) {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var env tpr.Environment
err = json.Unmarshal(body, &env)
if err != nil {
return nil, err
}
return &env, nil
}
func (c *Client) EnvironmentUpdate(env *tpr.Environment) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(env)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) EnvironmentDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("environments/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) EnvironmentList() ([]tpr.Environment, error) {
resp, err := http.Get(c.url("environments"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
envs := make([]tpr.Environment, 0)
err = json.Unmarshal(body, &envs)
if err != nil {
return nil, err
}
return envs, nil
}
+155
View File
@@ -0,0 +1,155 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (c *Client) FunctionCreate(f *tpr.Function) (*api.ObjectMeta, error) {
// ensure size limits
if len(f.Spec.Source.Literal) > 256*1024 {
return nil, fission.MakeError(fission.ErrorSizeLimitExceeded, "Source package literal larger than 256k")
}
if len(f.Spec.Deployment.Literal) > 256*1024 {
return nil, fission.MakeError(fission.ErrorSizeLimitExceeded, "Deployment package literal larger than 256k")
}
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("functions"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionGet(m *api.ObjectMeta) (*tpr.Function, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var f tpr.Function
err = json.Unmarshal(body, &f)
if err != nil {
return nil, err
}
return &f, nil
}
func (c *Client) FunctionGetRawDeployment(m *api.ObjectMeta) ([]byte, error) {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
relativeUrl += fmt.Sprintf("&deploymentraw=1")
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return c.handleResponse(resp)
}
func (c *Client) FunctionUpdate(f *tpr.Function) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(f)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("functions/%v", f.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) FunctionDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("functions/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) FunctionList() ([]tpr.Function, error) {
resp, err := http.Get(c.url("functions"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
funcs := make([]tpr.Function, 0)
err = json.Unmarshal(body, &funcs)
if err != nil {
return nil, err
}
return funcs, nil
}
+131
View File
@@ -0,0 +1,131 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission/tpr"
)
func (c *Client) HTTPTriggerCreate(t *tpr.Httptrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/http"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerGet(m *api.ObjectMeta) (*tpr.Httptrigger, error) {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t tpr.Httptrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) HTTPTriggerUpdate(t *tpr.Httptrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/http/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) HTTPTriggerDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/http/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) HTTPTriggerList() ([]tpr.Httptrigger, error) {
resp, err := http.Get(c.url("triggers/http"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]tpr.Httptrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
+111
View File
@@ -0,0 +1,111 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (c *Client) WatchCreate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(w)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("watches"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) WatchGet(m *api.ObjectMeta) (*tpr.Kuberneteswatchtrigger, error) {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var w tpr.Kuberneteswatchtrigger
err = json.Unmarshal(body, &w)
if err != nil {
return nil, err
}
return &w, nil
}
func (c *Client) WatchUpdate(w *tpr.Kuberneteswatchtrigger) (*api.ObjectMeta, error) {
return nil, fission.MakeError(fission.ErrorNotImplmented,
"watch update not implemented")
}
func (c *Client) WatchDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("watches/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) WatchList() ([]tpr.Kuberneteswatchtrigger, error) {
resp, err := http.Get(c.url("watches"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
watches := make([]tpr.Kuberneteswatchtrigger, 0)
err = json.Unmarshal(body, &watches)
if err != nil {
return nil, err
}
return watches, err
}
+137
View File
@@ -0,0 +1,137 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission/tpr"
)
func (c *Client) MessageQueueTriggerCreate(t *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/messagequeue"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerGet(m *api.ObjectMeta) (*tpr.Messagequeuetrigger, error) {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t tpr.Messagequeuetrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) MessageQueueTriggerUpdate(mqTrigger *tpr.Messagequeuetrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(mqTrigger)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", mqTrigger.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) MessageQueueTriggerDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/messagequeue/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) MessageQueueTriggerList(mqType string) ([]tpr.Messagequeuetrigger, error) {
relativeUrl := "triggers/messagequeue"
if len(mqType) > 0 {
// TODO remove this, replace with field selector
relativeUrl += fmt.Sprintf("?mqtype=%v", mqType)
}
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]tpr.Messagequeuetrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
+131
View File
@@ -0,0 +1,131 @@
/*
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 client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission/tpr"
)
func (c *Client) TimeTriggerCreate(t *tpr.Timetrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
resp, err := http.Post(c.url("triggers/time"), "application/json", bytes.NewReader(reqbody))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleCreateResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerGet(m *api.ObjectMeta) (*tpr.Timetrigger, error) {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
resp, err := http.Get(c.url(relativeUrl))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var t tpr.Timetrigger
err = json.Unmarshal(body, &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (c *Client) TimeTriggerUpdate(t *tpr.Timetrigger) (*api.ObjectMeta, error) {
reqbody, err := json.Marshal(t)
if err != nil {
return nil, err
}
relativeUrl := fmt.Sprintf("triggers/time/%v", t.Metadata.Name)
resp, err := c.put(relativeUrl, "application/json", reqbody)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
var m api.ObjectMeta
err = json.Unmarshal(body, &m)
if err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) TimeTriggerDelete(m *api.ObjectMeta) error {
relativeUrl := fmt.Sprintf("triggers/time/%v", m.Name)
relativeUrl += fmt.Sprintf("?namespace=%v", m.Namespace)
return c.delete(relativeUrl)
}
func (c *Client) TimeTriggerList() ([]tpr.Timetrigger, error) {
resp, err := http.Get(c.url("triggers/time"))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := c.handleResponse(resp)
if err != nil {
return nil, err
}
triggers := make([]tpr.Timetrigger, 0)
err = json.Unmarshal(body, &triggers)
if err != nil {
return nil, err
}
return triggers, nil
}
@@ -17,16 +17,27 @@ limitations under the License.
package controller
import (
"encoding/json"
"log"
"github.com/fission/fission/tpr"
)
type JsonSerializer struct {
}
func Start(port int) {
fc, kc, err := tpr.MakeFissionClient()
if err != nil {
log.Fatalf("Failed to connect to K8s API: %v", err)
}
func (j JsonSerializer) serialize(r resource) ([]byte, error) {
return json.Marshal(r)
}
err = tpr.EnsureFissionTPRs(kc)
if err != nil {
log.Fatalf("Failed to create fission TPRs: %v", err)
}
func (j JsonSerializer) deserialize(buf []byte, r resource) error {
return json.Unmarshal(buf, r)
fc.WaitForTPRs()
api, err := MakeAPI()
if err != nil {
log.Fatalf("Failed to start controller: %v", err)
}
api.Serve(port)
}
+45 -46
View File
@@ -23,133 +23,132 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
envs, err := api.EnvironmentStore.List()
func (a *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) {
envs, err := a.fissionClient.Environments(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(envs)
resp, err := json.Marshal(envs.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
func (a *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var env fission.Environment
var env tpr.Environment
err = json.Unmarshal(body, &env)
if err != nil {
log.Printf("Failed to unmarshal request body: [%v]", body)
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.EnvironmentStore.Create(&env)
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Create(&env)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(enew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
var m fission.Metadata
func (a *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
m.Name = vars["environment"]
m.Uid = r.FormValue("uid") // empty if uid is absent
name := vars["environment"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
env, err := api.EnvironmentStore.Get(&m)
env, err := a.fissionClient.Environments(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(env)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
func (a *API) EnvironmentApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["environment"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var env fission.Environment
var env tpr.Environment
err = json.Unmarshal(body, &env)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
if name != env.Metadata.Name {
err = fission.MakeError(fission.ErrorInvalidArgument, "Environment name doesn't match URL")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.EnvironmentStore.Update(&env)
enew, err := a.fissionClient.Environments(env.Metadata.Namespace).Update(&env)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(enew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) EnvironmentApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var m fission.Metadata
m.Name = vars["environment"]
m.Uid = r.FormValue("uid") // empty if uid is absent
if len(m.Uid) == 0 {
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
name := vars["environment"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
err := api.EnvironmentStore.Delete(m)
err := a.fissionClient.Environments(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
-79
View File
@@ -1,79 +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 controller
import (
"github.com/satori/go.uuid"
"github.com/fission/fission"
)
type EnvironmentStore struct {
ResourceStore
}
func (es *EnvironmentStore) Create(e *fission.Environment) (string, error) {
e.Metadata.Uid = uuid.NewV4().String()
return e.Metadata.Uid, es.ResourceStore.create(e)
}
func (es *EnvironmentStore) Get(m *fission.Metadata) (*fission.Environment, error) {
var e fission.Environment
err := es.ResourceStore.read(m.Name, &e)
if err != nil {
return nil, err
}
return &e, nil
}
func (es *EnvironmentStore) Update(e *fission.Environment) (string, error) {
e.Metadata.Uid = uuid.NewV4().String()
return e.Metadata.Uid, es.ResourceStore.update(e)
}
func (es *EnvironmentStore) Delete(m fission.Metadata) error {
typeName, err := getTypeName(fission.Environment{})
if err != nil {
return err
}
return es.ResourceStore.delete(typeName, m.Name)
}
func (es *EnvironmentStore) List() ([]fission.Environment, error) {
typeName, err := getTypeName(fission.Environment{})
if err != nil {
return nil, err
}
bufs, err := es.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
envs := make([]fission.Environment, 0, len(bufs))
js := JsonSerializer{}
for _, buf := range bufs {
var e fission.Environment
err = js.deserialize([]byte(buf), &e)
if err != nil {
return nil, err
}
envs = append(envs, e)
}
return envs, nil
}
-134
View File
@@ -1,134 +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 controller
import (
"io/ioutil"
"os"
"path"
log "github.com/sirupsen/logrus"
)
type requestType int
const (
READ requestType = iota
WRITE
DELETE
)
type (
FileStore struct {
root string // abs path of root of filestore
requestChannel chan fileStoreRequest
}
fileStoreRequest struct {
requestType
fileName string // relative path
fileContents []byte
responseChannel chan fileStoreResponse
}
fileStoreResponse struct {
error
fileContents []byte
}
)
func MakeFileStore(path string) *FileStore {
// create directory if necessary
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(path, os.ModeDir|0700)
if err != nil {
log.Printf("Failed to create directory %v", path)
return nil
}
log.Printf("Created directory %v", path)
} else {
log.Printf("Unknown error initializing filestore: %v", err)
return nil
}
}
fileStore := &FileStore{
root: path,
requestChannel: make(chan fileStoreRequest),
}
go fileStore.fileStoreService()
return fileStore
}
func (fs *FileStore) fileStoreService() {
for {
req := <-fs.requestChannel
response := &fileStoreResponse{}
log.WithFields(log.Fields{"file": req.fileName, "type": req.requestType}).Debug("fileStore request")
switch req.requestType {
case READ:
response.fileContents, response.error = ioutil.ReadFile(path.Join(fs.root, req.fileName))
case WRITE:
response.error = ioutil.WriteFile(path.Join(fs.root, req.fileName), req.fileContents, 0600)
case DELETE:
response.error = os.Remove(path.Join(fs.root, req.fileName))
if os.IsNotExist(response.error) {
response.error = nil
}
default:
log.Panic("bad request")
}
req.responseChannel <- *response
}
}
func (fs *FileStore) read(fileName string) ([]byte, error) {
req := fileStoreRequest{
requestType: READ,
fileName: fileName,
responseChannel: make(chan fileStoreResponse),
}
fs.requestChannel <- req
response := <-req.responseChannel
return response.fileContents, response.error
}
func (fs *FileStore) write(fileName string, contents []byte) error {
req := fileStoreRequest{
requestType: WRITE,
fileName: fileName,
fileContents: contents,
responseChannel: make(chan fileStoreResponse),
}
fs.requestChannel <- req
response := <-req.responseChannel
return response.error
}
func (fs *FileStore) delete(fileName string) error {
req := fileStoreRequest{
requestType: DELETE,
fileName: fileName,
responseChannel: make(chan fileStoreResponse),
}
fs.requestChannel <- req
response := <-req.responseChannel
return response.error
}
-63
View File
@@ -1,63 +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 controller
import (
"io/ioutil"
"log"
"os"
"testing"
)
func TestFileStore(t *testing.T) {
// tmp dir
dir, err := ioutil.TempDir("", "testFileStore")
if err != nil {
t.Fatalf("error creating temp dir: %v", err)
}
defer os.RemoveAll(dir)
log.Printf("temp dir at %v", dir)
// file store
fs := MakeFileStore(dir)
_, err = fs.read("nonexistent")
if err == nil {
t.Fatalf("expected an error")
}
path := "fileStoreTest"
contents := []byte("bar")
err = fs.write(path, contents)
if err != nil {
t.Fatalf("error: %v", err)
}
observedContents, err := fs.read(path)
if err != nil {
t.Fatalf("error: %v", err)
}
if string(observedContents) != string(contents) {
t.Fatalf("contents don't match")
}
err = fs.delete(path)
if err != nil {
t.Fatalf("error: %v", err)
}
}
+62 -67
View File
@@ -17,7 +17,6 @@ limitations under the License.
package controller
import (
"encoding/base64"
"encoding/json"
"io/ioutil"
"net/http"
@@ -26,165 +25,161 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
funcs, err := api.FunctionStore.List()
func (a *API) FunctionApiList(w http.ResponseWriter, r *http.Request) {
funcs, err := a.fissionClient.Functions(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(funcs)
resp, err := json.Marshal(funcs.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
func (a *API) FunctionApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var f fission.Function
var f tpr.Function
err = json.Unmarshal(body, &f)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
dec, err := base64.StdEncoding.DecodeString(f.Code)
if err != nil {
api.respondWithError(w, err)
// Ensure size limits
if len(f.Spec.Source.Literal) > 256*1024 {
err := fission.MakeError(fission.ErrorInvalidArgument, "Source package literal larger than 256K")
a.respondWithError(w, err)
return
}
f.Code = string(dec)
uid, err := api.FunctionStore.Create(&f)
if err != nil {
api.respondWithError(w, err)
if len(f.Spec.Deployment.Literal) > 256*1024 {
err := fission.MakeError(fission.ErrorInvalidArgument, "Deployment package literal larger than 256K")
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: f.Name, Uid: uid}
resp, err := json.Marshal(m)
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Create(&f)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
var m fission.Metadata
func (a *API) FunctionApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
m.Name = vars["function"]
m.Uid = r.FormValue("uid") // empty if uid is absent
raw := r.FormValue("raw") // just the code
name := vars["function"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
raw := r.FormValue("deploymentraw") // just the deployment pkg
f, err := api.FunctionStore.Get(&m)
f, err := a.fissionClient.Functions(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var resp []byte
if raw != "" {
resp = []byte(f.Code)
resp = []byte(f.Spec.Deployment.Literal)
} else {
f.Code = base64.StdEncoding.EncodeToString([]byte(f.Code))
resp, err = json.Marshal(f)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
func (a *API) FunctionApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
funcName := vars["function"]
name := vars["function"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var f fission.Function
var f tpr.Function
err = json.Unmarshal(body, &f)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
if funcName != f.Metadata.Name {
if name != f.Metadata.Name {
err = fission.MakeError(fission.ErrorInvalidArgument, "Function name doesn't match URL")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
dec, err := base64.StdEncoding.DecodeString(f.Code)
fnew, err := a.fissionClient.Functions(f.Metadata.Namespace).Update(&f)
if err != nil {
api.respondWithError(w, err)
return
}
f.Code = string(dec)
uid, err := api.FunctionStore.Update(&f)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: f.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(fnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) FunctionApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var m fission.Metadata
m.Name = vars["function"]
m.Uid = r.FormValue("uid") // empty if uid is absent
if len(m.Uid) == 0 {
log.WithFields(log.Fields{"function": m.Name}).Info("Deleting all versions")
name := vars["function"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
err := api.FunctionStore.Delete(m)
err := a.fissionClient.Functions(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
// FunctionLogsApiPost establishes a proxy server to log database, and redirect
// query command send from client to database then proxy back the db response.
func (api *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
func (a *API) FunctionLogsApiPost(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
// get dbType from url
dbType := vars["dbType"]
// find correspond db http url
dbCnf := api.getLogDBConfig(dbType)
dbCnf := a.getLogDBConfig(dbType)
svcUrl, err := url.Parse(dbCnf.httpURL)
if err != nil {
-156
View File
@@ -1,156 +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 controller
import (
log "github.com/sirupsen/logrus"
"github.com/fission/fission"
)
type FunctionStore struct {
ResourceStore
}
func (fs *FunctionStore) Create(f *fission.Function) (string, error) {
code := []byte(f.Code)
_, uid, err := fs.ResourceStore.writeFile(f.Key(), code)
if err != nil {
return "", err
}
f.Metadata.Uid = uid
f.Code = ""
err = fs.ResourceStore.create(f)
if err != nil {
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore errors
return "", err
}
return f.Metadata.Uid, nil
}
func (fs *FunctionStore) Get(m *fission.Metadata) (*fission.Function, error) {
var f fission.Function
err := fs.ResourceStore.read(m.Name, &f)
if err != nil {
return nil, err
}
var code []byte
if len(m.Uid) > 0 {
log.WithFields(log.Fields{"Uid": m.Uid}).Info("fetching by uid")
code, err = fs.ResourceStore.readFile(m.Name, &m.Uid)
f.Metadata = *m
} else {
code, err = fs.ResourceStore.readFile(m.Name, nil)
}
if err != nil {
return nil, err
}
f.Code = string(code)
return &f, nil
}
func (fs *FunctionStore) Update(f *fission.Function) (string, error) {
code := []byte(f.Code)
_, uid, err := fs.ResourceStore.writeFile(f.Key(), code)
if err != nil {
return "", err
}
var fnew fission.Function
err = fs.ResourceStore.read(f.Metadata.Name, &fnew)
if err != nil {
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore err
return "", err
}
fnew.Metadata.Uid = uid
fnew.Environment = f.Environment
err = fs.ResourceStore.update(fnew)
if err != nil {
fs.ResourceStore.deleteFile(f.Key(), uid) // ignore err
return "", err
}
return uid, err
}
func (fs *FunctionStore) Delete(m fission.Metadata) error {
if len(m.Uid) == 0 {
err := fs.ResourceStore.deleteAllFiles(m.Name)
if err != nil {
return err
}
} else {
err := fs.ResourceStore.deleteFile(m.Name, m.Uid)
if err != nil {
return err
}
}
typeName, err := getTypeName(fission.Function{})
if err != nil {
return err
}
bufs, err := fs.ResourceStore.getAll("file/" + m.Name)
if err != nil {
return err
}
if len(bufs) == 0 {
return fs.ResourceStore.delete(typeName, m.Name)
}
fnew, err := fs.Get(&fission.Metadata{Name: m.Name})
if err != nil {
return err
}
latestUid := bufs[len(bufs)-1] // function always tracks the latest version of code
if latestUid == fnew.Uid {
return nil
}
fnew.Uid = latestUid
return fs.ResourceStore.update(fnew)
}
func (fs *FunctionStore) List() ([]fission.Function, error) {
typeName, err := getTypeName(fission.Function{})
if err != nil {
return nil, err
}
bufs, err := fs.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
js := JsonSerializer{}
functions := make([]fission.Function, 0, len(bufs))
for _, buf := range bufs {
var f fission.Function
err = js.deserialize([]byte(buf), &f)
if err != nil {
return nil, err
}
functions = append(functions, f)
}
return functions, nil
}
+79 -66
View File
@@ -18,151 +18,164 @@ package controller
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := api.HTTPTriggerStore.List()
func (a *API) HTTPTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers)
resp, err := json.Marshal(triggers.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
func (a *API) checkHttpTriggerDuplicates(t *tpr.Httptrigger) error {
triggers, err := a.fissionClient.Httptriggers(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
return
return err
}
var t fission.HTTPTrigger
err = json.Unmarshal(body, &t)
if err != nil {
api.respondWithError(w, err)
return
}
triggers, err := api.HTTPTriggerStore.List()
if err != nil {
api.respondWithError(w, err)
return
}
for _, url := range triggers {
if url.UrlPattern == t.UrlPattern && url.Method == t.Method {
err = fission.MakeError(fission.ErrorNameExists,
"HTTPTrigger with same URL & method already exists")
api.respondWithError(w, err)
return
for _, ht := range triggers.Items {
if ht.Spec.RelativeURL == t.Spec.RelativeURL && ht.Spec.Method == t.Spec.Method {
return fission.MakeError(fission.ErrorNameExists,
fmt.Sprintf("HTTPTrigger with same URL & method already exists (%v)",
ht.Metadata.Name))
}
}
return nil
}
uid, err := api.HTTPTriggerStore.Create(&t)
func (a *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
var t tpr.Httptrigger
err = json.Unmarshal(body, &t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
// Ensure we don't have a duplicate HTTP route defined (same URL and method)
err = a.checkHttpTriggerDuplicates(&t)
if err != nil {
a.respondWithError(w, err)
return
}
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Create(&t)
if err != nil {
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
var m fission.Metadata
func (a *API) HTTPTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
m.Name = vars["httpTrigger"]
m.Uid = r.FormValue("uid") // empty if uid is absent
name := vars["httpTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
t, err := api.HTTPTriggerStore.Get(&m)
t, err := a.fissionClient.Httptriggers(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
func (a *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["httpTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var t fission.HTTPTrigger
var t tpr.Httptrigger
err = json.Unmarshal(body, &t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
if name != t.Metadata.Name {
err = fission.MakeError(fission.ErrorInvalidArgument, "HTTPTrigger name doesn't match URL")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.HTTPTriggerStore.Update(&t)
err = a.checkHttpTriggerDuplicates(&t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
tnew, err := a.fissionClient.Httptriggers(t.Metadata.Namespace).Update(&t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
a.respondWithError(w, err)
return
}
a.respondWithSuccess(w, resp)
}
func (api *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) HTTPTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var m fission.Metadata
m.Name = vars["httpTrigger"]
m.Uid = r.FormValue("uid") // empty if uid is absent
if len(m.Uid) == 0 {
log.WithFields(log.Fields{"httpTrigger": m.Name}).Info("Deleting all versions")
name := vars["httpTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
err := api.HTTPTriggerStore.Delete(m)
err := a.fissionClient.Httptriggers(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
-79
View File
@@ -1,79 +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 controller
import (
"github.com/satori/go.uuid"
"github.com/fission/fission"
)
type HTTPTriggerStore struct {
ResourceStore
}
func (hts *HTTPTriggerStore) Create(ht *fission.HTTPTrigger) (string, error) {
ht.Metadata.Uid = uuid.NewV4().String()
return ht.Metadata.Uid, hts.ResourceStore.create(ht)
}
func (hts *HTTPTriggerStore) Get(m *fission.Metadata) (*fission.HTTPTrigger, error) {
var ht fission.HTTPTrigger
err := hts.ResourceStore.read(m.Name, &ht)
if err != nil {
return nil, err
}
return &ht, nil
}
func (hts *HTTPTriggerStore) Update(ht *fission.HTTPTrigger) (string, error) {
ht.Metadata.Uid = uuid.NewV4().String()
return ht.Metadata.Uid, hts.ResourceStore.update(ht)
}
func (hts *HTTPTriggerStore) Delete(m fission.Metadata) error {
typeName, err := getTypeName(fission.HTTPTrigger{})
if err != nil {
return err
}
return hts.ResourceStore.delete(typeName, m.Name)
}
func (hts *HTTPTriggerStore) List() ([]fission.HTTPTrigger, error) {
typeName, err := getTypeName(fission.HTTPTrigger{})
if err != nil {
return nil, err
}
bufs, err := hts.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
triggers := make([]fission.HTTPTrigger, 0, len(bufs))
js := JsonSerializer{}
for _, buf := range bufs {
var ht fission.HTTPTrigger
err = js.deserialize([]byte(buf), &ht)
if err != nil {
return nil, err
}
triggers = append(triggers, ht)
}
return triggers, nil
}
+48 -67
View File
@@ -22,145 +22,126 @@ import (
"net/http"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
mqType := r.FormValue("mqtype")
triggers, err := api.MessageQueueTriggerStore.List(mqType)
func (a *API) MessageQueueTriggerApiList(w http.ResponseWriter, r *http.Request) {
//mqType := r.FormValue("mqtype") // ignored for now
triggers, err := a.fissionClient.Messagequeuetriggers(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers)
resp, err := json.Marshal(triggers.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) MessageQueueApiCreate(w http.ResponseWriter, r *http.Request) {
func (a *API) MessageQueueTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var mqTrigger fission.MessageQueueTrigger
var mqTrigger tpr.Messagequeuetrigger
err = json.Unmarshal(body, &mqTrigger)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
// trigger name must not conflict with any other trigger
// even they are different message queue type
triggers, err := api.MessageQueueTriggerStore.List("")
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Create(&mqTrigger)
if err != nil {
api.respondWithError(w, err)
return
}
for _, trigger := range triggers {
if trigger.Name == mqTrigger.Name {
err = fission.MakeError(fission.ErrorNameExists,
"Message queue trigger with same name already exists")
api.respondWithError(w, err)
return
}
}
// save trigger info
uid, err := api.MessageQueueTriggerStore.Create(&mqTrigger)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
mqTriggerMeta := fission.Metadata{Name: mqTrigger.Metadata.Name, Uid: uid}
resp, err := json.Marshal(mqTriggerMeta)
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) MessageQueueApiGet(w http.ResponseWriter, r *http.Request) {
func (a *API) MessageQueueTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
mqTriggerMeta := fission.Metadata{
Name: vars["mqTrigger"],
Uid: r.FormValue("uid"), // empty if uid is absent
name := vars["mqTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
mqTrigger, err := api.MessageQueueTriggerStore.Get(&mqTriggerMeta)
mqTrigger, err := a.fissionClient.Messagequeuetriggers(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(mqTrigger)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) MessageQueueApiUpdate(w http.ResponseWriter, r *http.Request) {
func (a *API) MessageQueueTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
mqtName := vars["mqTrigger"]
name := vars["mqTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var mqTrigger fission.MessageQueueTrigger
var mqTrigger tpr.Messagequeuetrigger
err = json.Unmarshal(body, &mqTrigger)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
if mqtName != mqTrigger.Metadata.Name {
if name != mqTrigger.Metadata.Name {
err = fission.MakeError(fission.ErrorInvalidArgument, "Message queue trigger name doesn't match URL")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.MessageQueueTriggerStore.Update(&mqTrigger)
tnew, err := a.fissionClient.Messagequeuetriggers(mqTrigger.Metadata.Namespace).Update(&mqTrigger)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
mqTriggerMeta := fission.Metadata{Name: mqTrigger.Metadata.Name, Uid: uid}
resp, err := json.Marshal(mqTriggerMeta)
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) MessageQueueApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) MessageQueueTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
mqTriggerMeta := fission.Metadata{
Name: vars["mqTrigger"],
Uid: r.FormValue("uid"), // empty if uid is absent
name := vars["mqTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
if len(mqTriggerMeta.Uid) == 0 {
log.WithFields(log.Fields{"mqTrigger": mqTriggerMeta.Name}).Info("Deleting all versions")
}
err := api.MessageQueueTriggerStore.Delete(mqTriggerMeta)
err := a.fissionClient.Messagequeuetriggers(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
-80
View File
@@ -1,80 +0,0 @@
/*
CopyrigmqTrigger 2017 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
mqTriggertp://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 controller
import (
"github.com/fission/fission"
uuid "github.com/satori/go.uuid"
)
type MessageQueueTriggerStore struct {
ResourceStore
}
func (mqs *MessageQueueTriggerStore) Create(mqTrigger *fission.MessageQueueTrigger) (string, error) {
mqTrigger.Metadata.Uid = uuid.NewV4().String()
return mqTrigger.Metadata.Uid, mqs.ResourceStore.create(mqTrigger)
}
func (mqs *MessageQueueTriggerStore) Get(m *fission.Metadata) (*fission.MessageQueueTrigger, error) {
var mqTrigger fission.MessageQueueTrigger
err := mqs.ResourceStore.read(m.Name, &mqTrigger)
if err != nil {
return nil, err
}
return &mqTrigger, nil
}
func (mqs *MessageQueueTriggerStore) Update(mqTrigger *fission.MessageQueueTrigger) (string, error) {
mqTrigger.Metadata.Uid = uuid.NewV4().String()
return mqTrigger.Metadata.Uid, mqs.ResourceStore.update(mqTrigger)
}
func (mqs *MessageQueueTriggerStore) Delete(m fission.Metadata) error {
typeName, err := getTypeName(fission.MessageQueueTrigger{})
if err != nil {
return err
}
return mqs.ResourceStore.delete(typeName, m.Name)
}
func (mqs *MessageQueueTriggerStore) List(mqType string) ([]fission.MessageQueueTrigger, error) {
typeName, err := getTypeName(fission.MessageQueueTrigger{})
if err != nil {
return nil, err
}
bufs, err := mqs.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
triggers := make([]fission.MessageQueueTrigger, 0, len(bufs))
js := JsonSerializer{}
for _, buf := range bufs {
var mqTrigger fission.MessageQueueTrigger
err = js.deserialize([]byte(buf), &mqTrigger)
if err != nil {
return nil, err
}
if len(mqType) > 0 && mqType != mqTrigger.MessageQueueType {
continue
}
triggers = append(triggers, mqTrigger)
}
return triggers, nil
}
-287
View File
@@ -1,287 +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 controller
import (
"errors"
"fmt"
"reflect"
"strings"
"time"
"github.com/coreos/etcd/client"
"github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"github.com/fission/fission"
)
type (
ResourceStore struct {
*FileStore
client.KeysAPI
serializer
}
)
func MakeResourceStore(fs *FileStore, etcdUrls []string) (*ResourceStore, error) {
ks, err := getEtcdKeyAPI(etcdUrls)
if err != nil {
return nil, err
}
s := JsonSerializer{}
return &ResourceStore{FileStore: fs, KeysAPI: ks, serializer: s}, nil
}
func getEtcdKeyAPI(etcdUrls []string) (client.KeysAPI, error) {
cfg := client.Config{
Endpoints: etcdUrls,
Transport: client.DefaultTransport,
// set timeout per request to fail fast when the target endpoint is unavailable
HeaderTimeoutPerRequest: time.Second,
}
c, err := client.New(cfg)
if err != nil {
log.Printf("failed to connect to etcd: %v", err)
return nil, err
}
return client.NewKeysAPI(c), nil
}
func getTypeName(r resource) (string, error) {
typ := reflect.TypeOf(r)
if typ.Kind().String() == "ptr" {
typ = typ.Elem()
}
typName := typ.Name()
if len(typName) == 0 {
return "", errors.New("Failed to get type")
}
return typName, nil
}
func getKey(r resource) (string, error) {
typName, err := getTypeName(r)
if err != nil {
return "", err
}
rkey := r.Key()
return (typName + "/" + rkey), nil
}
func (rs *ResourceStore) create(r resource) error {
key, err := getKey(r)
if err != nil {
return err
}
serialized, err := rs.serializer.serialize(r)
if err != nil {
return err
}
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
&client.SetOptions{PrevExist: client.PrevNoExist})
return handleEtcdErrorForResource(err, r)
}
func (rs *ResourceStore) read(rkey string, res resource) error {
typName, err := getTypeName(res)
if err != nil {
return err
}
key := typName + "/" + rkey
resp, err := rs.KeysAPI.Get(context.Background(), key, nil)
if err != nil {
return handleEtcdError(err, typName, rkey)
}
return rs.serializer.deserialize([]byte(resp.Node.Value), res)
}
func (rs *ResourceStore) update(r resource) error {
key, err := getKey(r)
if err != nil {
return err
}
serialized, err := rs.serializer.serialize(r)
if err != nil {
return err
}
_, err = rs.KeysAPI.Set(context.Background(), key, string(serialized),
&client.SetOptions{PrevExist: client.PrevExist})
return handleEtcdErrorForResource(err, r)
}
func (rs *ResourceStore) delete(typename, rkey string) error {
key := typename + "/" + rkey
_, err := rs.KeysAPI.Delete(context.Background(), key, nil) // ignore response
return handleEtcdError(err, typename, rkey)
}
// getAll finds all entries under key. If none or found or key
// doesn't exist, returns an empty slice.
func (rs *ResourceStore) getAll(key string) ([]string, error) {
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Recursive: true, Sort: true})
if err != nil {
if client.IsKeyNotFound(err) {
return []string{}, nil
}
return nil, handleEtcdError(err, "", key)
}
res := make([]string, 0, len(resp.Node.Nodes))
for _, n := range resp.Node.Nodes {
res = append(res, n.Value)
}
return res, nil
}
func (rs *ResourceStore) writeFile(parentKey string, contents []byte) (string, string, error) {
uid := uuid.NewV4().String()
err := rs.FileStore.write(uid, contents)
if err != nil {
return "", "", err
}
parentKey = "file/" + parentKey
resp, err := rs.KeysAPI.CreateInOrder(context.Background(), parentKey, uid, nil)
if err != nil {
_ = rs.FileStore.delete(uid)
return "", "", handleEtcdError(err, "file", parentKey)
}
return resp.Node.Key, uid, nil
}
func (rs *ResourceStore) readFile(key string, uid *string) ([]byte, error) {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return nil, handleEtcdError(err, "file", key)
}
if uid == nil {
// get latest
n := resp.Node.Nodes
uid = &n[len(n)-1].Value
} else {
// validate uid is in the list
found := false
for _, u := range resp.Node.Nodes {
if *uid == u.Value {
found = true
break
}
}
if !found {
return nil, errors.New("Invalid UID " + *uid)
}
}
contents, err := rs.FileStore.read(*uid)
return contents, err
}
func (rs *ResourceStore) deleteFile(key string, uid string) error {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return handleEtcdError(err, "file", key)
}
var node *client.Node
for _, u := range resp.Node.Nodes {
if u.Value == uid {
node = u
}
}
if node == nil {
log.WithFields(log.Fields{"key": key, "uid": uid}).Error("unreferenced file")
return errors.New("won't delete unreferenced file")
}
err = rs.FileStore.delete(node.Value)
if err != nil {
return err
}
_, err = rs.KeysAPI.Delete(context.Background(), node.Key, nil)
if err != nil {
return handleEtcdError(err, "", node.Key)
}
if len(resp.Node.Nodes) == 1 {
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
return handleEtcdError(err, "file", key)
}
return nil
}
func (rs *ResourceStore) deleteAllFiles(key string) error {
key = "file/" + key
resp, err := rs.KeysAPI.Get(context.Background(), key, &client.GetOptions{Sort: true})
if err != nil {
return handleEtcdError(err, "file", key)
}
for _, u := range resp.Node.Nodes {
err = rs.FileStore.delete(u.Value)
if err != nil {
return err
}
_, err = rs.KeysAPI.Delete(context.Background(), u.Key, nil)
if err != nil {
return handleEtcdError(err, "", u.Key)
}
}
_, err = rs.KeysAPI.Delete(context.Background(), key, &client.DeleteOptions{Dir: true})
return handleEtcdError(err, "file", key)
}
func handleEtcdErrorForResource(e error, r resource) error {
resourceType, _ := getTypeName(r)
return handleEtcdError(e, resourceType, r.Key())
}
func handleEtcdError(e error, resourceType string, resourceKey string) error {
ee, ok := e.(client.Error)
if !ok {
return e
}
code := fission.ErrorInternal
msg := ee.Error()
if len(resourceType) > 0 {
resourceType = strings.ToLower(resourceType) + " "
}
//TODO: handle any other etcd error codes we care about
switch ee.Code {
case client.ErrorCodeNodeExist:
code = fission.ErrorNameExists
msg = fmt.Sprintf("%s'%s' already exists", resourceType, resourceKey)
case client.ErrorCodeKeyNotFound:
code = fission.ErrorNotFound
msg = fmt.Sprintf("%s'%s' does not exist", resourceType, resourceKey)
}
return fission.MakeError(code, msg)
}
-152
View File
@@ -1,152 +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 controller
import (
"io/ioutil"
"log"
"os"
"testing"
"github.com/coreos/etcd/client"
"golang.org/x/net/context"
)
type TestResource struct {
A string
B int
}
func (tr TestResource) Key() string {
return tr.A
}
func panicIf(err error) {
if err != nil {
log.Panicf("err: %v", err)
}
}
func assert(b bool, msg string) {
if !b {
log.Panic("assertion failed: " + msg)
}
}
func getTestResourceStore() (*FileStore, client.KeysAPI, *ResourceStore) {
// make a tmp dir
dir, err := ioutil.TempDir("", "testFileStore")
panicIf(err)
fs := MakeFileStore(dir)
rs, err := MakeResourceStore(fs, []string{"http://localhost:2379"})
panicIf(err)
return fs, rs.KeysAPI, rs
}
func TestResourceStore(t *testing.T) {
fs, ks, rs := getTestResourceStore()
defer os.RemoveAll(fs.root)
s := JsonSerializer{}
tr := TestResource{A: "hello", B: 1}
// Delete the key first, in case of a panic'd previous test run; ignore errors
_ = rs.delete("TestResource", tr.Key())
ks.Delete(context.Background(), "/TestResource", &client.DeleteOptions{Dir: true})
// Verify getAll for empty db
trs, err := rs.getAll("TestResource")
panicIf(err)
if len(trs) != 0 {
log.Fatalf("Expected zero length slice, got %v", trs)
}
// Create
err = rs.create(tr)
panicIf(err)
defer ks.Delete(context.Background(), "/TestResource", &client.DeleteOptions{Dir: true})
defer rs.delete("TestResource", tr.Key())
// Etcd key /TestResource/hello should exist
_, err = ks.Get(context.Background(), "TestResource/hello", nil)
panicIf(err)
// Read
tr1 := TestResource{}
err = rs.read(tr.Key(), &tr1)
panicIf(err)
assert(tr1 == tr, "retrieved value must equal created value")
// Update and Read
tr.B += 1
err = rs.update(tr)
panicIf(err)
err = rs.read(tr.Key(), &tr1)
panicIf(err)
assert(tr1 == tr, "retrieved value must equal updated value")
// Get list
results, err := rs.getAll("TestResource")
panicIf(err)
res := make([]TestResource, 0, 0)
for _, r := range results {
tmp := TestResource{}
err = s.deserialize([]byte(r), &tmp)
panicIf(err)
res = append(res, tmp)
}
assert(res[0] == tr, "value from retrieved list must equal updated value")
// file tests
fileKey := "ResourceStoreTest"
fileContents1 := []byte("hello")
fileContents2 := []byte("world")
key, uid1, err := rs.writeFile(fileKey, fileContents1)
panicIf(err)
defer rs.deleteFile(fileKey, uid1)
log.Printf("key = %v, uid = %v", key, uid1)
// read latest
contents, err := rs.readFile(fileKey, nil)
panicIf(err)
assert(string(contents) == string(fileContents1), "retrieved file contents must match written value")
// update-- same key new contents
_, uid2, err := rs.writeFile(fileKey, fileContents2)
panicIf(err)
defer rs.deleteFile(fileKey, uid2)
// read latest
contents, err = rs.readFile(fileKey, nil)
panicIf(err)
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
// read by uid
// 1
contents, err = rs.readFile(fileKey, &uid1)
panicIf(err)
assert(string(contents) == string(fileContents1), "retrieved file contents must match updated value")
// 2
contents, err = rs.readFile(fileKey, &uid2)
panicIf(err)
assert(string(contents) == string(fileContents2), "retrieved file contents must match updated value")
}
+50 -65
View File
@@ -23,161 +23,146 @@ import (
"github.com/gorilla/mux"
"github.com/robfig/cron"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := api.TimeTriggerStore.List()
func (a *API) TimeTriggerApiList(w http.ResponseWriter, r *http.Request) {
triggers, err := a.fissionClient.Timetriggers(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(triggers)
resp, err := json.Marshal(triggers.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
func (a *API) TimeTriggerApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var t fission.TimeTrigger
var t tpr.Timetrigger
err = json.Unmarshal(body, &t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
triggers, err := api.TimeTriggerStore.List()
if err != nil {
api.respondWithError(w, err)
return
}
for _, trigger := range triggers {
if trigger.Name == t.Name {
err = fission.MakeError(fission.ErrorNameExists,
"TimeTrigger with same name already exists")
api.respondWithError(w, err)
return
}
}
_, err = cron.Parse(t.Cron)
// validate
_, err = cron.Parse(t.Spec.Cron)
if err != nil {
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.TimeTriggerStore.Create(&t)
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Create(&t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
var m fission.Metadata
func (a *API) TimeTriggerApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
m.Name = vars["timeTrigger"]
m.Uid = r.FormValue("uid") // empty if uid is absent
name := vars["timeTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
t, err := api.TimeTriggerStore.Get(&m)
t, err := a.fissionClient.Timetriggers(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
func (a *API) TimeTriggerApiUpdate(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["timeTrigger"]
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var t fission.TimeTrigger
var t tpr.Timetrigger
err = json.Unmarshal(body, &t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
if name != t.Metadata.Name {
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger name doesn't match URL")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
_, err = cron.Parse(t.Cron)
_, err = cron.Parse(t.Spec.Cron)
if err != nil {
err = fission.MakeError(fission.ErrorInvalidArgument, "TimeTrigger cron spec is not valid")
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
uid, err := api.TimeTriggerStore.Update(&t)
tnew, err := a.fissionClient.Timetriggers(t.Metadata.Namespace).Update(&t)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(tnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) TimeTriggerApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var m fission.Metadata
m.Name = vars["timeTrigger"]
m.Uid = r.FormValue("uid") // empty if uid is absent
if len(m.Uid) == 0 {
log.WithFields(log.Fields{"timeTrigger": m.Name}).Info("Deleting all versions")
name := vars["timeTrigger"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
err := api.TimeTriggerStore.Delete(m)
err := a.fissionClient.Timetriggers(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
-79
View File
@@ -1,79 +0,0 @@
/*
Copyrigtt 2017 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
tttp://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 controller
import (
"github.com/satori/go.uuid"
"github.com/fission/fission"
)
type TimeTriggerStore struct {
ResourceStore
}
func (tts *TimeTriggerStore) Create(tt *fission.TimeTrigger) (string, error) {
tt.Metadata.Uid = uuid.NewV4().String()
return tt.Metadata.Uid, tts.ResourceStore.create(tt)
}
func (tts *TimeTriggerStore) Get(m *fission.Metadata) (*fission.TimeTrigger, error) {
var tt fission.TimeTrigger
err := tts.ResourceStore.read(m.Name, &tt)
if err != nil {
return nil, err
}
return &tt, nil
}
func (tts *TimeTriggerStore) Update(tt *fission.TimeTrigger) (string, error) {
tt.Metadata.Uid = uuid.NewV4().String()
return tt.Metadata.Uid, tts.ResourceStore.update(tt)
}
func (tts *TimeTriggerStore) Delete(m fission.Metadata) error {
typeName, err := getTypeName(fission.TimeTrigger{})
if err != nil {
return err
}
return tts.ResourceStore.delete(typeName, m.Name)
}
func (tts *TimeTriggerStore) List() ([]fission.TimeTrigger, error) {
typeName, err := getTypeName(fission.TimeTrigger{})
if err != nil {
return nil, err
}
bufs, err := tts.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
triggers := make([]fission.TimeTrigger, 0, len(bufs))
js := JsonSerializer{}
for _, buf := range bufs {
var tt fission.TimeTrigger
err = js.deserialize([]byte(buf), &tt)
if err != nil {
return nil, err
}
triggers = append(triggers, tt)
}
return triggers, nil
}
+10 -9
View File
@@ -16,13 +16,14 @@ limitations under the License.
package controller
type (
resource interface {
Key() string
}
serializer interface {
serialize(r resource) ([]byte, error)
deserialize(buf []byte, r resource) error
}
import (
"github.com/fission/fission/tpr"
)
func makeTPRBackedAPI() (*API, error) {
fissionClient, _, err := tpr.MakeFissionClient()
if err != nil {
return nil, err
}
return &API{fissionClient: fissionClient}, nil
}
+38 -39
View File
@@ -22,102 +22,101 @@ import (
"net/http"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/1.5/pkg/api"
"github.com/fission/fission"
"github.com/fission/fission/tpr"
)
func (api *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
watches, err := api.WatchStore.List()
func (a *API) WatchApiList(w http.ResponseWriter, r *http.Request) {
watches, err := a.fissionClient.Kuberneteswatchtriggers(api.NamespaceAll).List(api.ListOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(watches)
resp, err := json.Marshal(watches.Items)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
func (a *API) WatchApiCreate(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
var watch fission.Watch
var watch tpr.Kuberneteswatchtrigger
err = json.Unmarshal(body, &watch)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
watch.Target = fission.UrlForFunction(&watch.Function)
// TODO check for duplicate watches
uid, err := api.WatchStore.Create(&watch)
wnew, err := a.fissionClient.Kuberneteswatchtriggers(watch.Metadata.Namespace).Create(&watch)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
m := &fission.Metadata{Name: watch.Metadata.Name, Uid: uid}
resp, err := json.Marshal(m)
resp, err := json.Marshal(wnew.Metadata)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
w.WriteHeader(http.StatusCreated)
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
var m fission.Metadata
func (a *API) WatchApiGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
m.Name = vars["watch"]
m.Uid = r.FormValue("uid") // empty if uid is absent
name := vars["watch"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
watch, err := api.WatchStore.Get(&m)
watch, err := a.fissionClient.Kuberneteswatchtriggers(ns).Get(name)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
resp, err := json.Marshal(watch)
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, resp)
a.respondWithSuccess(w, resp)
}
func (api *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
api.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
func (a *API) WatchApiUpdate(w http.ResponseWriter, r *http.Request) {
a.respondWithError(w, fission.MakeError(fission.ErrorNotImplmented,
"Not implemented"))
}
func (api *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
func (a *API) WatchApiDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
var m fission.Metadata
m.Name = vars["watch"]
m.Uid = r.FormValue("uid") // empty if uid is absent
if len(m.Uid) == 0 {
log.WithFields(log.Fields{"watch": m.Name}).Info("Deleting all versions")
name := vars["watch"]
ns := vars["namespace"]
if len(ns) == 0 {
ns = api.NamespaceDefault
}
err := api.WatchStore.Delete(m)
err := a.fissionClient.Kuberneteswatchtriggers(ns).Delete(name, &api.DeleteOptions{})
if err != nil {
api.respondWithError(w, err)
a.respondWithError(w, err)
return
}
api.respondWithSuccess(w, []byte(""))
a.respondWithSuccess(w, []byte(""))
}
-79
View File
@@ -1,79 +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 controller
import (
"github.com/satori/go.uuid"
"github.com/fission/fission"
)
type WatchStore struct {
ResourceStore
}
func (ws *WatchStore) Create(w *fission.Watch) (string, error) {
w.Metadata.Uid = uuid.NewV4().String()
return w.Metadata.Uid, ws.ResourceStore.create(w)
}
func (ws *WatchStore) Get(m *fission.Metadata) (*fission.Watch, error) {
var w fission.Watch
err := ws.ResourceStore.read(m.Name, &w)
if err != nil {
return nil, err
}
return &w, err
}
func (ws *WatchStore) Update(w *fission.Watch) (string, error) {
w.Metadata.Uid = uuid.NewV4().String()
return w.Metadata.Uid, ws.ResourceStore.update(w)
}
func (ws *WatchStore) Delete(m fission.Metadata) error {
typeName, err := getTypeName(fission.Watch{})
if err != nil {
return err
}
return ws.ResourceStore.delete(typeName, m.Name)
}
func (ws *WatchStore) List() ([]fission.Watch, error) {
typeName, err := getTypeName(fission.Watch{})
if err != nil {
return nil, err
}
bufs, err := ws.ResourceStore.getAll(typeName)
if err != nil {
return nil, err
}
watches := make([]fission.Watch, 0, len(bufs))
js := JsonSerializer{}
for _, buf := range bufs {
var w fission.Watch
err = js.deserialize([]byte(buf), &w)
if err != nil {
return nil, err
}
watches = append(watches, w)
}
return watches, nil
}