diff --git a/controller/api.go b/controller/api.go index 5bc9e6d0..9941ba3d 100644 --- a/controller/api.go +++ b/controller/api.go @@ -90,11 +90,11 @@ func (api *API) serve(port int) { r.HandleFunc("/triggers/http/{httpTrigger}", api.HTTPTriggerApiUpdate).Methods("PUT") r.HandleFunc("/triggers/http/{httpTrigger}", api.HTTPTriggerApiDelete).Methods("DELETE") - // r.HandleFunc("/environments", api.EnvironmentApiList).Methods("GET") - // r.HandleFunc("/environments", api.EnvironmentApiCreate).Methods("POST") - // r.HandleFunc("/environments/{environment}", api.EnvironmentApiGet).Methods("GET") - // r.HandleFunc("/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT") - // r.HandleFunc("/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE") + r.HandleFunc("/environments", api.EnvironmentApiList).Methods("GET") + r.HandleFunc("/environments", api.EnvironmentApiCreate).Methods("POST") + r.HandleFunc("/environments/{environment}", api.EnvironmentApiGet).Methods("GET") + r.HandleFunc("/environments/{environment}", api.EnvironmentApiUpdate).Methods("PUT") + r.HandleFunc("/environments/{environment}", api.EnvironmentApiDelete).Methods("DELETE") address := fmt.Sprintf(":%v", port) diff --git a/controller/api_test.go b/controller/api_test.go index 585f0466..d7302804 100644 --- a/controller/api_test.go +++ b/controller/api_test.go @@ -152,6 +152,51 @@ func TestHTTPTriggerApi(t *testing.T) { assert(len(ts) == 2, "created two triggers, but didn't find them") } +func TestEnvironmentApi(t *testing.T) { + testEnv := &fission.Environment{ + Metadata: fission.Metadata{ + Name: "xxx", + Uid: "yyy", + }, + RunContainerImageUrl: "gcr.io/xyz", + } + uid, err := g.client.EnvironmentCreate(testEnv) + panicIf(err) + m := &fission.Metadata{ + Name: testEnv.Metadata.Name, + Uid: uid, + } + defer g.client.EnvironmentDelete(m) + + tr, err := g.client.EnvironmentGet(m) + panicIf(err) + testEnv.Metadata.Uid = m.Uid + assert(*testEnv == *tr, "env should match after reading") + + testEnv.RunContainerImageUrl = "/hi" + uid2, err := g.client.EnvironmentUpdate(testEnv) + panicIf(err) + + m.Uid = uid2 + tr, err = g.client.EnvironmentGet(m) + panicIf(err) + testEnv.Metadata.Uid = m.Uid + assert(*testEnv == *tr, "env should match after reading") + + testEnv.Metadata.Name = "yyy" + uid, err = g.client.EnvironmentCreate(testEnv) + panicIf(err) + m = &fission.Metadata{ + Name: testEnv.Metadata.Name, + Uid: uid, + } + defer g.client.EnvironmentDelete(m) + + ts, err := g.client.EnvironmentList() + panicIf(err) + assert(len(ts) == 2, "created two envs, but didn't find them") +} + func TestMain(m *testing.M) { flag.Parse() @@ -167,6 +212,7 @@ func TestMain(m *testing.M) { ks.Delete(context.Background(), "Function", &etcdClient.DeleteOptions{Recursive: true}) ks.Delete(context.Background(), "HTTPTrigger", &etcdClient.DeleteOptions{Recursive: true}) + ks.Delete(context.Background(), "Environment", &etcdClient.DeleteOptions{Recursive: true}) go api.serve(8888) time.Sleep(500 * time.Millisecond) diff --git a/controller/client/client.go b/controller/client/client.go index 0ec594ca..413f305c 100644 --- a/controller/client/client.go +++ b/controller/client/client.go @@ -318,13 +318,113 @@ func (c *Client) HTTPTriggerList() ([]fission.HTTPTrigger, error) { return triggers, nil } -// func (c *Client) EnvironmentCreate(f *fission.Environment) (string, error) { -// } -// func (c *Client) EnvironmentGet(m *fission.Metadata) (*fission.Environment, error) { -// } -// func (c *Client) EnvironmentUpdate(f *fission.Environment) (string, error) { -// } -// func (c *Client) EnvironmentDelete(m *fission.Metadata) error { -// } -// func (c *Client) EnvironmentList() ([]fission.Environment, error) { -// } +func (c *Client) EnvironmentCreate(env *fission.Environment) (string, error) { + reqbody, err := json.Marshal(env) + if err != nil { + return "", err + } + + resp, err := http.Post(c.url("environments"), "application/json", bytes.NewReader(reqbody)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := c.handleResponse(resp) + if err != nil { + log.WithFields(log.Fields{ + "name": env.Metadata.Name, + "err": err, + }).Error("Failed to create environment") + return "", err + } + + var m fission.Metadata + err = json.Unmarshal(body, &m) + if err != nil { + return "", err + } + + return m.Uid, 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) (string, error) { + reqbody, err := json.Marshal(env) + if err != nil { + return "", err + } + relativeUrl := fmt.Sprintf("environments/%v", env.Metadata.Name) + + resp, err := c.put(relativeUrl, "application/json", reqbody) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := c.handleResponse(resp) + if err != nil { + return "", err + } + + var m fission.Metadata + err = json.Unmarshal(body, &m) + if err != nil { + return "", err + } + return m.Uid, 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 + } + + 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 +} diff --git a/controller/environmentApi.go b/controller/environmentApi.go new file mode 100644 index 00000000..57ca0287 --- /dev/null +++ b/controller/environmentApi.go @@ -0,0 +1,151 @@ +/* +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 ( + "encoding/json" + "io/ioutil" + "net/http" + + log "github.com/Sirupsen/logrus" + "github.com/gorilla/mux" + + "github.com/platform9/fission" +) + +func (api *API) EnvironmentApiList(w http.ResponseWriter, r *http.Request) { + envs, err := api.EnvironmentStore.List() + if err != nil { + api.respondWithError(w, err) + return + } + + resp, err := json.Marshal(envs) + if err != nil { + api.respondWithError(w, err) + return + } + + api.respondWithSuccess(w, resp) +} + +func (api *API) EnvironmentApiCreate(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + api.respondWithError(w, err) + } + + var env fission.Environment + err = json.Unmarshal(body, &env) + if err != nil { + api.respondWithError(w, err) + return + } + + uid, err := api.EnvironmentStore.Create(&env) + if err != nil { + api.respondWithError(w, err) + return + } + + m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid} + resp, err := json.Marshal(m) + if err != nil { + api.respondWithError(w, err) + return + } + + api.respondWithSuccess(w, resp) +} + +func (api *API) EnvironmentApiGet(w http.ResponseWriter, r *http.Request) { + var m fission.Metadata + + vars := mux.Vars(r) + m.Name = vars["environment"] + m.Uid = r.FormValue("uid") // empty if uid is absent + + env, err := api.EnvironmentStore.Get(&m) + if err != nil { + api.respondWithError(w, err) + return + } + + resp, err := json.Marshal(env) + if err != nil { + api.respondWithError(w, err) + return + } + + api.respondWithSuccess(w, resp) +} + +func (api *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) + } + + var env fission.Environment + err = json.Unmarshal(body, &env) + if err != nil { + api.respondWithError(w, err) + return + } + + if name != env.Metadata.Name { + err = fission.MakeError(fission.ErrorInvalidArgument, "Environment name doesn't match URL") + api.respondWithError(w, err) + return + } + + uid, err := api.EnvironmentStore.Update(&env) + if err != nil { + api.respondWithError(w, err) + return + } + + m := &fission.Metadata{Name: env.Metadata.Name, Uid: uid} + resp, err := json.Marshal(m) + if err != nil { + api.respondWithError(w, err) + return + } + api.respondWithSuccess(w, resp) +} + +func (api *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") + } + + err := api.EnvironmentStore.Delete(m) + if err != nil { + api.respondWithError(w, err) + return + } + + api.respondWithSuccess(w, []byte("")) +} diff --git a/controller/environmentStore.go b/controller/environmentStore.go index 49bd8efa..07e442e0 100644 --- a/controller/environmentStore.go +++ b/controller/environmentStore.go @@ -26,12 +26,12 @@ type EnvironmentStore struct { resourceStore } -func (es *EnvironmentStore) create(e *fission.Environment) error { +func (es *EnvironmentStore) Create(e *fission.Environment) (string, error) { e.Metadata.Uid = uuid.NewV4().String() - return es.resourceStore.create(e) + return e.Metadata.Uid, es.resourceStore.create(e) } -func (es *EnvironmentStore) read(m fission.Metadata) (*fission.Environment, error) { +func (es *EnvironmentStore) Get(m *fission.Metadata) (*fission.Environment, error) { var e fission.Environment err := es.resourceStore.read(m.Name, &e) if err != nil { @@ -40,12 +40,12 @@ func (es *EnvironmentStore) read(m fission.Metadata) (*fission.Environment, erro return &e, nil } -func (es *EnvironmentStore) update(e *fission.Environment) error { +func (es *EnvironmentStore) Update(e *fission.Environment) (string, error) { e.Metadata.Uid = uuid.NewV4().String() - return es.resourceStore.update(e) + return e.Metadata.Uid, es.resourceStore.update(e) } -func (es *EnvironmentStore) delete(m fission.Metadata) error { +func (es *EnvironmentStore) Delete(m fission.Metadata) error { typeName, err := getTypeName(fission.Environment{}) if err != nil { return err @@ -53,7 +53,7 @@ func (es *EnvironmentStore) delete(m fission.Metadata) error { return es.resourceStore.delete(typeName, m.Name) } -func (es *EnvironmentStore) list() ([]fission.Environment, error) { +func (es *EnvironmentStore) List() ([]fission.Environment, error) { typeName, err := getTypeName(fission.Environment{}) if err != nil { return nil, err @@ -64,7 +64,7 @@ func (es *EnvironmentStore) list() ([]fission.Environment, error) { return nil, err } - triggers := make([]fission.Environment, 0, len(bufs)) + envs := make([]fission.Environment, 0, len(bufs)) js := JsonSerializer{} for _, buf := range bufs { var e fission.Environment @@ -72,8 +72,8 @@ func (es *EnvironmentStore) list() ([]fission.Environment, error) { if err != nil { return nil, err } - triggers = append(triggers, e) + envs = append(envs, e) } - return triggers, nil + return envs, nil } diff --git a/controller/httpTriggerApi.go b/controller/httpTriggerApi.go index be3a6f26..51173fd6 100644 --- a/controller/httpTriggerApi.go +++ b/controller/httpTriggerApi.go @@ -62,7 +62,7 @@ func (api *API) HTTPTriggerApiCreate(w http.ResponseWriter, r *http.Request) { return } - m := &fission.Metadata{Name: t.Name, Uid: uid} + m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid} resp, err := json.Marshal(m) if err != nil { api.respondWithError(w, err) @@ -122,7 +122,7 @@ func (api *API) HTTPTriggerApiUpdate(w http.ResponseWriter, r *http.Request) { return } - m := &fission.Metadata{Name: t.Name, Uid: uid} + m := &fission.Metadata{Name: t.Metadata.Name, Uid: uid} resp, err := json.Marshal(m) if err != nil { api.respondWithError(w, err)